Codebase list python-cx-oracle / f9004d4
Import upstream version 8.1 Kali Janitor 3 years ago
254 changed file(s) with 33459 addition(s) and 22598 deletion(s). Raw diff Collapse all Expand all
0 ---
1 name: Announcements
2 about: Use this if you are sharing something interesting
3 title: ''
4 labels: announcement
5 assignees: ''
6
7 ---
8
9 <!-- 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. -->
0 ---
1 name: Bug report
2 about: Create a report to help us improve
3 title: ''
4 labels: bug
5 assignees: ''
6
7 ---
8
9 <!--
10
11 Thank you for using cx_Oracle.
12
13 See https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html for how to report security issues
14
15 Please answer these questions so we can help you.
16
17 Use Markdown syntax, see https://help.github.com/github/writing-on-github/basic-writing-and-formatting-syntax
18
19 -->
20
21 1. What versions are you using?
22
23 <!--
24
25 Give your database version.
26
27
28 Also run Python and show the output of:
29
30 import sys
31 import platform
32
33 print("platform.platform:", platform.platform())
34 print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
35 print("platform.python_version:", platform.python_version())
36
37 And:
38
39 import cx_Oracle
40 print("cx_Oracle.version:", cx_Oracle.version)
41 print("cx_Oracle.clientversion:", cx_Oracle.clientversion())
42
43 -->
44
45 2. Is it an error or a hang or a crash?
46
47 3. What error(s) or behavior you are seeing?
48
49 <!--
50
51 Cut and paste text showing the command you ran. No screenshots.
52
53 Use a gist for long screen output and logs: see https://gist.github.com/
54
55 -->
56
57 4. Include a runnable Python script that shows the problem.
58
59 <!--
60
61 Include all SQL needed to create the database schema.
62
63 Format code by using three backticks on a line before and after code snippets, for example:
64
65 ```
66 import cx_Oracle
67 ```
68
69 -->
0 ---
1 name: Documentation and Example Improvements
2 about: Use this to suggest changes to documentation and examples
3 title: ''
4 labels: enhancement
5 assignees: ''
6
7 ---
8
9 <!--
10
11 Thank you for using cx_Oracle.
12
13 Please answer these questions so we can help you.
14
15 Use Markdown syntax, see https://help.github.com/github/writing-on-github/basic-writing-and-formatting-syntax
16
17 -->
18
19 1. What is the link to the documentation section that needs improving?
20
21 2. Describe the confusion
22
23 3. Suggest changes that would help
0 ---
1 name: Enhancement Requests
2 about: Use this for enhancement requests
3 title: ''
4 labels: enhancement
5 assignees: ''
6
7 ---
8
9 <!--
10
11 Thank you for using cx_Oracle.
12
13 Review existing enhancement requests: https://github.com/oracle/python-cx_Oracle/labels/enhancement
14
15 Please answer these questions so we can help you.
16
17 Use Markdown syntax, see https://help.github.com/github/writing-on-github/basic-writing-and-formatting-syntax
18
19 -->
20
21 1. Describe your new request in detail
22
23 2. Give supporting information about tools and operating systems. Give relevant product version numbers
0 ---
1 name: General Questions and Runtime Problems
2 about: For general cx_Oracle questions
3 title: ''
4 labels: question
5 assignees: ''
6
7 ---
8
9 <!--
10
11 Thank you for using cx_Oracle.
12
13 Review the user manual: https://cx-oracle.readthedocs.io/en/latest/index.html
14
15 Please answer these questions so we can help you.
16
17 Use Markdown syntax, see https://help.github.com/github/writing-on-github/basic-writing-and-formatting-syntax
18
19 GitHub issues that are not updated for a month may be automatically closed. Feel free to update them at any time.
20
21 -->
22
23 1. What versions are you using?
24
25 <!--
26
27 Give your database version.
28
29 Also run Python and show the output of:
30
31 import sys
32 import platform
33
34 print("platform.platform:", platform.platform())
35 print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
36 print("platform.python_version:", platform.python_version())
37
38 And:
39
40 import cx_Oracle
41 print("cx_Oracle.version:", cx_Oracle.version)
42 print("cx_Oracle.clientversion:", cx_Oracle.clientversion())
43
44 -->
45
46 2. Describe the problem
47
48 <!-- Cut and paste text showing the command you ran. No screenshots. -->
49
50 3. Include a runnable Python script that shows the problem.
51
52 <!--
53
54 Include all SQL needed to create the database schema.
55
56 Use a gist for long code: see https://gist.github.com/
57
58 Format code by using three backticks on a line before and after code snippets, for example:
59
60 ```
61 import cx_Oracle
62 ```
63
64 -->
0 ---
1 name: Installation Questions
2 about: Use this for cx_Oracle installation questions
3 title: ''
4 labels: install & configuration
5 assignees: ''
6
7 ---
8
9 <!--
10
11 Thank you for using cx_Oracle.
12
13 Do these before creating a new issue:
14
15 Review and follow the Installation Instructions: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html
16
17 Review the troubleshooting tips: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html#troubleshooting
18
19 Review the user manual: https://cx-oracle.readthedocs.io/en/latest/index.html
20
21 If you have a `DPI-1047`, `DPI-1050` or `DPI-1072` error, re-review the links above.
22
23 Google any errors.
24
25 Then please answer these questions so we can help you.
26
27 GitHub issues that are not updated for a month may be automatically closed. Feel free to update them at any time.
28
29 -->
30
31 1. What versions are you using?
32
33 <!--
34
35 Give your database version.
36
37 Also run Python and show the output of:
38
39 import sys
40 import platform
41
42 print("platform.platform:", platform.platform())
43 print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
44 print("platform.python_version:", platform.python_version())
45
46 -->
47
48 2. Describe the problem
49
50 <!-- Cut and paste text showing the command you ran. No screenshots. -->
51
52 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?
53
54 4. Show what the `PATH` environment variable (on Windows) or `LD_LIBRARY_PATH` (on Linux) is set to?
55
56 5. Show any Oracle environment variables set (e.g. ORACLE_HOME, ORACLE_BASE).
0 # Reporting Security Vulnerabilities
1
2 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.
3
4 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 [email protected] preferably with a proof of concept. We provide additional information on [how to report security vulnerabilities to Oracle](https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html) which includes public encryption keys for secure email.
5
6 We ask that you do not use other channels or contact project contributors directly.
7
8 Non-vulnerability related security issues such as great new ideas for security features are welcome on GitHub Issues.
9
10 ## Security-Related Information
11
12 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.
+0
-45
.github/issue_template.md less more
0 *Delete unnecessary parts of this template.*
1
2 ### For security issues:
3
4 See https://www.oracle.com/support/assurance/vulnerability-remediation/reporting-security-vulnerabilities.html for how to report security issues.
5
6 ### For general questions:
7
8 Describe exactly what you did and what you want to happen.
9 Use the questions at the bottom of this template as a guide.
10
11 Use Markdown syntax, particularly for code blocks: see https://help.github.com/articles/basic-writing-and-formatting-syntax/#quoting-code
12
13 ### For installation issues:
14
15 Use a gist for screen output and logs: see https://gist.github.com/
16 **Do not paste long output into this issue**
17
18 Review your output and logs. **Google any errors**
19 Try to install in a different way. Try some potential solutions.
20
21 Review the install instructions at
22 http://cx-oracle.readthedocs.io/en/latest/installation.html
23
24 #### Answer the following questions:
25
26 1. What is your version of Python? Is it 32-bit or 64-bit?
27
28 2. What is your cx_Oracle version?
29
30 3. What *exact* command caused the problem (e.g. what command did you try to install with)? Who were you logged in as?
31
32 4. What error(s) you are seeing?
33
34 5. What OS (and version) is Python executing on?
35
36 6. What is your version of the Oracle client (e.g. Instant Client)? How was it installed? Where is it installed?
37
38 7. What is your Oracle Database version?
39
40 8. What is the `PATH` environment variable (on Windows) or `LD_LIBRARY_PATH` (on Linux) set to? On macOS, what is in `~/lib`?
41
42 9. What Oracle environment variables did you set? How *exactly* did you set them?
43
44 10. Do you have a small, single Python script that immediately runs to show us the problem?
0 # https://probot.github.io/apps/stale/
1
2 # Number of days of inactivity before an issue becomes stale
3 daysUntilStale: 30
4 # Number of days of inactivity before a stale issue is closed
5 daysUntilClose: 7
6 # Issues with these labels will never be considered stale
7 exemptLabels:
8 - pinned
9 - enhancement
10 - bug
11 - announcement
12 - OCA accepted
13 # Label to use when marking an issue as stale
14 staleLabel: inactive
15 # Comment to post when marking an issue as stale. Set to `false` to disable
16 markComment: >
17 This issue has been automatically marked as inactive because it has not been
18 updated recently. It will be closed if no further activity occurs. Thank you
19 for your contributions.
20 # Comment to post when closing a stale issue. Set to `false` to disable
21 closeComment: >
22 This issue has been automatically closed because it has not been updated for a month.
00 *.pyc
1 .tox/
12 build/
23 dist/
34 doc/build
00 [submodule "odpi"]
11 path = odpi
22 url = ../odpi.git
3 branch = v3.1.x
0 # Contributing to cx_Oracle
0 # Contributing
11
2 *Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.*
2 We welcome your contributions! There are multiple ways to contribute.
33
4 Pull requests can be made under
5 [The Oracle Contributor Agreement](https://www.oracle.com/technetwork/community/oca-486395.html)
6 (OCA).
4 ## Issues
75
8 For pull requests to be accepted into cx_Oracle, the bottom of
9 your commit message must have the following line using your name and
10 e-mail address as it appears in the OCA Signatories list.
6 For bugs or enhancement requests, please file a GitHub issue unless it's security related. When filing a bug remember that the better written the bug is, the more likely it is to be fixed. If you think you've found a security vulnerability, do not raise a GitHub issue and follow the instructions on our [Security Policy](./.github/SECURITY.md).
117
12 ```
8 ## Contributing Code
9
10 We welcome your code contributions. To get started, you will need to sign the [Oracle Contributor Agreement](https://www.oracle.com/technetwork/community/oca-486395.html) (OCA).
11
12 For pull requests to be accepted, the bottom of your commit message must have
13 the following line using the name and e-mail address you used for the OCA.
14
15 ```text
1316 Signed-off-by: Your Name <[email protected]>
1417 ```
1518
1619 This can be automatically added to pull requests by committing with:
1720
21 ```text
22 git commit --signoff
1823 ```
19 git commit --signoff
20 ````
2124
2225 Only pull requests from committers that can be verified as having
2326 signed the OCA can be accepted.
27
28 ### Pull request process
29
30 1. Fork this repository
31 1. Create a branch in your fork to implement the changes. We recommend using
32 the issue number as part of your branch name, e.g. `1234-fixes`
33 1. Ensure that any documentation is updated with the changes that are required
34 by your fix.
35 1. Ensure that any samples are updated if the base image has been changed.
36 1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly
37 what your changes are meant to do and provide simple steps on how to validate
38 your changes. Ensure that you reference the issue you created as well.
39 1. We will review your PR before it is merged.
40
41 ## Code of Conduct
42
43 Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd like more specific guidelines see the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct/)
0 # cx_Oracle version 7.1
0 # cx_Oracle version 8.1
11
22 cx_Oracle is a Python extension module that enables access to Oracle
33 Database. It conforms to the [Python database API 2.0
66 [homepage](https://oracle.github.io/python-cx_Oracle/index.html) for a
77 feature list.
88
9 cx_Oracle 7 has been tested with Python version 2.7, and with versions
10 3.5 and higher. You can use cx_Oracle with Oracle 11.2 and higher client
11 libraries (including Oracle 18.3). Oracle's standard client-server version
12 interoperability allows connection to both older and newer databases,
13 for example Oracle 18.3 client libraries can connect to Oracle
14 Database 11.2.
9 cx_Oracle 8 has been tested with Python versions 3.6 through 3.9. You can use
10 cx_Oracle with Oracle 11.2, 12c, 18c, 19c and 21c client libraries. Oracle's
11 standard client-server version interoperability allows connection to both older
12 and newer databases. For example Oracle 19c client libraries can connect to
13 Oracle Database 11.2. Older versions of cx_Oracle may work with older
14 versions of Python.
1515
1616 ## Installation
1717
5454 [9]: https://github.com/oracle/python-cx_Oracle/issues
5555 [11]: https://github.com/oracle/python-cx_Oracle/tree/master/test
5656 [12]: https://github.com/oracle/python-cx_Oracle/tree/master/samples
57 [14]: http://cx-oracle.readthedocs.io/en/latest/releasenotes.html
58 [15]: http://cx-oracle.readthedocs.io/en/latest/installation.html
57 [14]: https://cx-oracle.readthedocs.io/en/latest/release_notes.html
58 [15]: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html
0 .. _aq:
1
2 *********************
3 Advanced Queuing (AQ)
4 *********************
5
6 See :ref:`aqusermanual` for more information about using AQ in cx_Oracle.
7
8 .. note::
9
10 All of these objects are extensions to the DB API.
11
12 .. _queue:
13
14 ------
15 Queues
16 ------
17
18 These objects are created using the :meth:`Connection.queue()` method and are
19 used to enqueue and dequeue messages.
20
21 .. attribute:: Queue.connection
22
23 This read-only attribute returns a reference to the connection object on
24 which the queue was created.
25
26
27 .. method:: Queue.deqMany(maxMessages)
28
29 Dequeues up to the specified number of messages from the queue and returns
30 a list of these messages. Each element of the returned list is a
31 :ref:`message property<msgproperties>` object.
32
33
34 .. method:: Queue.deqOne()
35
36 Dequeues at most one message from the queue. If a message is dequeued, it
37 will be a :ref:`message property<msgproperties>` object; otherwise, it will
38 be the value None.
39
40 .. attribute:: Queue.deqOptions
41
42 This read-only attribute returns a reference to the :ref:`options
43 <deqoptions>` that will be used when dequeuing messages from the queue.
44
45
46 .. method:: Queue.enqOne(message)
47
48 Enqueues a single message into the queue. The message must be a
49 :ref:`message property<msgproperties>` object which has had its payload
50 attribute set to a value that the queue supports.
51
52
53 .. method:: Queue.enqMany(messages)
54
55 Enqueues multiple messages into the queue. The messages parameter must be a
56 sequence containing :ref:`message property <msgproperties>` objects which
57 have all had their payload attribute set to a value that the queue
58 supports.
59
60 Warning: calling this function in parallel on different connections
61 acquired from the same pool may fail due to Oracle bug 29928074. Ensure
62 that this function is not run in parallel, use standalone connections or
63 connections from different pools, or make multiple calls to
64 :meth:`Queue.enqOne()` instead. The function :meth:`Queue.deqMany()`
65 call is not affected.
66
67
68 .. attribute:: Queue.enqOptions
69
70 This read-only attribute returns a reference to the :ref:`options
71 <enqoptions>` that will be used when enqueuing messages into the queue.
72
73
74 .. attribute:: Queue.name
75
76 This read-only attribute returns the name of the queue.
77
78
79 .. attribute:: Queue.payloadType
80
81 This read-only attribute returns the object type for payloads that can be
82 enqueued and dequeued. If using a raw queue, this returns the value None.
83
84
85 .. _deqoptions:
86
87 ---------------
88 Dequeue Options
89 ---------------
90
91 .. note::
92
93 These objects are used to configure how messages are dequeued from queues.
94 An instance of this object is found in the attribute
95 :attr:`Queue.deqOptions`.
96
97
98 .. attribute:: DeqOptions.condition
99
100 This attribute specifies a boolean expression similar to the where clause
101 of a SQL query. The boolean expression can include conditions on message
102 properties, user data properties and PL/SQL or SQL functions. The default
103 is to have no condition specified.
104
105
106 .. attribute:: DeqOptions.consumername
107
108 This attribute specifies the name of the consumer. Only messages matching
109 the consumer name will be accessed. If the queue is not set up for multiple
110 consumers this attribute should not be set. The default is to have no
111 consumer name specified.
112
113
114 .. attribute:: DeqOptions.correlation
115
116 This attribute specifies the correlation identifier of the message to be
117 dequeued. Special pattern-matching characters, such as the percent sign (%)
118 and the underscore (_), can be used. If multiple messages satisfy the
119 pattern, the order of dequeuing is indeterminate. The default is to have no
120 correlation specified.
121
122
123 .. attribute:: DeqOptions.deliverymode
124
125 This write-only attribute specifies what types of messages should be
126 dequeued. It should be one of the values :data:`~cx_Oracle.MSG_PERSISTENT`
127 (default), :data:`~cx_Oracle.MSG_BUFFERED` or
128 :data:`~cx_Oracle.MSG_PERSISTENT_OR_BUFFERED`.
129
130
131 .. attribute:: DeqOptions.mode
132
133 This attribute specifies the locking behaviour associated with the dequeue
134 operation. It should be one of the values :data:`~cx_Oracle.DEQ_BROWSE`,
135 :data:`~cx_Oracle.DEQ_LOCKED`,
136 :data:`~cx_Oracle.DEQ_REMOVE` (default), or
137 :data:`~cx_Oracle.DEQ_REMOVE_NODATA`.
138
139
140 .. attribute:: DeqOptions.msgid
141
142 This attribute specifies the identifier of the message to be dequeued. The
143 default is to have no message identifier specified.
144
145
146 .. attribute:: DeqOptions.navigation
147
148 This attribute specifies the position of the message that is retrieved. It
149 should be one of the values :data:`~cx_Oracle.DEQ_FIRST_MSG`,
150 :data:`~cx_Oracle.DEQ_NEXT_MSG` (default), or
151 :data:`~cx_Oracle.DEQ_NEXT_TRANSACTION`.
152
153
154 .. attribute:: DeqOptions.transformation
155
156 This attribute specifies the name of the transformation that must be
157 applied after the message is dequeued from the database but before it is
158 returned to the calling application. The transformation must be created
159 using dbms_transform. The default is to have no transformation specified.
160
161
162 .. attribute:: DeqOptions.visibility
163
164 This attribute specifies the transactional behavior of the dequeue request.
165 It should be one of the values :data:`~cx_Oracle.DEQ_ON_COMMIT` (default)
166 or :data:`~cx_Oracle.DEQ_IMMEDIATE`. This attribute is ignored when using
167 the :data:`~cx_Oracle.DEQ_BROWSE` mode. Note the value of
168 :attr:`~Connection.autocommit` is always ignored.
169
170
171 .. attribute:: DeqOptions.wait
172
173 This attribute specifies the time to wait, in seconds, for a message
174 matching the search criteria to become available for dequeuing. One of the
175 values :data:`~cx_Oracle.DEQ_NO_WAIT` or
176 :data:`~cx_Oracle.DEQ_WAIT_FOREVER` can also be used. The default is
177 :data:`~cx_Oracle.DEQ_WAIT_FOREVER`.
178
179
180 .. _enqoptions:
181
182 ---------------
183 Enqueue Options
184 ---------------
185
186 .. note::
187
188 These objects are used to configure how messages are enqueued into queues.
189 An instance of this object is found in the attribute
190 :attr:`Queue.enqOptions`.
191
192
193 .. attribute:: EnqOptions.deliverymode
194
195 This write-only attribute specifies what type of messages should be
196 enqueued. It should be one of the values :data:`~cx_Oracle.MSG_PERSISTENT`
197 (default) or :data:`~cx_Oracle.MSG_BUFFERED`.
198
199
200 .. attribute:: EnqOptions.transformation
201
202 This attribute specifies the name of the transformation that must be
203 applied before the message is enqueued into the database. The
204 transformation must be created using dbms_transform. The default is to have
205 no transformation specified.
206
207
208 .. attribute:: EnqOptions.visibility
209
210 This attribute specifies the transactional behavior of the enqueue request.
211 It should be one of the values :data:`~cx_Oracle.ENQ_ON_COMMIT` (default)
212 or :data:`~cx_Oracle.ENQ_IMMEDIATE`. Note the value of
213 :attr:`~Connection.autocommit` is ignored.
214
215
216 .. _msgproperties:
217
218 ------------------
219 Message Properties
220 ------------------
221
222 .. note::
223
224 These objects are used to identify the properties of messages that are
225 enqueued and dequeued in queues. They are created by the method
226 :meth:`Connection.msgproperties()`. They are used by the methods
227 :meth:`Queue.enqOne()` and :meth:`Queue.enqMany()` and
228 returned by the methods :meth:`Queue.deqOne()` and :meth:`Queue.deqMany()`.
229
230
231 .. attribute:: MessageProperties.attempts
232
233 This read-only attribute specifies the number of attempts that have been
234 made to dequeue the message.
235
236
237 .. attribute:: MessageProperties.correlation
238
239 This attribute specifies the correlation used when the message was
240 enqueued.
241
242
243 .. attribute:: MessageProperties.delay
244
245 This attribute specifies the number of seconds to delay an enqueued
246 message. Any integer is acceptable but the constant
247 :data:`~cx_Oracle.MSG_NO_DELAY` can also be used indicating that the
248 message is available for immediate dequeuing.
249
250
251 .. attribute:: MessageProperties.deliverymode
252
253 This read-only attribute specifies the type of message that was dequeued.
254 It will be one of the values :data:`~cx_Oracle.MSG_PERSISTENT` or
255 :data:`~cx_Oracle.MSG_BUFFERED`.
256
257
258 .. attribute:: MessageProperties.enqtime
259
260 This read-only attribute specifies the time that the message was enqueued.
261
262
263 .. attribute:: MessageProperties.exceptionq
264
265 This attribute specifies the name of the queue to which the message is
266 moved if it cannot be processed successfully. Messages are moved if the
267 number of unsuccessful dequeue attempts has exceeded the maximum number of
268 retries or if the message has expired. All messages in the exception queue
269 are in the :data:`~cx_Oracle.MSG_EXPIRED` state. The default value is the
270 name of the exception queue associated with the queue table.
271
272
273 .. attribute:: MessageProperties.expiration
274
275 This attribute specifies, in seconds, how long the message is available for
276 dequeuing. This attribute is an offset from the delay attribute. Expiration
277 processing requires the queue monitor to be running. Any integer is
278 accepted but the constant :data:`~cx_Oracle.MSG_NO_EXPIRATION` can also be
279 used indicating that the message never expires.
280
281
282 .. attribute:: MessageProperties.msgid
283
284 This attribute specifies the id of the message in the last queue that
285 generated this message.
286
287
288 .. attribute:: MessageProperties.payload
289
290 This attribute identifies the payload that will be enqueued or the payload
291 that was dequeued when using a :ref:`queue <queue>`. When enqueuing, the
292 value is checked to ensure that it conforms to the type expected by that
293 queue. For RAW queues, the value can be a bytes object or a string. If the
294 value is a string it will first be converted to bytes by encoding in the
295 encoding identified by the attribute :attr:`Connection.encoding`.
296
297
298 .. attribute:: MessageProperties.priority
299
300 This attribute specifies the priority of the message. A smaller number
301 indicates a higher priority. The priority can be any integer, including
302 negative numbers. The default value is zero.
303
304
305 .. attribute:: MessageProperties.state
306
307 This read-only attribute specifies the state of the message at the time of
308 the dequeue. It will be one of the values :data:`~cx_Oracle.MSG_WAITING`,
309 :data:`~cx_Oracle.MSG_READY`, :data:`~cx_Oracle.MSG_PROCESSED` or
310 :data:`~cx_Oracle.MSG_EXPIRED`.
0 .. _connobj:
1
2 *****************
3 Connection Object
4 *****************
5
6 .. note::
7
8 Any outstanding changes will be rolled back when the connection object
9 is destroyed or closed.
10
11
12
13 .. method:: Connection.__enter__()
14
15 The entry point for the connection as a context manager. It returns itself.
16
17 .. note::
18
19 This method is an extension to the DB API definition.
20
21
22 .. method:: Connection.__exit__()
23
24 The exit point for the connection as a context manager. This will close
25 the connection and roll back any uncommitted transaction.
26
27 .. note::
28
29 This method is an extension to the DB API definition.
30
31
32 .. attribute:: Connection.action
33
34 This write-only attribute sets the action column in the v$session table. It
35 is a string attribute and cannot be set to None -- use the empty string
36 instead.
37
38 .. note::
39
40 This attribute is an extension to the DB API definition.
41
42
43 .. attribute:: Connection.autocommit
44
45 This read-write attribute determines whether autocommit mode is on or off.
46 When autocommit mode is on, all statements are committed as soon as they
47 have completed executing.
48
49 .. note::
50
51 This attribute is an extension to the DB API definition.
52
53
54 .. method:: Connection.begin([formatId, transactionId, branchId])
55
56 Explicitly begin a new transaction. Without parameters, this explicitly
57 begins a local transaction; otherwise, this explicitly begins a distributed
58 (global) transaction with the given parameters. See the Oracle
59 documentation for more details.
60
61 Note that in order to make use of global (distributed) transactions, the
62 :attr:`~Connection.internal_name` and :attr:`~Connection.external_name`
63 attributes must be set.
64
65 .. note::
66
67 This method is an extension to the DB API definition.
68
69
70 .. attribute:: Connection.callTimeout
71
72 This read-write attribute specifies the amount of time (in milliseconds)
73 that a single round-trip to the database may take before a timeout will
74 occur. A value of 0 means that no timeout will take place.
75
76 .. versionadded:: 7.0
77
78 .. note::
79
80 This attribute is an extension to the DB API definition and is only
81 available in Oracle Client 18c and higher.
82
83
84 .. method:: Connection.cancel()
85
86 Break a long-running transaction.
87
88 .. note::
89
90 This method is an extension to the DB API definition.
91
92
93 .. method:: Connection.changepassword(oldpassword, newpassword)
94
95 Change the password of the logon.
96
97 .. note::
98
99 This method is an extension to the DB API definition.
100
101
102 .. attribute:: Connection.client_identifier
103
104 This write-only attribute sets the client_identifier column in the
105 v$session table.
106
107 .. note::
108
109 This attribute is an extension to the DB API definition.
110
111
112 .. attribute:: Connection.clientinfo
113
114 This write-only attribute sets the client_info column in the v$session
115 table.
116
117 .. note::
118
119 This attribute is an extension to the DB API definition.
120
121
122 .. method:: Connection.close()
123
124 Close the connection now, rather than whenever __del__ is called. The
125 connection will be unusable from this point forward; an Error exception
126 will be raised if any operation is attempted with the connection.
127
128 All open cursors and LOBs created by the connection will be closed and will
129 also no longer be usable.
130
131 Internally, references to the connection are held by cursor objects,
132 LOB objects, subscription objects, etc. Once all of these references are
133 released, the connection itself will be closed automatically. Either
134 control references to these related objects carefully or explicitly close
135 connections in order to ensure sufficient resources are available.
136
137
138 .. method:: Connection.commit()
139
140 Commit any pending transactions to the database.
141
142
143 .. method:: Connection.createlob(lobType)
144
145 Create and return a new temporary :ref:`LOB object <lobobj>` of the
146 specified type. The lobType parameter should be one of
147 :data:`cx_Oracle.CLOB`, :data:`cx_Oracle.BLOB` or :data:`cx_Oracle.NCLOB`.
148
149 .. versionadded:: 6.2
150
151 .. note::
152
153 This method is an extension to the DB API definition.
154
155
156 .. attribute:: Connection.current_schema
157
158 This read-write attribute sets the current schema attribute for the
159 session. Setting this value is the same as executing the SQL statement
160 "ALTER SESSION SET CURRENT_SCHEMA". The attribute is set (and verified) on
161 the next call that does a round trip to the server. The value is placed
162 before unqualified database objects in SQL statements you then execute.
163
164 .. note::
165
166 This attribute is an extension to the DB API definition.
167
168
169 .. method:: Connection.cursor()
170
171 Return a new :ref:`cursor object <cursorobj>` using the connection.
172
173
174 .. attribute:: Connection.dbop
175
176 This write-only attribute sets the database operation that is to be
177 monitored. This can be viewed in the DBOP_NAME column of the V$SQL_MONITOR
178 table.
179
180 .. note::
181
182 This attribute is an extension to the DB API definition.
183
184
185 .. method:: Connection.deq(name, options, msgproperties, payload)
186
187 Returns a message id after successfully dequeuing a message. The options
188 object can be created using :meth:`~Connection.deqoptions()` and the
189 msgproperties object can be created using
190 :meth:`~Connection.msgproperties()`. The payload must be an object created
191 using :meth:`ObjectType.newobject()`.
192
193 .. versionadded:: 5.3
194
195 .. deprecated:: 7.2
196
197 Use the methods :meth:`Queue.deqOne()` or :meth:`Queue.deqMany()`
198 instead.
199
200 .. note::
201
202 This method is an extension to the DB API definition.
203
204
205 .. method:: Connection.deqoptions()
206
207 Returns an object specifying the options to use when dequeuing messages.
208 See :ref:`deqoptions` for more information.
209
210 .. versionadded:: 5.3
211
212 .. deprecated:: 7.2
213
214 Use the attribute :attr:`Queue.deqOptions` instead.
215
216 .. note::
217
218 This method is an extension to the DB API definition.
219
220
221 .. attribute:: Connection.dsn
222
223 This read-only attribute returns the TNS entry of the database to which a
224 connection has been established.
225
226 .. note::
227
228 This attribute is an extension to the DB API definition.
229
230
231 .. attribute:: Connection.edition
232
233 This read-only attribute gets the session edition and is only available in
234 Oracle Database 11.2 (both client and server must be at this level or
235 higher for this to work).
236
237 .. versionadded:: 5.3
238
239 .. note::
240
241 This attribute is an extension to the DB API definition.
242
243
244 .. attribute:: Connection.encoding
245
246 This read-only attribute returns the IANA character set name of the
247 character set in use by the Oracle client for regular strings.
248
249 .. note::
250
251 This attribute is an extension to the DB API definition.
252
253
254 .. method:: Connection.enq(name, options, msgproperties, payload)
255
256 Returns a message id after successfully enqueuing a message. The options
257 object can be created using :meth:`~Connection.enqoptions()` and the
258 msgproperties object can be created using
259 :meth:`~Connection.msgproperties()`. The payload must be an object created
260 using :meth:`ObjectType.newobject()`.
261
262 .. versionadded:: 5.3
263
264 .. deprecated:: 7.2
265
266 Use the methods :meth:`Queue.enqOne()` or :meth:`Queue.enqMany()`
267 instead.
268
269 .. note::
270
271 This method is an extension to the DB API definition.
272
273
274 .. method:: Connection.enqoptions()
275
276 Returns an object specifying the options to use when enqueuing messages.
277 See :ref:`enqoptions` for more information.
278
279 .. versionadded:: 5.3
280
281 .. deprecated:: 7.2
282
283 Use the attribute :attr:`Queue.enqOptions` instead.
284
285 .. note::
286
287 This method is an extension to the DB API definition.
288
289
290 .. attribute:: Connection.external_name
291
292 This read-write attribute specifies the external name that is used by the
293 connection when logging distributed transactions.
294
295 .. versionadded:: 5.3
296
297 .. note::
298
299 This attribute is an extension to the DB API definition.
300
301
302 .. method:: Connection.getSodaDatabase()
303
304 Return a :ref:`SodaDatabase <sodadb>` object for Simple Oracle Document
305 Access (SODA). All SODA operations are performed either on the returned
306 SodaDatabase object or from objects created by the returned SodaDatabase
307 object. See `here <http://www.oracle.com/pls/topic/lookup?
308 ctx=dblatest&id=GUID-BE42F8D3-B86B-43B4-B2A3-5760A4DF79FB>`__ for
309 additional information on SODA.
310
311 .. versionadded:: 7.0
312
313 .. note::
314
315 This method is an extension to the DB API definition.
316
317
318 .. method:: Connection.gettype(name)
319
320 Return a :ref:`type object <objecttype>` given its name. This can then be
321 used to create objects which can be bound to cursors created by this
322 connection.
323
324 .. versionadded:: 5.3
325
326 .. note::
327
328 This method is an extension to the DB API definition.
329
330
331 .. attribute:: Connection.handle
332
333 This read-only attribute returns the OCI service context handle for the
334 connection. It is primarily provided to facilitate testing the creation of
335 a connection using the OCI service context handle.
336
337 .. note::
338
339 This attribute is an extension to the DB API definition.
340
341
342 .. attribute:: Connection.inputtypehandler
343
344 This read-write attribute specifies a method called for each value that is
345 bound to a statement executed on any cursor associated with this
346 connection. The method signature is handler(cursor, value, arraysize) and
347 the return value is expected to be a variable object or None in which case
348 a default variable object will be created. If this attribute is None, the
349 default behavior will take place for all values bound to statements.
350
351 .. note::
352
353 This attribute is an extension to the DB API definition.
354
355
356 .. attribute:: Connection.internal_name
357
358 This read-write attribute specifies the internal name that is used by the
359 connection when logging distributed transactions.
360
361 .. versionadded:: 5.3
362
363 .. note::
364
365 This attribute is an extension to the DB API definition.
366
367
368 .. attribute:: Connection.ltxid
369
370 This read-only attribute returns the logical transaction id for the
371 connection. It is used within Oracle Transaction Guard as a means of
372 ensuring that transactions are not duplicated. See the Oracle documentation
373 and the provided sample for more information.
374
375 .. versionadded:: 5.3
376
377 .. note:
378
379 This attribute is an extension to the DB API definition. It is only
380 available when Oracle Database 12.1 or higher is in use on both the
381 server and the client.
382
383
384 .. attribute:: Connection.maxBytesPerCharacter
385
386 This read-only attribute returns the maximum number of bytes each character
387 can use for the client character set.
388
389 .. note::
390
391 This attribute is an extension to the DB API definition.
392
393
394 .. attribute:: Connection.module
395
396 This write-only attribute sets the module column in the v$session table.
397 The maximum length for this string is 48 and if you exceed this length you
398 will get ORA-24960.
399
400 .. note:
401
402 This attribute is an extension to the DB API definition.
403
404
405 .. method:: Connection.msgproperties(payload, correlation, delay, exceptionq, \
406 expiration, priority)
407
408 Returns an object specifying the properties of messages used in advanced
409 queuing. See :ref:`msgproperties` for more information.
410
411 Each of the parameters are optional. If specified, they act as a shortcut
412 for setting each of the equivalently named properties.
413
414 .. versionadded:: 5.3
415
416 .. versionchanged:: 7.2 Added parameters
417
418 .. note::
419
420 This method is an extension to the DB API definition.
421
422
423 .. attribute:: Connection.nencoding
424
425 This read-only attribute returns the IANA character set name of the
426 national character set in use by the Oracle client.
427
428 .. note::
429
430 This attribute is an extension to the DB API definition.
431
432
433 .. attribute:: Connection.outputtypehandler
434
435 This read-write attribute specifies a method called for each column that is
436 going to be fetched from any cursor associated with this connection. The
437 method signature is handler(cursor, name, defaultType, length, precision,
438 scale) and the return value is expected to be a variable object or None in
439 which case a default variable object will be created. If this attribute is
440 None, the default behavior will take place for all columns fetched from
441 cursors.
442
443 See :ref:`outputtypehandlers`.
444
445 .. note::
446
447 This attribute is an extension to the DB API definition.
448
449
450 .. method:: Connection.ping()
451
452 Ping the server which can be used to test if the connection is still
453 active.
454
455 .. note::
456
457 This method is an extension to the DB API definition.
458
459
460 .. method:: Connection.prepare()
461
462 Prepare the distributed (global) transaction for commit. Return a boolean
463 indicating if a transaction was actually prepared in order to avoid the
464 error ORA-24756 (transaction does not exist).
465
466 .. note::
467
468 This method is an extension to the DB API definition.
469
470
471 .. method:: Connection.queue(name, payloadType=None)
472
473 Creates a :ref:`queue <queue>` which is used to enqueue and dequeue
474 messages in Advanced Queueing.
475
476 The name parameter is expected to be a string identifying the queue in
477 which messages are to be enqueued or dequeued.
478
479 The payloadType parameter, if specified, is expected to be an
480 :ref:`object type <objecttype>` that identifies the type of payload the
481 queue expects. If not specified, RAW data is enqueued and dequeued.
482
483 .. versionadded:: 7.2
484
485 .. note::
486
487 This method is an extension to the DB API definition.
488
489
490 .. method:: Connection.rollback()
491
492 Rollback any pending transactions.
493
494
495 .. method:: Connection.shutdown([mode])
496
497 Shutdown the database. In order to do this the connection must be connected
498 as :data:`~cx_Oracle.SYSDBA` or :data:`~cx_Oracle.SYSOPER`. Two calls must
499 be made unless the mode specified is :data:`~cx_Oracle.DBSHUTDOWN_ABORT`.
500 An example is shown below:
501
502 ::
503
504 import cx_Oracle
505
506 connection = cx_Oracle.connect(mode = cx_Oracle.SYSDBA)
507 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_IMMEDIATE)
508 cursor = connection.cursor()
509 cursor.execute("alter database close normal")
510 cursor.execute("alter database dismount")
511 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_FINAL)
512
513 .. note::
514
515 This method is an extension to the DB API definition.
516
517
518 .. method:: Connection.startup(force=False, restrict=False, pfile=None)
519
520 Startup the database. This is equivalent to the SQL\*Plus command "startup
521 nomount". The connection must be connected as :data:`~cx_Oracle.SYSDBA` or
522 :data:`~cx_Oracle.SYSOPER` with the :data:`~cx_Oracle.PRELIM_AUTH` option
523 specified for this to work.
524
525 The pfile parameter, if specified, is expected to be a string identifying
526 the location of the parameter file (PFILE) which will be used instead of
527 the stored parameter file (SPFILE).
528
529 An example is shown below:
530
531 ::
532
533 import cx_Oracle
534
535 connection = cx_Oracle.connect(
536 mode=cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
537 connection.startup()
538 connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA)
539 cursor = connection.cursor()
540 cursor.execute("alter database mount")
541 cursor.execute("alter database open")
542
543 .. note::
544
545 This method is an extension to the DB API definition.
546
547
548 .. attribute:: Connection.stmtcachesize
549
550 This read-write attribute specifies the size of the statement cache. This
551 value can make a significant difference in performance (up to 100x) if you
552 have a small number of statements that you execute repeatedly.
553
554 The default value is 20.
555
556 See :ref:`Statement Caching <stmtcache>` for more information.
557
558 .. note::
559
560 This attribute is an extension to the DB API definition.
561
562
563 .. 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, clientInitiated=False)
564
565 Return a new :ref:`subscription object <subscrobj>` that receives
566 notifications for events that take place in the database that match the
567 given parameters.
568
569 The namespace parameter specifies the namespace the subscription uses. It
570 can be one of :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE` or
571 :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`.
572
573 The protocol parameter specifies the protocol to use when notifications are
574 sent. Currently the only valid value is :data:`cx_Oracle.SUBSCR_PROTO_OCI`.
575
576 The callback is expected to be a callable that accepts a single parameter.
577 A :ref:`message object <msgobjects>` is passed to this callback whenever a
578 notification is received.
579
580 The timeout value specifies that the subscription expires after the given
581 time in seconds. The default value of 0 indicates that the subscription
582 never expires.
583
584 The operations parameter enables filtering of the messages that are sent
585 (insert, update, delete). The default value will send notifications for all
586 operations. This parameter is only used when the namespace is set to
587 :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE`.
588
589 The port parameter specifies the listening port for callback notifications
590 from the database server. If not specified, an unused port will be selected
591 by the Oracle Client libraries.
592
593 The qos parameter specifies quality of service options. It should be one or
594 more of the following flags, OR'ed together:
595 :data:`cx_Oracle.SUBSCR_QOS_RELIABLE`,
596 :data:`cx_Oracle.SUBSCR_QOS_DEREG_NFY`,
597 :data:`cx_Oracle.SUBSCR_QOS_ROWIDS`,
598 :data:`cx_Oracle.SUBSCR_QOS_QUERY`,
599 :data:`cx_Oracle.SUBSCR_QOS_BEST_EFFORT`.
600
601 The ipAddress parameter specifies the IP address (IPv4 or IPv6) in standard
602 string notation to bind for callback notifications from the database
603 server. If not specified, the client IP address will be determined by the
604 Oracle Client libraries.
605
606 The groupingClass parameter specifies what type of grouping of
607 notifications should take place. Currently, if set, this value can only be
608 set to the value :data:`cx_Oracle.SUBSCR_GROUPING_CLASS_TIME`, which
609 will group notifications by the number of seconds specified in the
610 groupingValue parameter. The groupingType parameter should be one of the
611 values :data:`cx_Oracle.SUBSCR_GROUPING_TYPE_SUMMARY` (the default) or
612 :data:`cx_Oracle.SUBSCR_GROUPING_TYPE_LAST`.
613
614 The name parameter is used to identify the subscription and is specific to
615 the selected namespace. If the namespace parameter is
616 :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE` then the name is optional and
617 can be any value. If the namespace parameter is
618 :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`, however, the name must be in the
619 format '<QUEUE_NAME>' for single consumer queues and
620 '<QUEUE_NAME>:<CONSUMER_NAME>' for multiple consumer queues, and identifies
621 the queue that will be monitored for messages. The queue name may include
622 the schema, if needed.
623
624 The clientInitiated parameter is used to determine if client initiated
625 connections or server initiated connections (the default) will be
626 established. Client initiated connections are only available in Oracle
627 Client 19.4 and Oracle Database 19.4 and higher.
628
629 .. versionadded:: 6.4
630
631 The parameters ipAddress, groupingClass, groupingValue, groupingType
632 and name were added.
633
634 .. versionadded:: 7.3
635
636 The parameter clientInitiated was added.
637
638 .. note::
639
640 This method is an extension to the DB API definition.
641
642 .. note::
643
644 The subscription can be deregistered in the database by calling the
645 function :meth:`~Connection.unsubscribe()`. If this method is not
646 called and the connection that was used to create the subscription is
647 explicitly closed using the function :meth:`~Connection.close()`, the
648 subscription will not be deregistered in the database.
649
650
651 .. attribute:: Connection.tag
652
653 This read-write attribute initially contains the actual tag of the session
654 that was acquired from a pool by :meth:`SessionPool.acquire()`. If the
655 connection was not acquired from a pool or no tagging parameters were
656 specified (tag and matchanytag) when the connection was acquired from the
657 pool, this value will be None. If the value is changed, it must be a string
658 containing name=value pairs like "k1=v1;k2=v2".
659
660 If this value is not None when the connection is released back to the pool
661 it will be used to retag the session. This value can be overridden in the
662 call to :meth:`SessionPool.release()`.
663
664 .. note::
665
666 This attribute is an extension to the DB API definition.
667
668 .. versionadded:: 7.1
669
670
671 .. attribute:: Connection.tnsentry
672
673 This read-only attribute returns the TNS entry of the database to which a
674 connection has been established.
675
676 .. note::
677
678 This attribute is an extension to the DB API definition.
679
680
681 .. method:: Connection.unsubscribe(subscr)
682
683 Unsubscribe from events in the database that were originally subscribed to
684 using :meth:`~Connection.subscribe()`. The connection used to unsubscribe
685 should be the same one used to create the subscription, or should access
686 the same database and be connected as the same user name.
687
688 .. versionadded:: 6.4
689
690
691 .. attribute:: Connection.username
692
693 This read-only attribute returns the name of the user which established the
694 connection to the database.
695
696 .. note::
697
698 This attribute is an extension to the DB API definition.
699
700
701 .. attribute:: Connection.version
702
703 This read-only attribute returns the version of the database to which a
704 connection has been established.
705
706 .. note::
707
708 This attribute is an extension to the DB API definition.
709
710 .. note::
711
712 If you connect to Oracle Database 18 or higher with client libraries
713 12.2 or lower that you will only receive the base version (such as
714 18.0.0.0.0) instead of the full version (18.3.0.0.0).
0 .. _cursorobj:
1
2 *************
3 Cursor Object
4 *************
5
6
7 .. method:: Cursor.__enter__()
8
9 The entry point for the cursor as a context manager. It returns itself.
10
11 .. note::
12
13 This method is an extension to the DB API definition.
14
15
16 .. method:: Cursor.__exit__()
17
18 The exit point for the cursor as a context manager. It closes the cursor.
19
20 .. note::
21
22 This method is an extension to the DB API definition.
23
24
25 .. attribute:: Cursor.arraysize
26
27 This read-write attribute can be used to tune the number of rows internally
28 fetched and buffered by internal calls to the database when fetching rows
29 from SELECT statements and REF CURSORS. The value can drastically affect
30 the performance of a query since it directly affects the number of network
31 round trips between Python and the database. For methods like
32 :meth:`~Cursor.fetchone()` and :meth:`~Cursor.fetchall()` it does not change
33 how many rows are returned to the application. For
34 :meth:`~Cursor.fetchmany()` it is the default number of rows to fetch.
35
36 Due to the performance benefits, the default ``Cursor.arraysize`` is 100
37 instead of the 1 that the DB API recommends. This value means that 100 rows
38 are fetched by each internal call to the database.
39
40 See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
41
42 .. attribute:: Cursor.bindarraysize
43
44 This read-write attribute specifies the number of rows to bind at a time
45 and is used when creating variables via :meth:`~Cursor.setinputsizes()` or
46 :meth:`~Cursor.var()`. It defaults to 1 meaning to bind a single row at a
47 time.
48
49 .. note::
50
51 The DB API definition does not define this attribute.
52
53
54 .. method:: Cursor.arrayvar(dataType, value, [size])
55
56 Create an array variable associated with the cursor of the given type and
57 size and return a :ref:`variable object <varobj>`. The value is either an
58 integer specifying the number of elements to allocate or it is a list and
59 the number of elements allocated is drawn from the size of the list. If the
60 value is a list, the variable is also set with the contents of the list. If
61 the size is not specified and the type is a string or binary, 4000 bytes
62 is allocated. This is needed for passing arrays to PL/SQL (in cases where
63 the list might be empty and the type cannot be determined automatically) or
64 returning arrays from PL/SQL.
65
66 Array variables can only be used for PL/SQL associative arrays with
67 contiguous keys. For PL/SQL associative arrays with sparsely populated keys
68 or for varrays and nested tables, the approach shown in this
69 `example <https://github.com/oracle/python-cx_Oracle/blob/master/
70 samples/PLSQLCollection.py>`__ needs to be used.
71
72 .. note::
73
74 The DB API definition does not define this method.
75
76
77 .. method:: Cursor.bindnames()
78
79 Return the list of bind variable names bound to the statement. Note that a
80 statement must have been prepared first.
81
82 .. note::
83
84 The DB API definition does not define this method.
85
86
87 .. attribute:: Cursor.bindvars
88
89 This read-only attribute provides the bind variables used for the last
90 execute. The value will be either a list or a dictionary depending on
91 whether binding was done by position or name. Care should be taken when
92 referencing this attribute. In particular, elements should not be removed
93 or replaced.
94
95 .. note::
96
97 The DB API definition does not define this attribute.
98
99
100 .. method:: Cursor.callfunc(name, returnType, parameters=[], \
101 keywordParameters={})
102
103 Call a function with the given name. The return type is specified in the
104 same notation as is required by :meth:`~Cursor.setinputsizes()`. The
105 sequence of parameters must contain one entry for each parameter that the
106 function expects. Any keyword parameters will be included after the
107 positional parameters. The result of the call is the return value of the
108 function.
109
110 See :ref:`plsqlfunc` for an example.
111
112 .. note::
113
114 The DB API definition does not define this method.
115
116 .. note::
117
118 If you intend to call :meth:`Cursor.setinputsizes()` on the cursor
119 prior to making this call, then note that the first item in the
120 parameter list refers to the return value of the function.
121
122
123 .. method:: Cursor.callproc(name, parameters=[], keywordParameters={})
124
125 Call a procedure with the given name. The sequence of parameters must
126 contain one entry for each parameter that the procedure expects. The result
127 of the call is a modified copy of the input sequence. Input parameters are
128 left untouched; output and input/output parameters are replaced with
129 possibly new values. Keyword parameters will be included after the
130 positional parameters and are not returned as part of the output sequence.
131
132 See :ref:`plsqlproc` for an example.
133
134 .. note::
135
136 The DB API definition does not allow for keyword parameters.
137
138
139 .. method:: Cursor.close()
140
141 Close the cursor now, rather than whenever __del__ is called. The cursor
142 will be unusable from this point forward; an Error exception will be raised
143 if any operation is attempted with the cursor.
144
145
146 .. attribute:: Cursor.connection
147
148 This read-only attribute returns a reference to the connection object on
149 which the cursor was created.
150
151 .. note::
152
153 This attribute is an extension to the DB API definition but it is
154 mentioned in PEP 249 as an optional extension.
155
156
157 .. data:: Cursor.description
158
159 This read-only attribute is a sequence of 7-item sequences. Each of these
160 sequences contains information describing one result column: (name, type,
161 display_size, internal_size, precision, scale, null_ok). This attribute
162 will be None for operations that do not return rows or if the cursor has
163 not had an operation invoked via the :meth:`~Cursor.execute()` method yet.
164
165 The type will be one of the :ref:`database type constants <dbtypes>`
166 defined at the module level.
167
168
169 .. method:: Cursor.execute(statement, [parameters], \*\*keywordParameters)
170
171 Execute a statement against the database. See :ref:`sqlexecution`.
172
173 Parameters may be passed as a dictionary or sequence or as keyword
174 parameters. If the parameters are a dictionary, the values will be bound by
175 name and if the parameters are a sequence the values will be bound by
176 position. Note that if the values are bound by position, the order of the
177 variables is from left to right as they are encountered in the statement
178 and SQL statements are processed differently than PL/SQL statements. For
179 this reason, it is generally recommended to bind parameters by name instead
180 of by position.
181
182 Parameters passed as a dictionary are name and value pairs. The name maps
183 to the bind variable name used by the statement and the value maps to the
184 Python value you wish bound to that bind variable.
185
186 A reference to the statement will be retained by the cursor. If None or the
187 same string object is passed in again, the cursor will execute that
188 statement again without performing a prepare or rebinding and redefining.
189 This is most effective for algorithms where the same statement is used, but
190 different parameters are bound to it (many times). Note that parameters
191 that are not passed in during subsequent executions will retain the value
192 passed in during the last execution that contained them.
193
194 For maximum efficiency when reusing an statement, it is best to use the
195 :meth:`~Cursor.setinputsizes()` method to specify the parameter types and
196 sizes ahead of time; in particular, None is assumed to be a string of
197 length 1 so any values that are later bound as numbers or dates will raise
198 a TypeError exception.
199
200 If the statement is a query, the cursor is returned as a convenience to the
201 caller (so it can be used directly as an iterator over the rows in the
202 cursor); otherwise, ``None`` is returned.
203
204 .. note::
205
206 The DB API definition does not define the return value of this method.
207
208
209 .. method:: Cursor.executemany(statement, parameters, batcherrors=False, \
210 arraydmlrowcounts=False)
211
212 Prepare a statement for execution against a database and then execute it
213 against all parameter mappings or sequences found in the sequence
214 parameters. See :ref:`batchstmnt`.
215
216 The statement is managed in the same way as the :meth:`~Cursor.execute()`
217 method manages it. If the size of the buffers allocated for any of the
218 parameters exceeds 2 GB, you will receive the error "DPI-1015: array size
219 of <n> is too large", where <n> varies with the size of each element being
220 allocated in the buffer. If you receive this error, decrease the number of
221 elements in the sequence parameters.
222
223 If there are no parameters, or parameters have previously been bound, the
224 number of iterations can be specified as an integer instead of needing to
225 provide a list of empty mappings or sequences.
226
227 When true, the batcherrors parameter enables batch error support within
228 Oracle and ensures that the call succeeds even if an exception takes place
229 in one or more of the sequence of parameters. The errors can then be
230 retrieved using :meth:`~Cursor.getbatcherrors()`.
231
232 When true, the arraydmlrowcounts parameter enables DML row counts to be
233 retrieved from Oracle after the method has completed. The row counts can
234 then be retrieved using :meth:`~Cursor.getarraydmlrowcounts()`.
235
236 Both the batcherrors parameter and the arraydmlrowcounts parameter can only
237 be true when executing an insert, update, delete or merge statement; in all
238 other cases an error will be raised.
239
240 For maximum efficiency, it is best to use the
241 :meth:`~Cursor.setinputsizes()` method to specify the parameter types and
242 sizes ahead of time; in particular, None is assumed to be a string of
243 length 1 so any values that are later bound as numbers or dates will raise
244 a TypeError exception.
245
246
247 .. method:: Cursor.executemanyprepared(numIters)
248
249 Execute the previously prepared and bound statement the given number of
250 times. The variables that are bound must have already been set to their
251 desired value before this call is made. This method was designed for the
252 case where optimal performance is required as it comes at the expense of
253 compatibility with the DB API.
254
255 .. note::
256
257 The DB API definition does not define this method.
258
259 .. deprecated:: 6.4
260 Use :meth:`~Cursor.executemany()` instead with None for the statement
261 argument and an integer for the parameters argument.
262
263
264 .. method:: Cursor.fetchall()
265
266 Fetch all (remaining) rows of a query result, returning them as a list of
267 tuples. An empty list is returned if no more rows are available. Note that
268 the cursor's arraysize attribute can affect the performance of this
269 operation, as internally reads from the database are done in batches
270 corresponding to the arraysize.
271
272 An exception is raised if the previous call to :meth:`~Cursor.execute()`
273 did not produce any result set or no call was issued yet.
274
275 See :ref:`fetching` for an example.
276
277
278 .. method:: Cursor.fetchmany([numRows=cursor.arraysize])
279
280 Fetch the next set of rows of a query result, returning a list of tuples.
281 An empty list is returned if no more rows are available. Note that the
282 cursor's arraysize attribute can affect the performance of this operation.
283
284 The number of rows to fetch is specified by the parameter. If it is not
285 given, the cursor's arraysize attribute determines the number of rows to be
286 fetched. If the number of rows available to be fetched is fewer than the
287 amount requested, fewer rows will be returned.
288
289 An exception is raised if the previous call to :meth:`~Cursor.execute()`
290 did not produce any result set or no call was issued yet.
291
292 See :ref:`fetching` for an example.
293
294 .. method:: Cursor.fetchone()
295
296 Fetch the next row of a query result set, returning a single tuple or None
297 when no more data is available.
298
299 An exception is raised if the previous call to :meth:`~Cursor.execute()`
300 did not produce any result set or no call was issued yet.
301
302 See :ref:`fetching` for an example.
303
304 .. method:: Cursor.fetchraw([numRows=cursor.arraysize])
305
306 Fetch the next set of rows of a query result into the internal buffers of
307 the defined variables for the cursor. The number of rows actually fetched
308 is returned. This method was designed for the case where optimal
309 performance is required as it comes at the expense of compatibility with
310 the DB API.
311
312 An exception is raised if the previous call to :meth:`~Cursor.execute()`
313 did not produce any result set or no call was issued yet.
314
315 .. note::
316
317 The DB API definition does not define this method.
318
319
320 .. attribute:: Cursor.fetchvars
321
322 This read-only attribute specifies the list of variables created for the
323 last query that was executed on the cursor. Care should be taken when
324 referencing this attribute. In particular, elements should not be removed
325 or replaced.
326
327 .. note::
328
329 The DB API definition does not define this attribute.
330
331
332 .. method:: Cursor.getarraydmlrowcounts()
333
334 Retrieve the DML row counts after a call to :meth:`~Cursor.executemany()`
335 with arraydmlrowcounts enabled. This will return a list of integers
336 corresponding to the number of rows affected by the DML statement for each
337 element of the array passed to :meth:`~Cursor.executemany()`.
338
339 .. note::
340
341 The DB API definition does not define this method and it is only
342 available for Oracle 12.1 and higher.
343
344
345 .. method:: Cursor.getbatcherrors()
346
347 Retrieve the exceptions that took place after a call to
348 :meth:`~Cursor.executemany()` with batcherrors enabled. This will return a
349 list of Error objects, one error for each iteration that failed. The offset
350 can be determined by looking at the offset attribute of the error object.
351
352 .. note::
353
354 The DB API definition does not define this method.
355
356
357 .. method:: Cursor.getimplicitresults()
358
359 Return a list of cursors which correspond to implicit results made
360 available from a PL/SQL block or procedure without the use of OUT ref
361 cursor parameters. The PL/SQL block or procedure opens the cursors and
362 marks them for return to the client using the procedure
363 dbms_sql.return_result. Cursors returned in this fashion should not be
364 closed. They will be closed automatically by the parent cursor when it is
365 closed. Closing the parent cursor will invalidate the cursors returned by
366 this method.
367
368 .. versionadded:: 5.3
369
370 .. note::
371
372 The DB API definition does not define this method and it is only
373 available for Oracle Database 12.1 (both client and server must be at
374 this level or higher). It is most like the DB API method nextset(), but
375 unlike that method (which requires that the next result set overwrite
376 the current result set), this method returns cursors which can be
377 fetched independently of each other.
378
379
380 .. attribute:: Cursor.inputtypehandler
381
382 This read-write attribute specifies a method called for each value that is
383 bound to a statement executed on the cursor and overrides the attribute
384 with the same name on the connection if specified. The method signature is
385 handler(cursor, value, arraysize) and the return value is expected to be a
386 variable object or None in which case a default variable object will be
387 created. If this attribute is None, the value of the attribute with the
388 same name on the connection is used.
389
390 .. note::
391
392 This attribute is an extension to the DB API definition.
393
394
395 .. method:: Cursor.__iter__()
396
397 Returns the cursor itself to be used as an iterator.
398
399 .. note::
400
401 This method is an extension to the DB API definition but it is
402 mentioned in PEP 249 as an optional extension.
403
404
405 .. data:: Cursor.lastrowid
406
407 This read-only attribute returns the rowid of the last row modified by the
408 cursor. If no row was modified by the last operation performed on the
409 cursor, the value None is returned.
410
411 .. versionadded:: 7.3
412
413
414 .. attribute:: Cursor.outputtypehandler
415
416 This read-write attribute specifies a method called for each column that is
417 to be fetched from this cursor. The method signature is
418 handler(cursor, name, defaultType, length, precision, scale) and the return
419 value is expected to be a variable object or None in which case a default
420 variable object will be created. If this attribute is None, the value of
421 the attribute with the same name on the connection is used instead.
422
423 See :ref:`outputtypehandlers`.
424
425 .. note::
426
427 This attribute is an extension to the DB API definition.
428
429
430 .. method:: Cursor.parse(statement)
431
432 This can be used to parse a statement without actually executing it (this
433 step is done automatically by Oracle when a statement is executed).
434
435 .. note::
436
437 The DB API definition does not define this method.
438
439 .. note::
440
441 You can parse any DML or DDL statement. DDL statements are executed
442 immediately and an implied commit takes place.
443
444
445 .. attribute:: Cursor.prefetchrows
446
447 This read-write attribute can be used to tune the number of rows that the
448 Oracle Client library fetches when a SELECT statement is executed. This
449 value can reduce the number of round-trips to the database that are required
450 to fetch rows but at the cost of additional memory. Setting this value to 0
451 can be useful when the timing of fetches must be explicitly controlled.
452
453 See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
454
455 .. note::
456
457 The DB API definition does not define this method.
458
459
460 .. method:: Cursor.prepare(statement, [tag])
461
462 This can be used before a call to :meth:`~Cursor.execute()` to define the
463 statement that will be executed. When this is done, the prepare phase will
464 not be performed when the call to :meth:`~Cursor.execute()` is made with
465 None or the same string object as the statement. If specified the
466 statement will be returned to the statement cache with the given tag. See
467 the Oracle documentation for more information about the statement cache.
468
469 See :ref:`Statement Caching <stmtcache>` for more information.
470
471 .. note::
472
473 The DB API definition does not define this method.
474
475
476 .. attribute:: Cursor.rowcount
477
478 This read-only attribute specifies the number of rows that have currently
479 been fetched from the cursor (for select statements), that have been
480 affected by the operation (for insert, update, delete and merge
481 statements), or the number of successful executions of the statement
482 (for PL/SQL statements).
483
484
485 .. attribute:: Cursor.rowfactory
486
487 This read-write attribute specifies a method to call for each row that is
488 retrieved from the database. Ordinarily a tuple is returned for each row
489 but if this attribute is set, the method is called with the tuple that
490 would normally be returned, and the result of the method is returned
491 instead.
492
493 See :ref:`rowfactories`.
494
495 .. note::
496
497 The DB API definition does not define this attribute.
498
499
500 .. method:: Cursor.scroll(value=0, mode="relative")
501
502 Scroll the cursor in the result set to a new position according to the
503 mode.
504
505 If mode is "relative" (the default value), the value is taken as an offset
506 to the current position in the result set. If set to "absolute", value
507 states an absolute target position. If set to "first", the cursor is
508 positioned at the first row and if set to "last", the cursor is set to the
509 last row in the result set.
510
511 An error is raised if the mode is "relative" or "absolute" and the scroll
512 operation would position the cursor outside of the result set.
513
514 .. versionadded:: 5.3
515
516 .. note::
517
518 This method is an extension to the DB API definition but it is
519 mentioned in PEP 249 as an optional extension.
520
521
522 .. attribute:: Cursor.scrollable
523
524 This read-write boolean attribute specifies whether the cursor can be
525 scrolled or not. By default, cursors are not scrollable, as the server
526 resources and response times are greater than nonscrollable cursors. This
527 attribute is checked and the corresponding mode set in Oracle when calling
528 the method :meth:`~Cursor.execute()`.
529
530 .. versionadded:: 5.3
531
532 .. note::
533
534 The DB API definition does not define this attribute.
535
536
537 .. method:: Cursor.setinputsizes(\*args, \*\*keywordArgs)
538
539 This can be used before a call to :meth:`~Cursor.execute()`,
540 :meth:`~Cursor.callfunc()` or :meth:`~Cursor.callproc()` to predefine
541 memory areas for the operation's parameters. Each parameter should be a
542 type object corresponding to the input that will be used or it should be an
543 integer specifying the maximum length of a string parameter. Use keyword
544 parameters when binding by name and positional parameters when binding by
545 position. The singleton None can be used as a parameter when using
546 positional parameters to indicate that no space should be reserved for that
547 position.
548
549 .. note::
550
551 If you plan to use :meth:`~Cursor.callfunc()` then be aware that the
552 first parameter in the list refers to the return value of the function.
553
554
555 .. method:: Cursor.setoutputsize(size, [column])
556
557 This method does nothing and is retained solely for compatibility with the
558 DB API. The module automatically allocates as much space as needed to fetch
559 LONG and LONG RAW columns (or CLOB as string and BLOB as bytes).
560
561
562 .. attribute:: Cursor.statement
563
564 This read-only attribute provides the string object that was previously
565 prepared with :meth:`~Cursor.prepare()` or executed with
566 :meth:`~Cursor.execute()`.
567
568 .. note::
569
570 The DB API definition does not define this attribute.
571
572
573 .. method:: Cursor.var(dataType, [size, arraysize, inconverter, outconverter, \
574 typename, encodingErrors])
575
576 Create a variable with the specified characteristics. This method was
577 designed for use with PL/SQL in/out variables where the length or type
578 cannot be determined automatically from the Python object passed in or for
579 use in input and output type handlers defined on cursors or connections.
580
581 The dataType parameter specifies the type of data that should be stored in
582 the variable. This should be one of the
583 :ref:`database type constants <dbtypes>`, :ref:`DB API constants <types>`,
584 an object type returned from the method :meth:`Connection.gettype()` or one
585 of the following Python types:
586
587 .. list-table::
588 :header-rows: 1
589
590 * - Python Type
591 - Database Type
592 * - bool
593 - :attr:`cx_Oracle.DB_TYPE_BOOLEAN`
594 * - bytes
595 - :attr:`cx_Oracle.DB_TYPE_RAW`
596 * - datetime.date
597 - :attr:`cx_Oracle.DB_TYPE_DATE`
598 * - datetime.datetime
599 - :attr:`cx_Oracle.DB_TYPE_DATE`
600 * - datetime.timedelta
601 - :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS`
602 * - decimal.Decimal
603 - :attr:`cx_Oracle.DB_TYPE_NUMBER`
604 * - float
605 - :attr:`cx_Oracle.DB_TYPE_NUMBER`
606 * - int
607 - :attr:`cx_Oracle.DB_TYPE_NUMBER`
608 * - str
609 - :attr:`cx_Oracle.DB_TYPE_VARCHAR`
610
611 The size parameter specifies the length of string and raw variables and is
612 ignored in all other cases. If not specified for string and raw variables,
613 the value 4000 is used.
614
615 The arraysize parameter specifies the number of elements the variable will
616 have. If not specified the bind array size (usually 1) is used. When a
617 variable is created in an output type handler this parameter should be set
618 to the cursor's array size.
619
620 The inconverter and outconverter parameters specify methods used for
621 converting values to/from the database. More information can be found in
622 the section on :ref:`variable objects<varobj>`.
623
624 The typename parameter specifies the name of a SQL object type and must be
625 specified when using type :data:`cx_Oracle.OBJECT` unless the type object
626 was passed directly as the first parameter.
627
628 The encodingErrors parameter specifies what should happen when decoding
629 byte strings fetched from the database into strings. It should be one of
630 the values noted in the builtin
631 `decode <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__
632 function.
633
634 .. note::
635
636 The DB API definition does not define this method.
0 .. _lobobj:
1
2 ***********
3 LOB Objects
4 ***********
5
6 See :ref:`lobdata` for more information about using LOBs.
7
8 .. note::
9
10 This object is an extension the DB API. It is returned whenever Oracle
11 :data:`CLOB`, :data:`BLOB` and :data:`BFILE` columns are fetched.
12
13
14 .. method:: LOB.close()
15
16 Close the LOB. Call this when writing is completed so that the indexes
17 associated with the LOB can be updated -- but only if :meth:`~LOB.open()`
18 was called first.
19
20
21 .. method:: LOB.fileexists()
22
23 Return a boolean indicating if the file referenced by the BFILE type LOB
24 exists.
25
26
27 .. method:: LOB.getchunksize()
28
29 Return the chunk size for the internal LOB. Reading and writing to the LOB
30 in chunks of multiples of this size will improve performance.
31
32
33 .. method:: LOB.getfilename()
34
35 Return a two-tuple consisting of the directory alias and file name for a
36 BFILE type LOB.
37
38
39 .. method:: LOB.isopen()
40
41 Return a boolean indicating if the LOB has been opened using the method
42 :meth:`~LOB.open()`.
43
44
45 .. method:: LOB.open()
46
47 Open the LOB for writing. This will improve performance when writing to a
48 LOB in chunks and there are functional or extensible indexes associated
49 with the LOB. If this method is not called, each write will perform an open
50 internally followed by a close after the write has been completed.
51
52
53 .. method:: LOB.read([offset=1, [amount]])
54
55 Return a portion (or all) of the data in the LOB object. Note that the
56 amount and offset are in bytes for BLOB and BFILE type LOBs and in UCS-2
57 code points for CLOB and NCLOB type LOBs. UCS-2 code points are equivalent
58 to characters for all but supplemental characters. If supplemental
59 characters are in the LOB, the offset and amount will have to be chosen
60 carefully to avoid splitting a character.
61
62
63 .. method:: LOB.setfilename(dirAlias, name)
64
65 Set the directory alias and name of the BFILE type LOB.
66
67
68 .. method:: LOB.size()
69
70 Returns the size of the data in the LOB object. For BLOB and BFILE type
71 LOBs this is the number of bytes. For CLOB and NCLOB type LOBs this is the
72 number of UCS-2 code points. UCS-2 code points are equivalent to characters
73 for all but supplemental characters.
74
75
76 .. method:: LOB.trim([newSize=0])
77
78 Trim the LOB to the new size.
79
80
81 .. attribute:: LOB.type
82
83 This read-only attribute returns the type of the LOB as one of the
84 :ref:`database type constants <dbtypes>`.
85
86 .. versionadded:: 8.0
87
88
89 .. method:: LOB.write(data, [offset=1])
90
91 Write the data to the LOB object at the given offset. The offset is in
92 bytes for BLOB type LOBs and in UCS-2 code points for CLOB and NCLOB type
93 LOBs. UCS-2 code points are equivalent to characters for all but
94 supplemental characters. If supplemental characters are in the LOB, the
95 offset will have to be chosen carefully to avoid splitting a character.
96 Note that if you want to make the LOB value smaller, you must use the
97 :meth:`~LOB.trim()` function.
0 .. module:: cx_Oracle
1
2 .. _module:
3
4 ****************
5 Module Interface
6 ****************
7
8 .. data:: __future__
9
10 Special object which contains attributes which control the behavior of
11 cx_Oracle, allowing for opting in for new features. No attributes are
12 currently supported so all attributes will silently ignore being set and
13 will always appear to have the value None.
14
15 .. note::
16
17 This method is an extension to the DB API definition.
18
19 .. versionadded:: 6.2
20
21
22 .. function:: Binary(string)
23
24 Construct an object holding a binary (long) string value.
25
26
27 .. function:: clientversion()
28
29 Return the version of the client library being used as a 5-tuple. The five
30 values are the major version, minor version, update number, patch number
31 and port update number.
32
33 .. note::
34
35 This method is an extension to the DB API definition.
36
37
38 .. function:: connect(user=None, password=None, dsn=None, \
39 mode=cx_Oracle.DEFAULT_AUTH, handle=0, pool=None, threaded=False, \
40 events=False, cclass=None, purity=cx_Oracle.ATTR_PURITY_DEFAULT, \
41 newpassword=None, encoding=None, nencoding=None, edition=None, \
42 appcontext=[], tag=None, matchanytag=None, shardingkey=[], \
43 supershardingkey=[])
44 Connection(user=None, password=None, dsn=None, \
45 mode=cx_Oracle.DEFAULT_AUTH, handle=0, pool=None, threaded=False, \
46 events=False, cclass=None, purity=cx_Oracle.ATTR_PURITY_DEFAULT, \
47 newpassword=None, encoding=None, nencoding=None, edition=None, \
48 appcontext=[], tag=None, matchanytag=False, shardingkey=[], \
49 supershardingkey=[])
50
51 Constructor for creating a connection to the database. Return a
52 :ref:`connection object <connobj>`. All parameters are optional and can be
53 specified as keyword parameters. See :ref:`connhandling` information about
54 connections.
55
56 The dsn (data source name) is the TNS entry (from the Oracle names server
57 or tnsnames.ora file) or is a string like the one returned from
58 :meth:`~cx_Oracle.makedsn()`. If the user parameter is passed and the
59 password and dsn parameters are not passed, the user parameter is assumed
60 to be a connect string in the format ``user/password@dsn``, the same format
61 accepted by Oracle applications such as SQL\*Plus. See :ref:`connstr` for
62 more information.
63
64 If the mode is specified, it must be one of the
65 :ref:`connection authorization modes<connection-authorization-modes>`
66 which are defined at the module level.
67
68 If the handle is specified, it must be of type OCISvcCtx\* and is only of
69 use when embedding Python in an application (like PowerBuilder) which has
70 already made the connection. The connection thus created should *never* be
71 used after the source handle has been closed or destroyed.
72
73 The pool parameter is expected to be a
74 :ref:`session pool object <sesspool>` and the use of this parameter is the
75 equivalent of calling :meth:`SessionPool.acquire()`. Parameters not
76 accepted by that method are ignored.
77
78 The threaded parameter is expected to be a boolean expression which
79 indicates whether or not Oracle should wrap accesses to connections with a
80 mutex. Doing so in single threaded applications imposes a performance
81 penalty of about 10-15% which is why the default is False.
82
83 The events parameter is expected to be a boolean expression which indicates
84 whether or not to initialize Oracle in events mode. This is required for
85 continuous query notification and high availability event notifications.
86
87 The cclass parameter is expected to be a string and defines the connection
88 class for database resident connection pooling (DRCP).
89
90 The purity parameter is expected to be one of
91 :data:`~cx_Oracle.ATTR_PURITY_NEW`, :data:`~cx_Oracle.ATTR_PURITY_SELF`, or
92 :data:`~cx_Oracle.ATTR_PURITY_DEFAULT`.
93
94 The newpassword parameter is expected to be a string if specified and sets
95 the password for the logon during the connection process.
96
97 See the :ref:`globalization <globalization>` section for details on the
98 encoding and nencoding parameters. Note the default encoding and nencoding
99 values changed to "UTF-8" in cx_Oracle 8, and any character set in NLS_LANG
100 is ignored.
101
102 The edition parameter is expected to be a string if specified and sets the
103 edition to use for the session. It is only relevant if both the client and
104 the database are at least Oracle Database 11.2. If this parameter is used
105 with the cclass parameter the exception "DPI-1058: edition not supported
106 with connection class" will be raised.
107
108 The appcontext parameter is expected to be a list of 3-tuples, if specified,
109 and sets the application context for the connection. Application context
110 is available in the database by using the sys_context() PL/SQL method and
111 can be used within a logon trigger as well as any other PL/SQL procedures.
112 Each entry in the list is expected to contain three strings: the namespace,
113 the name and the value.
114
115 The tag parameter, if specified, is expected to be a string and will limit
116 the sessions that can be returned from a session pool unless the
117 matchanytag parameter is set to True. In that case sessions with the
118 specified tag will be preferred over others, but if no such sessions are
119 available a session with a different tag may be returned instead. In any
120 case, untagged sessions will always be returned if no sessions with the
121 specified tag are available. Sessions are tagged when they are
122 :meth:`released <SessionPool.release>` back to the pool.
123
124 The shardingkey and supershardingkey parameters, if specified, are expected
125 to be a sequence of values which will be used to identify the database
126 shard to connect to. The key values can be strings, numbers, bytes or dates.
127
128
129 .. function:: Cursor(connection)
130
131 Constructor for creating a cursor. Return a new
132 :ref:`cursor object <cursorobj>` using the connection.
133
134 .. note::
135
136 This method is an extension to the DB API definition.
137
138
139 .. function:: Date(year, month, day)
140
141 Construct an object holding a date value.
142
143
144 .. function:: DateFromTicks(ticks)
145
146 Construct an object holding a date value from the given ticks value (number
147 of seconds since the epoch; see the documentation of the standard Python
148 time module for details).
149
150
151 .. function:: init_oracle_client(lib_dir=None, config_dir=None, \
152 error_url=None, driver_name=None)
153
154 Initialize the Oracle client library now, rather than when
155 :func:`cx_Oracle.clientversion()`, :func:`cx_Oracle.connect()` or
156 :func:`cx_Oracle.SessionPool()` is called for the first time. If
157 initialization has already taken place, an exception is raised.
158
159 If the parameter `lib_dir` is not `None` or the empty string,
160 the specified directory is the only one searched for the Oracle Client
161 libraries; otherwise, the standard way of locating the Oracle Client
162 library is used.
163
164 If the parameter `config_dir` is not `None` or the empty string, the
165 specified directory is used to find Oracle Client library configuration
166 files. This is equivalent to setting the environment variable `TNS_ADMIN`
167 and overrides any value already set in `TNS_ADMIN`. If this parameter is not
168 set, the standard way of locating Oracle Client library configuration files
169 is used.
170
171 If the parameter `error_url` is not `None` or the empty string, the
172 specified value is included in the message of the exception raised when the
173 Oracle Client library cannot be loaded; otherwise, the :ref:`installation`
174 URL is included.
175
176 If the parameter `driver_name` is not `None` or the empty string, the
177 specified value can be found in database views that give information about
178 connections. For example, it is in the ``CLIENT_DRIVER`` column of
179 ``V$SESSION_CONNECT_INFO``. The standard is to set this value to
180 ``"<name> : version>"``, where <name> is the name of the driver and
181 <version> is its version. There should be a single space character before
182 and after the colon. If this value is not specified, then the default
183 value of "cx_Oracle : <version>" is used.
184
185 See :ref:`initialization` for more discussion.
186
187 .. note::
188
189 This method is an extension to the DB API definition.
190
191
192 .. function:: makedsn(host, port, sid=None, service_name=None, region=None, \
193 sharding_key=None, super_sharding_key=None)
194
195 Return a string suitable for use as the dsn parameter for
196 :meth:`~cx_Oracle.connect()`. This string is identical to the strings that
197 are defined by the Oracle names server or defined in the tnsnames.ora file.
198
199 .. note::
200
201 This method is an extension to the DB API definition.
202
203
204 .. function:: SessionPool(user=None, password=None, dsn=None, min=1, max=2, \
205 increment=1, connectiontype=cx_Oracle.Connection, threaded=False, \
206 getmode=cx_Oracle.SPOOL_ATTRVAL_NOWAIT, events=False, \
207 homogeneous=True, externalauth=False, encoding=None, nencoding=None, \
208 edition=None, timeout=0, waitTimeout=0, maxLifetimeSession=0, \
209 sessionCallback=None, maxSessionsPerShard=0)
210
211 Create and return a :ref:`session pool object <sesspool>`. Session pooling
212 (also known as connection pooling) creates a pool of available connections
213 to the database, allowing applications to acquire a connection very quickly.
214 It is of primary use in a server where connections are requested in rapid
215 succession and used for a short period of time, for example in a web server.
216 See :ref:`connpool` for more information.
217
218 Connection pooling in cx_Oracle is handled by Oracle's
219 `Session pooling <https://www.oracle.com/pls/topic/lookup?
220 ctx=dblatest&id=GUID-F9662FFB-EAEF-495C-96FC-49C6D1D9625C>`__
221 technology. This allows cx_Oracle applications to support features
222 like `Application Continuity <https://www.oracle.com/pls/topic/lookup?
223 ctx=dblatest&id=GUID-A8DD9422-2F82-42A9-9555-134296416E8F>`__.
224
225 The user, password and dsn parameters are the same as for
226 :meth:`cx_Oracle.connect()`
227
228 The min, max and increment parameters control pool growth behavior. A fixed
229 pool size where min equals max is recommended to help prevent connection
230 storms and to help overall system stability. The min parameter is the
231 number of connections opened when the pool is created. The increment is the
232 number of connections that are opened whenever a connection request exceeds
233 the number of currently open connections. The max parameter is the maximum
234 number of connections that can be open in the connection pool.
235
236 Note that when using :ref:`external authentication <extauth>`,
237 :ref:`heterogeneous pools <connpooltypes>`, or :ref:`drcp`, then the pool
238 growth behavior is different. In these cases the number of connections
239 created at pool startup is always zero, and the increment is always one.
240
241 If the connectiontype parameter is specified, all calls to
242 :meth:`~SessionPool.acquire()` will create connection objects of that type,
243 rather than the base type defined at the module level.
244
245 The threaded parameter is expected to be a boolean expression which
246 indicates whether Oracle should wrap accesses to connections with a mutex.
247 Doing so in single threaded applications imposes a performance penalty of
248 about 10-15% which is why the default is False.
249
250 The getmode parameter indicates whether or not future
251 :func:`SessionPool.acquire()` calls will wait for available connections. It
252 can be one of the :ref:`Session Pool Get Modes <sesspoolmodes>` values.
253
254 The events parameter is expected to be a boolean expression which indicates
255 whether or not to initialize Oracle in events mode. This is required for
256 continuous query notification and high availability event notifications.
257
258 The homogeneous parameter is expected to be a boolean expression which
259 indicates whether or not to create a homogeneous pool. A homogeneous pool
260 requires that all connections in the pool use the same credentials. As such
261 proxy authentication and external authentication is not possible with a
262 homogeneous pool. See :ref:`Heterogeneous and Homogeneous Connection Pools
263 <connpooltypes>`.
264
265 The externalauth parameter is expected to be a boolean expression which
266 indicates whether or not external authentication should be used. External
267 authentication implies that something other than the database is
268 authenticating the user to the database. This includes the use of operating
269 system authentication and Oracle wallets. See :ref:`Connecting Using
270 External Authentication <extauth>`.
271
272 The encoding and nencoding parameters set the encodings used for string
273 values transferred between cx_Oracle and Oracle Database, see
274 :ref:`Character Sets and Globalization <globalization>`. Note the default
275 encoding and nencoding values changed to "UTF-8" in cx_Oracle 8, and any
276 character set in NLS_LANG is ignored.
277
278 The edition parameter is expected to be a string, if specified, and sets the
279 edition to use for the sessions in the pool. It is only relevant if both the
280 client and the server are at least Oracle Database 11.2. See
281 :ref:`Edition-Based Redefinition (EBR) <ebr>`.
282
283 The timeout parameter is expected to be an integer, if specified, and sets
284 the length of time (in seconds) after which idle sessions in the pool are
285 terminated. Note that termination only occurs when the pool is accessed.
286 The default value of 0 means that no idle sessions are terminated.
287
288 The waitTimeout parameter is expected to be an integer, if specified, and
289 sets the length of time (in milliseconds) that the caller should wait for
290 a session to become available in the pool before returning with an error.
291 This value is only used if the getmode parameter is set to the value
292 :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`.
293
294 The maxLifetimeSession parameter is expected to be an integer, if
295 specified, and sets the maximum length of time (in seconds) a pooled
296 session may exist. Sessions that are in use will not be closed. They become
297 candidates for termination only when they are released back to the pool and
298 have existed for longer than maxLifetimeSession seconds. Note that
299 termination only occurs when the pool is accessed. The default value is 0
300 which means that there is no maximum length of time that a pooled session
301 may exist.
302
303 The sessionCallback parameter is expected to be either a string or a
304 callable. If the sessionCallback parameter is a callable, it will be called
305 when a newly created connection is returned from the pool, or when a tag is
306 requested and that tag does not match the connection's actual tag. The
307 callable will be invoked with the connection and the requested tag as its
308 only parameters. If the parameter is a string, it should be the name of a
309 PL/SQL procedure that will be called when :func:`SessionPool.acquire()`
310 requests a tag and that tag does not match the connection's actual tag. See
311 :ref:`Session CallBacks for Setting Pooled Connection State
312 <sessioncallback>`. Support for the PL/SQL procedure requires Oracle Client
313 libraries 12.2 or later. See the `OCI documentation
314 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
315 id=GUID-B853A020-752F-494A-8D88-D0396EF57177>`__ for more information.
316
317 The maxSessionsPerShard parameter is expected to be an integer, if
318 specified, and sets the maximum number of sessions in the pool that can be
319 used for any given shard in a sharded database. This value is ignored if
320 the Oracle client library version is less than 18.3.
321
322 .. note::
323
324 This method is an extension to the DB API definition.
325
326
327 .. function:: Time(hour, minute, second)
328
329 Construct an object holding a time value.
330
331 .. note::
332
333 The time only data type is not supported by Oracle. Calling this
334 function will raise a NotSupportedError exception.
335
336
337
338 .. function:: TimeFromTicks(ticks)
339
340 Construct an object holding a time value from the given ticks value (number
341 of seconds since the epoch; see the documentation of the standard Python
342 time module for details).
343
344 .. note::
345
346 The time only data type is not supported by Oracle. Calling this
347 function will raise a NotSupportedError exception.
348
349
350 .. function:: Timestamp(year, month, day, hour, minute, second)
351
352 Construct an object holding a time stamp value.
353
354
355 .. function:: TimestampFromTicks(ticks)
356
357 Construct an object holding a time stamp value from the given ticks value
358 (number of seconds since the epoch; see the documentation of the standard
359 Python time module for details).
360
361
362
363 .. _constants:
364
365 Constants
366 =========
367
368 General
369 -------
370
371 .. data:: apilevel
372
373 String constant stating the supported DB API level. Currently '2.0'.
374
375
376 .. data:: buildtime
377
378 String constant stating the time when the binary was built.
379
380 .. note::
381
382 This constant is an extension to the DB API definition.
383
384
385 .. data:: paramstyle
386
387 String constant stating the type of parameter marker formatting expected by
388 the interface. Currently 'named' as in 'where name = :name'.
389
390
391 .. data:: threadsafety
392
393 Integer constant stating the level of thread safety that the interface
394 supports. Currently 2, which means that threads may share the module and
395 connections, but not cursors. Sharing means that a thread may use a
396 resource without wrapping it using a mutex semaphore to implement resource
397 locking.
398
399 Note that in order to make use of multiple threads in a program which
400 intends to connect and disconnect in different threads, the threaded
401 parameter to :meth:`connect()` or :meth:`SessionPool()` must be true.
402
403
404 .. data:: version
405 .. data:: __version__
406
407 String constant stating the version of the module. Currently '|release|'.
408
409 .. note::
410
411 This attribute is an extension to the DB API definition.
412
413
414 Advanced Queuing: Delivery Modes
415 --------------------------------
416
417 These constants are extensions to the DB API definition. They are possible
418 values for the :attr:`~DeqOptions.deliverymode` attribute of the
419 :ref:`dequeue options object <deqoptions>` passed as the options parameter to
420 the :meth:`Connection.deq()` method as well as the
421 :attr:`~EnqOptions.deliverymode` attribute of the
422 :ref:`enqueue options object <enqoptions>` passed as the options parameter to
423 the :meth:`Connection.enq()` method. They are also possible values for the
424 :attr:`~MessageProperties.deliverymode` attribute of the
425 :ref:`message properties object <msgproperties>` passed as the msgproperties
426 parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()` methods.
427
428
429 .. data:: MSG_BUFFERED
430
431 This constant is used to specify that enqueue/dequeue operations should
432 enqueue or dequeue buffered messages.
433
434
435 .. data:: MSG_PERSISTENT
436
437 This constant is used to specify that enqueue/dequeue operations should
438 enqueue or dequeue persistent messages. This is the default value.
439
440
441 .. data:: MSG_PERSISTENT_OR_BUFFERED
442
443 This constant is used to specify that dequeue operations should dequeue
444 either persistent or buffered messages.
445
446
447 Advanced Queuing: Dequeue Modes
448 -------------------------------
449
450 These constants are extensions to the DB API definition. They are possible
451 values for the :attr:`~DeqOptions.mode` attribute of the
452 :ref:`dequeue options object <deqoptions>`. This object is the options
453 parameter for the :meth:`Connection.deq()` method.
454
455
456 .. data:: DEQ_BROWSE
457
458 This constant is used to specify that dequeue should read the message
459 without acquiring any lock on the message (equivalent to a select
460 statement).
461
462
463 .. data:: DEQ_LOCKED
464
465 This constant is used to specify that dequeue should read and obtain a
466 write lock on the message for the duration of the transaction (equivalent
467 to a select for update statement).
468
469
470 .. data:: DEQ_REMOVE
471
472 This constant is used to specify that dequeue should read the message and
473 update or delete it. This is the default value.
474
475
476 .. data:: DEQ_REMOVE_NODATA
477
478 This constant is used to specify that dequeue should confirm receipt of the
479 message but not deliver the actual message content.
480
481
482 Advanced Queuing: Dequeue Navigation Modes
483 ------------------------------------------
484
485 These constants are extensions to the DB API definition. They are possible
486 values for the :attr:`~DeqOptions.navigation` attribute of the
487 :ref:`dequeue options object <deqoptions>`. This object is the options
488 parameter for the :meth:`Connection.deq()` method.
489
490
491 .. data:: DEQ_FIRST_MSG
492
493 This constant is used to specify that dequeue should retrieve the first
494 available message that matches the search criteria. This resets the
495 position to the beginning of the queue.
496
497
498 .. data:: DEQ_NEXT_MSG
499
500 This constant is used to specify that dequeue should retrieve the next
501 available message that matches the search criteria. If the previous message
502 belongs to a message group, AQ retrieves the next available message that
503 matches the search criteria and belongs to the message group. This is the
504 default.
505
506
507 .. data:: DEQ_NEXT_TRANSACTION
508
509 This constant is used to specify that dequeue should skip the remainder of
510 the transaction group and retrieve the first message of the next
511 transaction group. This option can only be used if message grouping is
512 enabled for the current queue.
513
514
515 Advanced Queuing: Dequeue Visibility Modes
516 ------------------------------------------
517
518 These constants are extensions to the DB API definition. They are possible
519 values for the :attr:`~DeqOptions.visibility` attribute of the
520 :ref:`dequeue options object <deqoptions>`. This object is the options
521 parameter for the :meth:`Connection.deq()` method.
522
523
524 .. data:: DEQ_IMMEDIATE
525
526 This constant is used to specify that dequeue should perform its work as
527 part of an independent transaction.
528
529
530 .. data:: DEQ_ON_COMMIT
531
532 This constant is used to specify that dequeue should be part of the current
533 transaction. This is the default value.
534
535
536 Advanced Queuing: Dequeue Wait Modes
537 ------------------------------------
538
539 These constants are extensions to the DB API definition. They are possible
540 values for the :attr:`~DeqOptions.wait` attribute of the
541 :ref:`dequeue options object <deqoptions>`. This object is the options
542 parameter for the :meth:`Connection.deq()` method.
543
544
545 .. data:: DEQ_NO_WAIT
546
547 This constant is used to specify that dequeue not wait for messages to be
548 available for dequeuing.
549
550
551 .. data:: DEQ_WAIT_FOREVER
552
553 This constant is used to specify that dequeue should wait forever for
554 messages to be available for dequeuing. This is the default value.
555
556
557 Advanced Queuing: Enqueue Visibility Modes
558 ------------------------------------------
559
560 These constants are extensions to the DB API definition. They are possible
561 values for the :attr:`~EnqOptions.visibility` attribute of the
562 :ref:`enqueue options object <enqoptions>`. This object is the options
563 parameter for the :meth:`Connection.enq()` method.
564
565
566 .. data:: ENQ_IMMEDIATE
567
568 This constant is used to specify that enqueue should perform its work as
569 part of an independent transaction.
570
571
572 .. data:: ENQ_ON_COMMIT
573
574 This constant is used to specify that enqueue should be part of the current
575 transaction. This is the default value.
576
577
578 Advanced Queuing: Message States
579 --------------------------------
580
581 These constants are extensions to the DB API definition. They are possible
582 values for the :attr:`~MessageProperties.state` attribute of the
583 :ref:`message properties object <msgproperties>`. This object is the
584 msgproperties parameter for the :meth:`Connection.deq()` and
585 :meth:`Connection.enq()` methods.
586
587
588 .. data:: MSG_EXPIRED
589
590 This constant is used to specify that the message has been moved to the
591 exception queue.
592
593
594 .. data:: MSG_PROCESSED
595
596 This constant is used to specify that the message has been processed and
597 has been retained.
598
599
600 .. data:: MSG_READY
601
602 This constant is used to specify that the message is ready to be processed.
603
604
605 .. data:: MSG_WAITING
606
607 This constant is used to specify that the message delay has not yet been
608 reached.
609
610
611 Advanced Queuing: Other
612 -----------------------
613
614 These constants are extensions to the DB API definition. They are special
615 constants used in advanced queuing.
616
617
618 .. data:: MSG_NO_DELAY
619
620 This constant is a possible value for the :attr:`~MessageProperties.delay`
621 attribute of the :ref:`message properties object <msgproperties>` passed
622 as the msgproperties parameter to the :meth:`Connection.deq()` and
623 :meth:`Connection.enq()` methods. It specifies that no delay should be
624 imposed and the message should be immediately available for dequeuing. This
625 is also the default value.
626
627
628 .. data:: MSG_NO_EXPIRATION
629
630 This constant is a possible value for the
631 :attr:`~MessageProperties.expiration` attribute of the
632 :ref:`message properties object <msgproperties>` passed as the msgproperties
633 parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()`
634 methods. It specifies that the message never expires. This is also the
635 default value.
636
637
638 .. _connection-authorization-modes:
639
640 Connection Authorization Modes
641 ------------------------------
642
643 These constants are extensions to the DB API definition. They are possible
644 values for the mode parameter of the :meth:`connect()` method.
645
646
647 .. data:: DEFAULT_AUTH
648
649 This constant is used to specify that default authentication is to take
650 place. This is the default value if no mode is passed at all.
651
652 .. versionadded:: 7.2
653
654 .. data:: PRELIM_AUTH
655
656 This constant is used to specify that preliminary authentication is to be
657 used. This is needed for performing database startup and shutdown.
658
659
660 .. data:: SYSASM
661
662 This constant is used to specify that SYSASM access is to be acquired.
663
664
665 .. data:: SYSBKP
666
667 This constant is used to specify that SYSBACKUP access is to be acquired.
668
669
670 .. data:: SYSDBA
671
672 This constant is used to specify that SYSDBA access is to be acquired.
673
674
675 .. data:: SYSDGD
676
677 This constant is used to specify that SYSDG access is to be acquired.
678
679
680 .. data:: SYSKMT
681
682 This constant is used to specify that SYSKM access is to be acquired.
683
684
685 .. data:: SYSOPER
686
687 This constant is used to specify that SYSOPER access is to be acquired.
688
689
690 .. data:: SYSRAC
691
692 This constant is used to specify that SYSRAC access is to be acquired.
693
694
695 Database Shutdown Modes
696 -----------------------
697
698 These constants are extensions to the DB API definition. They are possible
699 values for the mode parameter of the :meth:`Connection.shutdown()` method.
700
701
702 .. data:: DBSHUTDOWN_ABORT
703
704 This constant is used to specify that the caller should not wait for
705 current processing to complete or for users to disconnect from the
706 database. This should only be used in unusual circumstances since database
707 recovery may be necessary upon next startup.
708
709
710 .. data:: DBSHUTDOWN_FINAL
711
712 This constant is used to specify that the instance can be truly halted.
713 This should only be done after the database has been shutdown with one of
714 the other modes (except abort) and the database has been closed and
715 dismounted using the appropriate SQL commands.
716
717
718 .. data:: DBSHUTDOWN_IMMEDIATE
719
720 This constant is used to specify that all uncommitted transactions should
721 be rolled back and any connected users should be disconnected.
722
723
724 .. data:: DBSHUTDOWN_TRANSACTIONAL
725
726 This constant is used to specify that further connections to the database
727 should be prohibited and no new transactions should be allowed. It then
728 waits for all active transactions to complete.
729
730
731 .. data:: DBSHUTDOWN_TRANSACTIONAL_LOCAL
732
733 This constant is used to specify that further connections to the database
734 should be prohibited and no new transactions should be allowed. It then
735 waits for only local active transactions to complete.
736
737
738 Event Types
739 -----------
740
741 These constants are extensions to the DB API definition. They are possible
742 values for the :attr:`Message.type` attribute of the messages that are sent
743 for subscriptions created by the :meth:`Connection.subscribe()` method.
744
745
746 .. data:: EVENT_AQ
747
748 This constant is used to specify that one or more messages are available
749 for dequeuing on the queue specified when the subscription was created.
750
751
752 .. data:: EVENT_DEREG
753
754 This constant is used to specify that the subscription has been
755 deregistered and no further notifications will be sent.
756
757
758 .. data:: EVENT_NONE
759
760 This constant is used to specify no information is available about the
761 event.
762
763
764 .. data:: EVENT_OBJCHANGE
765
766 This constant is used to specify that a database change has taken place on
767 a table registered with the :meth:`Subscription.registerquery()` method.
768
769
770 .. data:: EVENT_QUERYCHANGE
771
772 This constant is used to specify that the result set of a query registered
773 with the :meth:`Subscription.registerquery()` method has been changed.
774
775
776 .. data:: EVENT_SHUTDOWN
777
778 This constant is used to specify that the instance is in the process of
779 being shut down.
780
781
782 .. data:: EVENT_SHUTDOWN_ANY
783
784 This constant is used to specify that any instance (when running RAC) is in
785 the process of being shut down.
786
787
788 .. data:: EVENT_STARTUP
789
790 This constant is used to specify that the instance is in the process of
791 being started up.
792
793
794 .. _cqn-operation-codes:
795
796 Operation Codes
797 ---------------
798
799 These constants are extensions to the DB API definition. They are possible
800 values for the operations parameter for the :meth:`Connection.subscribe()`
801 method. One or more of these values can be OR'ed together. These values are
802 also used by the :attr:`MessageTable.operation` or
803 :attr:`MessageQuery.operation` attributes of the messages that are sent.
804
805
806 .. data:: OPCODE_ALLOPS
807
808 This constant is used to specify that messages should be sent for all
809 operations.
810
811
812 .. data:: OPCODE_ALLROWS
813
814 This constant is used to specify that the table or query has been
815 completely invalidated.
816
817
818 .. data:: OPCODE_ALTER
819
820 This constant is used to specify that messages should be sent when a
821 registered table has been altered in some fashion by DDL, or that the
822 message identifies a table that has been altered.
823
824
825 .. data:: OPCODE_DELETE
826
827 This constant is used to specify that messages should be sent when data is
828 deleted, or that the message identifies a row that has been deleted.
829
830
831 .. data:: OPCODE_DROP
832
833 This constant is used to specify that messages should be sent when a
834 registered table has been dropped, or that the message identifies a table
835 that has been dropped.
836
837
838 .. data:: OPCODE_INSERT
839
840 This constant is used to specify that messages should be sent when data is
841 inserted, or that the message identifies a row that has been inserted.
842
843
844 .. data:: OPCODE_UPDATE
845
846 This constant is used to specify that messages should be sent when data is
847 updated, or that the message identifies a row that has been updated.
848
849 .. _sesspoolmodes:
850
851 Session Pool Get Modes
852 ----------------------
853
854 These constants are extensions to the DB API definition. They are possible
855 values for the getmode parameter of the :meth:`SessionPool()` method.
856
857
858 .. data:: SPOOL_ATTRVAL_FORCEGET
859
860 This constant is used to specify that a new connection will be returned if
861 there are no free sessions available in the pool.
862
863
864 .. data:: SPOOL_ATTRVAL_NOWAIT
865
866 This constant is used to specify that an exception should be raised if
867 there are no free sessions available in the pool. This is the default
868 value.
869
870
871 .. data:: SPOOL_ATTRVAL_WAIT
872
873 This constant is used to specify that the caller should wait until a
874 session is available if there are no free sessions available in the pool.
875
876
877 .. data:: SPOOL_ATTRVAL_TIMEDWAIT
878
879 This constant is used to specify that the caller should wait for a period
880 of time (defined by the waitTimeout parameter) for a session to become
881 available before returning with an error.
882
883
884 Session Pool Purity
885 -------------------
886
887 These constants are extensions to the DB API definition. They are possible
888 values for the purity parameter of the :meth:`connect()` method, which is used
889 in database resident connection pooling (DRCP).
890
891
892 .. data:: ATTR_PURITY_DEFAULT
893
894 This constant is used to specify that the purity of the session is the
895 default value identified by Oracle (see Oracle's documentation for more
896 information). This is the default value.
897
898
899 .. data:: ATTR_PURITY_NEW
900
901 This constant is used to specify that the session acquired from the pool
902 should be new and not have any prior session state.
903
904
905 .. data:: ATTR_PURITY_SELF
906
907 This constant is used to specify that the session acquired from the pool
908 need not be new and may have prior session state.
909
910
911 Subscription Grouping Classes
912 -----------------------------
913
914 These constants are extensions to the DB API definition. They are possible
915 values for the groupingClass parameter of the :meth:`Connection.subscribe()`
916 method.
917
918 .. data:: SUBSCR_GROUPING_CLASS_TIME
919
920 This constant is used to specify that events are to be grouped by the
921 period of time in which they are received.
922
923
924 Subscription Grouping Types
925 ---------------------------
926
927 These constants are extensions to the DB API definition. They are possible
928 values for the groupingType parameter of the :meth:`Connection.subscribe()`
929 method.
930
931 .. data:: SUBSCR_GROUPING_TYPE_SUMMARY
932
933 This constant is used to specify that when events are grouped a summary of
934 the events should be sent instead of the individual events. This is the
935 default value.
936
937 .. data:: SUBSCR_GROUPING_TYPE_LAST
938
939 This constant is used to specify that when events are grouped the last
940 event that makes up the group should be sent instead of the individual
941 events.
942
943
944 .. _subscr-namespaces:
945
946 Subscription Namespaces
947 -----------------------
948
949 These constants are extensions to the DB API definition. They are possible
950 values for the namespace parameter of the :meth:`Connection.subscribe()`
951 method.
952
953 .. data:: SUBSCR_NAMESPACE_AQ
954
955 This constant is used to specify that notifications should be sent when a
956 queue has messages available to dequeue.
957
958 .. data:: SUBSCR_NAMESPACE_DBCHANGE
959
960 This constant is used to specify that database change notification or query
961 change notification messages are to be sent. This is the default value.
962
963
964 .. _subscr-protocols:
965
966 Subscription Protocols
967 ----------------------
968
969 These constants are extensions to the DB API definition. They are possible
970 values for the protocol parameter of the :meth:`Connection.subscribe()` method.
971
972
973 .. data:: SUBSCR_PROTO_HTTP
974
975 This constant is used to specify that notifications will be sent to an
976 HTTP URL when a message is generated. This value is currently not
977 supported.
978
979
980 .. data:: SUBSCR_PROTO_MAIL
981
982 This constant is used to specify that notifications will be sent to an
983 e-mail address when a message is generated. This value is currently not
984 supported.
985
986
987 .. data:: SUBSCR_PROTO_OCI
988
989 This constant is used to specify that notifications will be sent to the
990 callback routine identified when the subscription was created. It is the
991 default value and the only value currently supported.
992
993
994 .. data:: SUBSCR_PROTO_SERVER
995
996 This constant is used to specify that notifications will be sent to a
997 PL/SQL procedure when a message is generated. This value is currently not
998 supported.
999
1000
1001 .. _subscr-qos:
1002
1003 Subscription Quality of Service
1004 -------------------------------
1005
1006 These constants are extensions to the DB API definition. They are possible
1007 values for the qos parameter of the :meth:`Connection.subscribe()` method. One
1008 or more of these values can be OR'ed together.
1009
1010 .. data:: SUBSCR_QOS_BEST_EFFORT
1011
1012 This constant is used to specify that best effort filtering for query
1013 result set changes is acceptable. False positive notifications may be
1014 received. This behaviour may be suitable for caching applications.
1015
1016
1017 .. data:: SUBSCR_QOS_DEREG_NFY
1018
1019 This constant is used to specify that the subscription should be
1020 automatically unregistered after the first notification is received.
1021
1022
1023 .. data:: SUBSCR_QOS_QUERY
1024
1025 This constant is used to specify that notifications should be sent if the
1026 result set of the registered query changes. By default no false positive
1027 notifications will be generated.
1028
1029
1030 .. data:: SUBSCR_QOS_RELIABLE
1031
1032 This constant is used to specify that notifications should not be lost in
1033 the event of database failure.
1034
1035
1036 .. data:: SUBSCR_QOS_ROWIDS
1037
1038 This constant is used to specify that the rowids of the inserted, updated
1039 or deleted rows should be included in the message objects that are sent.
1040
1041
1042 .. _types:
1043
1044 DB API Types
1045 ------------
1046
1047 .. data:: BINARY
1048
1049 This type object is used to describe columns in a database that contain
1050 binary data. The database types :data:`DB_TYPE_RAW` and
1051 :data:`DB_TYPE_LONG_RAW` will compare equal to this value. If a variable is
1052 created with this type, the database type :data:`DB_TYPE_RAW` will be used.
1053
1054
1055 .. data:: DATETIME
1056
1057 This type object is used to describe columns in a database that are dates.
1058 The database types :data:`DB_TYPE_DATE`, :data:`DB_TYPE_TIMESTAMP`,
1059 :data:`DB_TYPE_TIMESTAMP_LTZ` and :data:`DB_TYPE_TIMESTAMP_TZ` will all
1060 compare equal to this value. If a variable is created with this
1061 type, the database type :data:`DB_TYPE_DATE` will be used.
1062
1063
1064 .. data:: NUMBER
1065
1066 This type object is used to describe columns in a database that are
1067 numbers. The database types :data:`DB_TYPE_BINARY_DOUBLE`,
1068 :data:`DB_TYPE_BINARY_FLOAT`, :data:`DB_TYPE_BINARY_INTEGER` and
1069 :data:`DB_TYPE_NUMBER` will all compare equal to this value. If a variable
1070 is created with this type, the database type :data:`DB_TYPE_NUMBER` will be
1071 used.
1072
1073
1074 .. data:: ROWID
1075
1076 This type object is used to describe the pseudo column "rowid". The
1077 database type :data:`DB_TYPE_ROWID` will compare equal to this value. If a
1078 variable is created with this type, the database type
1079 :data:`DB_TYPE_VARCHAR` will be used.
1080
1081
1082 .. data:: STRING
1083
1084 This type object is used to describe columns in a database that are
1085 strings. The database types :data:`DB_TYPE_CHAR`, :data:`DB_TYPE_LONG`,
1086 :data:`DB_TYPE_NCHAR`, :data:`DB_TYPE_NVARCHAR` and :data:`DB_TYPE_VARCHAR`
1087 will all compare equal to this value. If a variable is created with this
1088 type, the database type :data:`DB_TYPE_VARCHAR` will be used.
1089
1090
1091 .. _dbtypes:
1092
1093 Database Types
1094 --------------
1095
1096 All of these types are extensions to the DB API definition. They are found in
1097 query and object metadata. They can also be used to specify the database type
1098 when binding data.
1099
1100 .. data:: DB_TYPE_BFILE
1101
1102 Describes columns, attributes or array elements in a database that are of
1103 type BFILE. It will compare equal to the DB API type :data:`BINARY`.
1104
1105
1106 .. data:: DB_TYPE_BINARY_DOUBLE
1107
1108 Describes columns, attributes or array elements in a database that are of
1109 type BINARY_DOUBLE. It will compare equal to the DB API type
1110 :data:`NUMBER`.
1111
1112
1113 .. data:: DB_TYPE_BINARY_FLOAT
1114
1115 Describes columns, attributes or array elements in a database that are
1116 of type BINARY_FLOAT. It will compare equal to the DB API type
1117 :data:`NUMBER`.
1118
1119
1120 .. data:: DB_TYPE_BINARY_INTEGER
1121
1122 Describes attributes or array elements in a database that are of type
1123 BINARY_INTEGER. It will compare equal to the DB API type :data:`NUMBER`.
1124
1125
1126 .. data:: DB_TYPE_BLOB
1127
1128 Describes columns, attributes or array elements in a database that are of
1129 type BLOB. It will compare equal to the DB API type :data:`BINARY`.
1130
1131
1132 .. data:: DB_TYPE_BOOLEAN
1133
1134 Describes attributes or array elements in a database that are of type
1135 BOOLEAN. It is only available in Oracle 12.1 and higher and only within
1136 PL/SQL.
1137
1138
1139 .. data:: DB_TYPE_CHAR
1140
1141 Describes columns, attributes or array elements in a database that are of
1142 type CHAR. It will compare equal to the DB API type :data:`STRING`.
1143
1144 Note that these are fixed length string values and behave differently from
1145 VARCHAR2.
1146
1147
1148 .. data:: DB_TYPE_CLOB
1149
1150 Describes columns, attributes or array elements in a database that are of
1151 type CLOB. It will compare equal to the DB API type :data:`STRING`.
1152
1153
1154 .. data:: DB_TYPE_CURSOR
1155
1156 Describes columns in a database that are of type CURSOR. In PL/SQL these
1157 are knoown as REF CURSOR.
1158
1159
1160 .. data:: DB_TYPE_DATE
1161
1162 Describes columns, attributes or array elements in a database that are of
1163 type DATE. It will compare equal to the DB API type :data:`DATETIME`.
1164
1165
1166 .. data:: DB_TYPE_INTERVAL_DS
1167
1168 Describes columns, attributes or array elements in a database that are of
1169 type INTERVAL DAY TO SECOND.
1170
1171
1172 .. data:: DB_TYPE_INTERVAL_YM
1173
1174 Describes columns, attributes or array elements in a database that are of
1175 type INTERVAL YEAR TO MONTH. This database type is not currently supported
1176 by cx_Oracle.
1177
1178
1179 .. data:: DB_TYPE_JSON
1180
1181 Describes columns in a database that are of type JSON (with Oracle Database
1182 21 or later).
1183
1184 .. versionadded:: 8.1
1185
1186
1187 .. data:: DB_TYPE_LONG
1188
1189 Describes columns, attributes or array elements in a database that are of
1190 type LONG. It will compare equal to the DB API type :data:`STRING`.
1191
1192
1193 .. data:: DB_TYPE_LONG_RAW
1194
1195 Describes columns, attributes or array elements in a database that are of
1196 type LONG RAW. It will compare equal to the DB API type :data:`BINARY`.
1197
1198
1199 .. data:: DB_TYPE_NCHAR
1200
1201 Describes columns, attributes or array elements in a database that are of
1202 type NCHAR. It will compare equal to the DB API type :data:`STRING`.
1203
1204 Note that these are fixed length string values and behave differently from
1205 NVARCHAR2.
1206
1207
1208 .. data:: DB_TYPE_NCLOB
1209
1210 Describes columns, attributes or array elements in a database that are of
1211 type NCLOB. It will compare equal to the DB API type :data:`STRING`.
1212
1213
1214 .. data:: DB_TYPE_NUMBER
1215
1216 Describes columns, attributes or array elements in a database that are of
1217 type NUMBER. It will compare equal to the DB API type :data:`NUMBER`.
1218
1219
1220 .. data:: DB_TYPE_NVARCHAR
1221
1222 Describes columns, attributes or array elements in a database that are of
1223 type NVARCHAR2. It will compare equal to the DB API type :data:`STRING`.
1224
1225
1226 .. data:: DB_TYPE_OBJECT
1227
1228 Describes columns, attributes or array elements in a database that are an
1229 instance of a named SQL or PL/SQL type.
1230
1231
1232 .. data:: DB_TYPE_RAW
1233
1234 Describes columns, attributes or array elements in a database that are of
1235 type RAW. It will compare equal to the DB API type :data:`BINARY`.
1236
1237
1238 .. data:: DB_TYPE_ROWID
1239
1240 Describes columns, attributes or array elements in a database that are of
1241 type ROWID or UROWID. It will compare equal to the DB API type
1242 :data:`ROWID`.
1243
1244
1245 .. data:: DB_TYPE_TIMESTAMP
1246
1247 Describes columns, attributes or array elements in a database that are of
1248 type TIMESTAMP. It will compare equal to the DB API type :data:`DATETIME`.
1249
1250
1251 .. data:: DB_TYPE_TIMESTAMP_LTZ
1252
1253 Describes columns, attributes or array elements in a database that are of
1254 type TIMESTAMP WITH LOCAL TIME ZONE. It will compare equal to the DB API
1255 type :data:`DATETIME`.
1256
1257
1258 .. data:: DB_TYPE_TIMESTAMP_TZ
1259
1260 Describes columns, attributes or array elements in a database that are of
1261 type TIMESTAMP WITH TIME ZONE. It will compare equal to the DB API type
1262 :data:`DATETIME`.
1263
1264
1265 .. data:: DB_TYPE_VARCHAR
1266
1267 Describes columns, attributes or array elements in a database that are of
1268 type VARCHAR2. It will compare equal to the DB API type :data:`STRING`.
1269
1270
1271 .. _dbtypesynonyms:
1272
1273 Database Type Synonyms
1274 ----------------------
1275
1276 All of the following constants are deprecated and will be removed in a future
1277 version of cx_Oracle.
1278
1279 .. data:: BFILE
1280
1281 A synonym for :data:`DB_TYPE_BFILE`.
1282
1283 .. deprecated:: 8.0
1284
1285
1286 .. data:: BLOB
1287
1288 A synonym for :data:`DB_TYPE_BLOB`.
1289
1290 .. deprecated:: 8.0
1291
1292
1293 .. data:: BOOLEAN
1294
1295 A synonym for :data:`DB_TYPE_BOOLEAN`.
1296
1297 .. deprecated:: 8.0
1298
1299
1300 .. data:: CLOB
1301
1302 A synonym for :data:`DB_TYPE_CLOB`.
1303
1304 .. deprecated:: 8.0
1305
1306 .. data:: CURSOR
1307
1308 A synonym for :data:`DB_TYPE_CURSOR`.
1309
1310 .. deprecated:: 8.0
1311
1312
1313 .. data:: FIXED_CHAR
1314
1315 A synonym for :data:`DB_TYPE_CHAR`.
1316
1317 .. deprecated:: 8.0
1318
1319
1320 .. data:: FIXED_NCHAR
1321
1322 A synonym for :data:`DB_TYPE_NCHAR`.
1323
1324 .. deprecated:: 8.0
1325
1326
1327 .. data:: INTERVAL
1328
1329 A synonym for :data:`DB_TYPE_INTERVAL_DS`.
1330
1331 .. deprecated:: 8.0
1332
1333
1334 .. data:: LONG_BINARY
1335
1336 A synonym for :data:`DB_TYPE_LONG_RAW`.
1337
1338 .. deprecated:: 8.0
1339
1340
1341 .. data:: LONG_STRING
1342
1343 A synonym for :data:`DB_TYPE_LONG`.
1344
1345 .. deprecated:: 8.0
1346
1347
1348 .. data:: NATIVE_FLOAT
1349
1350 A synonym for :data:`DB_TYPE_BINARY_DOUBLE`.
1351
1352 .. deprecated:: 8.0
1353
1354
1355 .. data:: NATIVE_INT
1356
1357 A synonym for :data:`DB_TYPE_BINARY_INTEGER`.
1358
1359 .. deprecated:: 8.0
1360
1361
1362 .. data:: NCHAR
1363
1364 A synonym for :data:`DB_TYPE_NVARCHAR`.
1365
1366 .. deprecated:: 8.0
1367
1368
1369 .. data:: NCLOB
1370
1371 A synonym for :data:`DB_TYPE_NCLOB`.
1372
1373 .. deprecated:: 8.0
1374
1375
1376 .. data:: OBJECT
1377
1378 A synonym for :data:`DB_TYPE_OBJECT`.
1379
1380 .. deprecated:: 8.0
1381
1382
1383 .. data:: TIMESTAMP
1384
1385 A synonym for :data:`DB_TYPE_TIMESTAMP`.
1386
1387 .. deprecated:: 8.0
1388
1389
1390 Other Types
1391 -----------
1392
1393 All of these types are extensions to the DB API definition.
1394
1395 .. data:: ApiType
1396
1397 This type object is the Python type of the database API type constants
1398 :data:`BINARY`, :data:`DATETIME`, :data:`NUMBER`, :data:`ROWID` and
1399 :data:`STRING`.
1400
1401
1402 .. data:: DbType
1403
1404 This type object is the Python type of the
1405 :ref:`database type constants <dbtypes>`.
1406
1407
1408 .. data:: LOB
1409
1410 This type object is the Python type of :data:`DB_TYPE_BLOB`,
1411 :data:`DB_TYPE_BFILE`, :data:`DB_TYPE_CLOB` and :data:`DB_TYPE_NCLOB` data
1412 that is returned from cursors.
1413
1414
1415 .. _exceptions:
1416
1417 Exceptions
1418 ==========
1419
1420 .. exception:: Warning
1421
1422 Exception raised for important warnings and defined by the DB API but not
1423 actually used by cx_Oracle.
1424
1425
1426 .. exception:: Error
1427
1428 Exception that is the base class of all other exceptions defined by
1429 cx_Oracle and is a subclass of the Python StandardError exception (defined
1430 in the module exceptions).
1431
1432
1433 .. exception:: InterfaceError
1434
1435 Exception raised for errors that are related to the database interface
1436 rather than the database itself. It is a subclass of Error.
1437
1438
1439 .. exception:: DatabaseError
1440
1441 Exception raised for errors that are related to the database. It is a
1442 subclass of Error.
1443
1444
1445 .. exception:: DataError
1446
1447 Exception raised for errors that are due to problems with the processed
1448 data. It is a subclass of DatabaseError.
1449
1450
1451 .. exception:: OperationalError
1452
1453 Exception raised for errors that are related to the operation of the
1454 database but are not necessarily under the control of the programmer. It is
1455 a subclass of DatabaseError.
1456
1457
1458 .. exception:: IntegrityError
1459
1460 Exception raised when the relational integrity of the database is affected.
1461 It is a subclass of DatabaseError.
1462
1463
1464 .. exception:: InternalError
1465
1466 Exception raised when the database encounters an internal error. It is a
1467 subclass of DatabaseError.
1468
1469
1470 .. exception:: ProgrammingError
1471
1472 Exception raised for programming errors. It is a subclass of DatabaseError.
1473
1474
1475 .. exception:: NotSupportedError
1476
1477 Exception raised when a method or database API was used which is not
1478 supported by the database. It is a subclass of DatabaseError.
1479
1480
1481 .. _exchandling:
1482
1483 Exception handling
1484 ==================
1485
1486 .. note::
1487
1488 PEP 249 (Python Database API Specification v2.0) says the following about
1489 exception values:
1490
1491 [...] The values of these exceptions are not defined. They should
1492 give the user a fairly good idea of what went wrong, though. [...]
1493
1494 With cx_Oracle every exception object has exactly one argument in the
1495 ``args`` tuple. This argument is a ``cx_Oracle._Error`` object which has
1496 the following five read-only attributes.
1497
1498 .. attribute:: _Error.code
1499
1500 Integer attribute representing the Oracle error number (ORA-XXXXX).
1501
1502 .. attribute:: _Error.offset
1503
1504 Integer attribute representing the error offset when applicable.
1505
1506 .. attribute:: _Error.message
1507
1508 String attribute representing the Oracle message of the error. This
1509 message is localized by the environment of the Oracle connection.
1510
1511 .. attribute:: _Error.context
1512
1513 String attribute representing the context in which the exception was
1514 raised.
1515
1516 .. attribute:: _Error.isrecoverable
1517
1518 Boolean attribute representing whether the error is recoverable or not.
1519 This is False in all cases unless both Oracle Database 12.1 (or later) and
1520 Oracle Client 12.1 (or later) are being used.
1521
1522 .. versionadded:: 5.3
1523
1524
1525 This allows you to use the exceptions for example in the following way:
1526
1527 ::
1528
1529 import cx_Oracle
1530
1531 connection = cx_Oracle.connect("cx_Oracle/dev@localhost/orclpdb1")
1532 cursor = connection.cursor()
1533
1534 try:
1535 cursor.execute("select 1 / 0 from dual")
1536 except cx_Oracle.DatabaseError as exc:
1537 error, = exc.args
1538 print("Oracle-Error-Code:", error.code)
1539 print("Oracle-Error-Message:", error.message)
0 .. _objecttype:
1
2 *******************
3 Object Type Objects
4 *******************
5
6 .. note::
7
8 This object is an extension to the DB API. It is returned by the
9 :meth:`Connection.gettype()` call and is available as the
10 :data:`Variable.type` for variables containing Oracle objects.
11
12
13 .. method:: ObjectType([sequence])
14
15 The object type may be called directly and serves as an alternative way of
16 calling :meth:`~ObjectType.newobject()`.
17
18
19 .. attribute:: ObjectType.attributes
20
21 This read-only attribute returns a list of the :ref:`attributes
22 <objectattr>` that make up the object type.
23
24
25 .. attribute:: ObjectType.iscollection
26
27 This read-only attribute returns a boolean indicating if the object type
28 refers to a collection or not.
29
30
31 .. attribute:: ObjectType.name
32
33 This read-only attribute returns the name of the type.
34
35
36 .. attribute:: ObjectType.element_type
37
38 This read-only attribute returns the type of elements found in collections
39 of this type, if :attr:`~ObjectType.iscollection` is ``True``; otherwise,
40 it returns ``None``. If the collection contains objects, this will be
41 another object type; otherwise, it will be one of the
42 :ref:`database type constants <dbtypes>`.
43
44 .. versionadded:: 8.0
45
46
47 .. method:: ObjectType.newobject([sequence])
48
49 Return a new Oracle object of the given type. This object can then be
50 modified by setting its attributes and then bound to a cursor for
51 interaction with Oracle. If the object type refers to a collection, a
52 sequence may be passed and the collection will be initialized with the
53 items in that sequence.
54
55
56 .. attribute:: ObjectType.schema
57
58 This read-only attribute returns the name of the schema that owns the type.
59
60
61 Object Objects
62 --------------
63
64 .. note::
65
66 This object is an extension to the DB API. It is returned by the
67 :meth:`ObjectType.newobject()` call and can be bound to variables of
68 type :data:`~cx_Oracle.OBJECT`. Attributes can be retrieved and set
69 directly.
70
71 .. method:: Object.append(element)
72
73 Append an element to the collection object. If no elements exist in the
74 collection, this creates an element at index 0; otherwise, it creates an
75 element immediately following the highest index available in the
76 collection.
77
78
79 .. method:: Object.asdict()
80
81 Return a dictionary where the collection's indexes are the keys and the
82 elements are its values.
83
84 .. versionadded:: 7.0
85
86
87 .. method:: Object.aslist()
88
89 Return a list of each of the collection's elements in index order.
90
91
92 .. method:: Object.copy()
93
94 Create a copy of the object and return it.
95
96
97 .. method:: Object.delete(index)
98
99 Delete the element at the specified index of the collection. If the
100 element does not exist or is otherwise invalid, an error is raised. Note
101 that the indices of the remaining elements in the collection are not
102 changed. In other words, the delete operation creates holes in the
103 collection.
104
105
106 .. method:: Object.exists(index)
107
108 Return True or False indicating if an element exists in the collection at
109 the specified index.
110
111
112 .. method:: Object.extend(sequence)
113
114 Append all of the elements in the sequence to the collection. This is
115 the equivalent of performing :meth:`~Object.append()` for each element
116 found in the sequence.
117
118
119 .. method:: Object.first()
120
121 Return the index of the first element in the collection. If the collection
122 is empty, None is returned.
123
124
125 .. method:: Object.getelement(index)
126
127 Return the element at the specified index of the collection. If no element
128 exists at that index, an exception is raised.
129
130
131 .. method:: Object.last()
132
133 Return the index of the last element in the collection. If the collection
134 is empty, None is returned.
135
136
137 .. method:: Object.next(index)
138
139 Return the index of the next element in the collection following the
140 specified index. If there are no elements in the collection following the
141 specified index, None is returned.
142
143
144 .. method:: Object.prev(index)
145
146 Return the index of the element in the collection preceding the specified
147 index. If there are no elements in the collection preceding the
148 specified index, None is returned.
149
150
151 .. method:: Object.setelement(index, value)
152
153 Set the value in the collection at the specified index to the given value.
154
155
156 .. method:: Object.size()
157
158 Return the number of elements in the collection.
159
160
161 .. method:: Object.trim(num)
162
163 Remove the specified number of elements from the end of the collection.
164
165
166 .. _objectattr:
167
168 Object Attribute Objects
169 ------------------------
170
171 .. note::
172
173 This object is an extension to the DB API. The elements of
174 :attr:`ObjectType.attributes` are instances of this type.
175
176
177 .. attribute:: ObjectAttribute.name
178
179 This read-only attribute returns the name of the attribute.
180
181
182 .. attribute:: ObjectAttribute.type
183
184 This read-only attribute returns the type of the attribute. This will be an
185 :ref:`Oracle Object Type <objecttype>` if the variable binds
186 Oracle objects; otherwise, it will be one of the
187 :ref:`database type constants <dbtypes>`.
188
189 .. versionadded:: 8.0
0 .. _sesspool:
1
2 ******************
3 SessionPool Object
4 ******************
5
6 .. note::
7
8 This object is an extension to the DB API.
9
10 Connection pooling in cx_Oracle is handled by SessionPool objects.
11
12 See :ref:`connpool` for information on connection pooling.
13
14
15 .. method:: SessionPool.acquire(user=None, password=None, cclass=None, \
16 purity=cx_Oracle.ATTR_PURITY_DEFAULT, tag=None, matchanytag=False, \
17 shardingkey=[], supershardingkey=[])
18
19 Acquire a connection from the session pool and return a
20 :ref:`connection object <connobj>`.
21
22 If the pool is homogeneous, the user and password parameters cannot be
23 specified. If they are, an exception will be raised.
24
25 The cclass parameter, if specified, should be a string corresponding to the
26 connection class for database resident connection pooling (DRCP).
27
28 The purity parameter is expected to be one of
29 :data:`~cx_Oracle.ATTR_PURITY_NEW`, :data:`~cx_Oracle.ATTR_PURITY_SELF`, or
30 :data:`~cx_Oracle.ATTR_PURITY_DEFAULT`.
31
32 The tag parameter, if specified, is expected to be a string with name=value
33 pairs like "k1=v1;k2=v2" and will limit the sessions that can be returned
34 from a session pool unless the matchanytag parameter is set to True. In
35 that case sessions with the specified tag will be preferred over others,
36 but if no such sessions are available a session with a different tag may be
37 returned instead. In any case, untagged sessions will always be returned if
38 no sessions with the specified tag are available. Sessions are tagged when
39 they are :meth:`released <SessionPool.release>` back to the pool.
40
41 The shardingkey and supershardingkey parameters, if specified, are expected
42 to be a sequence of values which will be used to identify the database
43 shard to connect to. The key values can be strings, numbers, bytes or dates.
44
45 .. attribute:: SessionPool.busy
46
47 This read-only attribute returns the number of sessions currently acquired.
48
49
50 .. method:: SessionPool.close(force=False)
51
52 Close the session pool now, rather than when the last reference to it is
53 released, which makes it unusable for further work.
54
55 If any connections have been acquired and not released back to the pool
56 this method will fail unless the force parameter is set to True.
57
58
59 .. method:: SessionPool.drop(connection)
60
61 Drop the connection from the pool which is useful if the connection is no
62 longer usable (such as when the session is killed).
63
64
65 .. attribute:: SessionPool.dsn
66
67 This read-only attribute returns the TNS entry of the database to which a
68 connection has been established.
69
70
71 .. attribute:: SessionPool.homogeneous
72
73 This read-write boolean attribute indicates whether the pool is considered
74 homogeneous or not. If the pool is not homogeneous different authentication
75 can be used for each connection acquired from the pool.
76
77
78 .. attribute:: SessionPool.increment
79
80 This read-only attribute returns the number of sessions that will be
81 established when additional sessions need to be created.
82
83
84 .. attribute:: SessionPool.max
85
86 This read-only attribute returns the maximum number of sessions that the
87 session pool can control.
88
89
90 .. attribute:: SessionPool.max_lifetime_session
91
92 This read-write attribute returns the maximum length of time (in seconds)
93 that a pooled session may exist. Sessions that are in use will not be
94 closed. They become candidates for termination only when they are released
95 back to the pool and have existed for longer than max_lifetime_session
96 seconds. Note that termination only occurs when the pool is accessed. A
97 value of 0 means that there is no maximum length of time that a pooled
98 session may exist. This attribute is only available in Oracle Database
99 12.1.
100
101
102
103 .. versionadded:: 5.3
104
105
106 .. attribute:: SessionPool.min
107
108 This read-only attribute returns the number of sessions with which the
109 session pool was created and the minimum number of sessions that will be
110 controlled by the session pool.
111
112
113 .. attribute:: SessionPool.name
114
115 This read-only attribute returns the name assigned to the session pool by
116 Oracle.
117
118
119 .. attribute:: SessionPool.opened
120
121 This read-only attribute returns the number of sessions currently opened by
122 the session pool.
123
124
125 .. method:: SessionPool.release(connection, tag=None)
126
127 Release the connection back to the pool now, rather than whenever __del__
128 is called. The connection will be unusable from this point forward; an
129 Error exception will be raised if any operation is attempted with the
130 connection. Any cursors or LOBs created by the connection will also be
131 marked unusable and an Error exception will be raised if any operation is
132 attempted with them.
133
134 Internally, references to the connection are held by cursor objects,
135 LOB objects, etc. Once all of these references are released, the connection
136 itself will be released back to the pool automatically. Either control
137 references to these related objects carefully or explicitly release
138 connections back to the pool in order to ensure sufficient resources are
139 available.
140
141 If the tag is not None, it is expected to be a string with name=value pairs
142 like "k1=v1;k2=v2" and will override the value in the property
143 :attr:`Connection.tag`. If either :attr:`Connection.tag` or the tag
144 parameter are not None, the connection will be retagged when it is released
145 back to the pool.
146
147
148 .. attribute:: SessionPool.stmtcachesize
149
150 This read-write attribute specifies the size of the statement cache that
151 will be used as the starting point for any connections that are created by
152 the session pool. Once a connection is created, that connection's statement
153 cache size can only be changed by setting the
154 :attr:`Connection.stmtcachesize` attribute on the connection itself.
155
156 See :ref:`Statement Caching <stmtcache>` for more information.
157
158 .. versionadded:: 6.0
159
160
161 .. attribute:: SessionPool.timeout
162
163 This read-write attribute specifies the time (in seconds) after which idle
164 sessions will be terminated in order to maintain an optimum number of open
165 sessions. Note that termination only occurs when the pool is accessed. A
166 value of 0 means that no idle sessions are terminated.
167
168
169 .. attribute:: SessionPool.tnsentry
170
171 This read-only attribute returns the TNS entry of the database to which a
172 connection has been established.
173
174
175 .. attribute:: SessionPool.username
176
177 This read-only attribute returns the name of the user which established the
178 connection to the database.
179
180
181 .. attribute:: SessionPool.wait_timeout
182
183 This read-write attribute specifies the time (in milliseconds) that the
184 caller should wait for a session to become available in the pool before
185 returning with an error. This value is only used if the getmode parameter
186 to :meth:`cx_Oracle.SessionPool()` was the value
187 :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`.
188
189 .. versionadded:: 6.4
0 .. _soda:
1
2 ****
3 SODA
4 ****
5
6 `Oracle Database Simple Oracle Document Access (SODA)
7 <https://docs.oracle.com/en/database/oracle/simple-oracle-document-access>`__
8 allows documents to be inserted, queried, and retrieved from Oracle Database
9 using a set of NoSQL-style cx_Oracle methods. By default, documents are JSON
10 strings. See the :ref:`user manual <sodausermanual>` for examples.
11
12 .. _sodarequirements:
13
14 -----------------
15 SODA Requirements
16 -----------------
17
18 To use SODA, the role SODA_APP must be granted to the user. To create
19 collections, users need the CREATE TABLE privilege. These can be granted by a
20 DBA:
21
22 .. code-block:: sql
23
24 SQL> grant soda_app, create table to myuser;
25
26 Advanced users who are using Oracle sequences for keys will also need the CREATE
27 SEQUENCE privilege.
28
29 SODA requires Oracle Client 18.3 or higher and Oracle Database 18.1 and higher.
30
31 .. note::
32
33 If you are using Oracle Database 21c (or later) and create new collections
34 you need to do one of the following:
35
36 - Use Oracle Client libraries 21c (or later).
37
38 - Or, explicitly use collection metadata when creating collections and set
39 the data storage type to BLOB, for example::
40
41 {
42 "keyColumn": {
43 "name":"ID"
44 },
45 "contentColumn": {
46 "name": "JSON_DOCUMENT",
47 "sqlType": "BLOB"
48 },
49 "versionColumn": {
50 "name": "VERSION",
51 "method": "UUID"
52 },
53 "lastModifiedColumn": {
54 "name": "LAST_MODIFIED"
55 },
56 "creationTimeColumn": {
57 "name": "CREATED_ON"
58 }
59 }
60
61 - Or, set the database initialization parameter `compatible
62 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
63 id=GUID-A2E90F08-BC9F-4688-A9D0-4A948DD3F7A9>`__ to 19 or lower.
64
65 Otherwise you may get errors such as "ORA-40842: unsupported value JSON in
66 the metadata for the field sqlType" or "ORA-40659: Data type does not match
67 the specification in the collection metadata".
68
69 .. _sodadb:
70
71 --------------------
72 SODA Database Object
73 --------------------
74
75 .. note::
76
77 This object is an extension the DB API. It is returned by the method
78 :meth:`Connection.getSodaDatabase()`.
79
80
81 .. method:: SodaDatabase.createCollection(name, metadata=None, mapMode=False)
82
83 Creates a SODA collection with the given name and returns a new
84 :ref:`SODA collection object <sodacoll>`. If you try to create a
85 collection, and a collection with the same name and metadata already
86 exists, then that existing collection is opened without error.
87
88 If metadata is specified, it is expected to be a string containing valid
89 JSON or a dictionary that will be transformed into a JSON string. This JSON
90 permits you to specify the configuration of the collection including
91 storage options; specifying the presence or absence of columns for creation
92 timestamp, last modified timestamp and version; whether the collection can
93 store only JSON documents; and methods of key and version generation. The
94 default metadata creates a collection that only supports JSON documents and
95 uses system generated keys. See this `collection metadata reference
96 <https://www.oracle.com/pls/topic/
97 lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
98 for more information.
99
100 If the mapMode parameter is set to True, the new collection is mapped to an
101 existing table instead of creating a table. If a collection is created in
102 this way, dropping the collection will not drop the existing table either.
103
104 .. versionadded:: 7.0
105
106
107 .. method:: SodaDatabase.createDocument(content, key=None, mediaType="application/json")
108
109 Creates a :ref:`SODA document <sodadoc>` usable for SODA write operations.
110 You only need to use this method if your collection requires
111 client-assigned keys or has non-JSON content; otherwise, you can pass your
112 content directly to SODA write operations. SodaDocument attributes
113 'createdOn', 'lastModified' and 'version' will be None.
114
115 The content parameter can be a dictionary or list which will be transformed
116 into a JSON string and then UTF-8 encoded. It can also be a string which
117 will be UTF-8 encoded or it can be a bytes object which will be stored
118 unchanged. If a bytes object is provided and the content is expected to be
119 JSON, note that SODA only supports UTF-8, UTF-16LE and UTF-16BE encodings.
120
121 The key parameter should only be supplied if the collection in which the
122 document is to be placed requires client-assigned keys.
123
124 The mediaType parameter should only be supplied if the collection in which
125 the document is to be placed supports non-JSON documents and the content
126 for this document is non-JSON. Using a standard MIME type for this value is
127 recommended but any string will be accepted.
128
129 .. versionadded:: 7.0
130
131
132 .. method:: SodaDatabase.getCollectionNames(startName=None, limit=0)
133
134 Returns a list of the names of collections in the database that match the
135 criteria, in alphabetical order.
136
137 If the startName parameter is specified, the list of names returned will
138 start with this value and also contain any names that fall after this value
139 in alphabetical order.
140
141 If the limit parameter is specified and is non-zero, the number of
142 collection names returned will be limited to this value.
143
144 .. versionadded:: 7.0
145
146
147 .. method:: SodaDatabase.openCollection(name)
148
149 Opens an existing collection with the given name and returns a new
150 :ref:`SODA collection object <sodacoll>`. If a collection with that name
151 does not exist, None is returned.
152
153 .. versionadded:: 7.0
154
155
156 .. _sodacoll:
157
158 ----------------------
159 SODA Collection Object
160 ----------------------
161
162 .. note::
163
164 This object is an extension the DB API. It is used to represent SODA
165 collections and is created by methods
166 :meth:`SodaDatabase.createCollection()` and
167 :meth:`SodaDatabase.openCollection()`.
168
169
170 .. method:: SodaCollection.createIndex(spec)
171
172 Creates an index on a SODA collection. The spec is expected to be a
173 dictionary or a JSON-encoded string. See this `overview
174 <https://www.oracle.com/pls/topic/
175 lookup?ctx=dblatest&id=GUID-4848E6A0-58A7-44FD-8D6D-A033D0CCF9CB>`__
176 for information on indexes in SODA.
177
178 Note that a commit should be performed before attempting to create an
179 index.
180
181 .. versionadded:: 7.0
182
183
184 .. method:: SodaCollection.drop()
185
186 Drops the collection from the database, if it exists. Note that if the
187 collection was created with mapMode set to True the underlying table will
188 not be dropped.
189
190 A boolean value is returned indicating if the collection was actually
191 dropped.
192
193 .. versionadded:: 7.0
194
195
196 .. method:: SodaCollection.dropIndex(name, force=False)
197
198 Drops the index with the specified name, if it exists.
199
200 The force parameter, if set to True, can be used to force the dropping of
201 an index that the underlying Oracle Database domain index doesn't normally
202 permit. This is only applicable to spatial and JSON search indexes.
203 See `here <https://www.oracle.com/pls/topic/
204 lookup?ctx=dblatest&id=GUID-F60F75DF-2866-4F93-BB7F-8FCE64BF67B6>`__
205 for more information.
206
207 A boolean value is returned indicating if the index was actually dropped.
208
209 .. versionadded:: 7.0
210
211
212 .. method:: SodaCollection.find()
213
214 This method is used to begin an operation that will act upon documents in
215 the collection. It creates and returns a
216 :ref:`SodaOperation object <sodaop>` which is used to specify the criteria
217 and the operation that will be performed on the documents that match that
218 criteria.
219
220 .. versionadded:: 7.0
221
222
223 .. method:: SodaCollection.getDataGuide()
224
225 Returns a :ref:`SODA document object <sodadoc>` containing property names,
226 data types and lengths inferred from the JSON documents in the collection.
227 It can be useful for exploring the schema of a collection. Note that this
228 method is only supported for JSON-only collections where a JSON search
229 index has been created with the 'dataguide' option enabled. If there are
230 no documents in the collection, None is returned.
231
232 .. versionadded:: 7.0
233
234
235 .. method:: SodaCollection.insertMany(docs)
236
237 Inserts a list of documents into the collection at one time. Each of the
238 input documents can be a dictionary or list or an existing :ref:`SODA
239 document object <sodadoc>`.
240
241 .. note::
242
243 This method requires Oracle Client 18.5 and higher and is available
244 only as a preview.
245
246 .. versionadded:: 7.2
247
248
249 .. method:: SodaCollection.insertManyAndGet(docs)
250
251 Similarly to :meth:`~SodaCollection.insertMany()` this method inserts a
252 list of documents into the collection at one time. The only difference is
253 that it returns a list of :ref:`SODA Document objects <sodadoc>`. Note that
254 for performance reasons the returned documents do not contain the content.
255
256 .. note::
257
258 This method requires Oracle Client 18.5 and higher.
259
260 .. versionadded:: 7.2
261
262
263 .. method:: SodaCollection.insertOne(doc)
264
265 Inserts a given document into the collection. The input document can be a
266 dictionary or list or an existing :ref:`SODA document object <sodadoc>`.
267
268 .. versionadded:: 7.0
269
270
271 .. method:: SodaCollection.insertOneAndGet(doc)
272
273 Similarly to :meth:`~SodaCollection.insertOne()` this method inserts a
274 given document into the collection. The only difference is that it
275 returns a :ref:`SODA Document object <sodadoc>`. Note that for performance
276 reasons the returned document does not contain the content.
277
278 .. versionadded:: 7.0
279
280
281 .. attribute:: SodaCollection.metadata
282
283 This read-only attribute returns a dictionary containing the metadata that
284 was used to create the collection. See this `collection metadata reference
285 <https://www.oracle.com/pls/topic/
286 lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
287 for more information.
288
289 .. versionadded:: 7.0
290
291
292 .. attribute:: SodaCollection.name
293
294 This read-only attribute returns the name of the collection.
295
296 .. versionadded:: 7.0
297
298
299 .. method:: SodaCollection.save(doc)
300
301 Saves a document into the collection. This method is equivalent to
302 :meth:`~SodaCollection.insertOne()` except that if client-assigned keys are
303 used, and the document with the specified key already exists in the
304 collection, it will be replaced with the input document.
305
306 .. versionadded:: 8.0
307
308
309 .. method:: SodaCollection.saveAndGet(doc)
310
311 Saves a document into the collection. This method is equivalent to
312 :meth:`~SodaCollection.insertOneAndGet()` except that if client-assigned
313 keys are used, and the document with the specified key already exists in
314 the collection, it will be replaced with the input document.
315
316 .. versionadded:: 8.0
317
318
319 .. method:: SodaCollection.truncate()
320
321 Removes all of the documents in the collection, similarly to what is done
322 for rows in a table by the TRUNCATE TABLE statement.
323
324 .. versionadded:: 8.0
325
326
327 .. _sodadoc:
328
329 --------------------
330 SODA Document Object
331 --------------------
332
333 .. note::
334
335 This object is an extension the DB API. It is returned by the methods
336 :meth:`SodaDatabase.createDocument()`,
337 :meth:`SodaOperation.getDocuments()` and
338 :meth:`SodaOperation.getOne()` as well as by iterating over
339 :ref:`SODA document cursors <sodadoccur>`.
340
341
342 .. attribute:: SodaDoc.createdOn
343
344 This read-only attribute returns the creation time of the document in
345 `ISO 8601 <https://www.iso.org/iso-8601-date-and-time-format.html>`__
346 format. Documents created by :meth:`SodaDatabase.createDocument()` or
347 fetched from collections where this attribute is not stored will return
348 None.
349
350 .. versionadded:: 7.0
351
352
353 .. method:: SodaDoc.getContent()
354
355 Returns the content of the document as a dictionary or list. This method
356 assumes that the content is application/json and will raise an exception if
357 this is not the case. If there is no content, however, None will be
358 returned.
359
360 .. versionadded:: 7.0
361
362
363 .. method:: SodaDoc.getContentAsBytes()
364
365 Returns the content of the document as a bytes object. If there is no
366 content, however, None will be returned.
367
368 .. versionadded:: 7.0
369
370
371 .. method:: SodaDoc.getContentAsString()
372
373 Returns the content of the document as a string. If the document encoding
374 is not known, UTF-8 will be used. If there is no content, however, None
375 will be returned.
376
377 .. versionadded:: 7.0
378
379
380 .. attribute:: SodaDoc.key
381
382 This read-only attribute returns the unique key assigned to this document.
383 Documents created by :meth:`SodaDatabase.createDocument()` may not have a
384 value assigned to them and return None.
385
386 .. versionadded:: 7.0
387
388
389 .. attribute:: SodaDoc.lastModified
390
391 This read-only attribute returns the last modified time of the document in
392 `ISO 8601 <https://www.iso.org/iso-8601-date-and-time-format.html>`__
393 format. Documents created by :meth:`SodaDatabase.createDocument()` or
394 fetched from collections where this attribute is not stored will return
395 None.
396
397 .. versionadded:: 7.0
398
399
400 .. attribute:: SodaDoc.mediaType
401
402 This read-only attribute returns the media type assigned to the document.
403 By convention this is expected to be a MIME type but no checks are
404 performed on this value. If a value is not specified when calling
405 :meth:`SodaDatabase.createDocument()` or the document is fetched from a
406 collection where this component is not stored, the string
407 "application/json" is returned.
408
409 .. versionadded:: 7.0
410
411
412 .. attribute:: SodaDoc.version
413
414 This read-only attribute returns the version assigned to this document.
415 Documents created by :meth:`SodaDatabase.createDocument()` or fetched
416 from collections where this attribute is not stored will return None.
417
418 .. versionadded:: 7.0
419
420
421 .. _sodadoccur:
422
423 ---------------------------
424 SODA Document Cursor Object
425 ---------------------------
426
427 .. note::
428
429 This object is an extension the DB API. It is returned by the method
430 :meth:`SodaOperation.getCursor()` and implements the iterator protocol.
431 Each iteration will return a :ref:`SODA document object <sodadoc>`.
432
433
434 .. method:: SodaDocCursor.close()
435
436 Close the cursor now, rather than whenever __del__ is called. The cursor
437 will be unusable from this point forward; an Error exception will be raised
438 if any operation is attempted with the cursor.
439
440 .. versionadded:: 7.0
441
442
443 .. _sodaop:
444
445 ---------------------
446 SODA Operation Object
447 ---------------------
448
449 .. note::
450
451 This object is an extension to the DB API. It represents an operation that
452 will be performed on all or some of the documents in a SODA collection. It
453 is created by the method :meth:`SodaCollection.find()`.
454
455
456 .. method:: SodaOperation.count()
457
458 Returns a count of the number of documents in the collection that match
459 the criteria. If :meth:`~SodaOperation.skip()` or
460 :meth:`~SodaOperation.limit()` were called on this object, an exception is
461 raised.
462
463 .. versionadded:: 7.0
464
465
466 .. method:: SodaOperation.fetchArraySize(value)
467
468 This is a tuning method to specify the number of documents that are
469 internally fetched in batches by calls to :meth:`~SodaOperation.getCursor()`
470 and :meth:`~SodaOperation.getDocuments()`. It does not affect how many
471 documents are returned to the application. A value of 0 will use the default
472 value (100). This method is only available in Oracle Client 19.5 and higher.
473
474 As a convenience, the SodaOperation object is returned so that further
475 criteria can be specified by chaining methods together.
476
477 .. versionadded:: 8.0
478
479
480 .. method:: SodaOperation.filter(value)
481
482 Sets a filter specification for complex document queries and ordering of
483 JSON documents. Filter specifications must be provided as a dictionary or
484 JSON-encoded string and can include comparisons, regular expressions,
485 logical and spatial operators, among others. See the
486 `overview of SODA filter specifications
487 <https://www.oracle.com/pls/topic/
488 lookup?ctx=dblatest&id=GUID-CB09C4E3-BBB1-40DC-88A8-8417821B0FBE>`__
489 for more information.
490
491 As a convenience, the SodaOperation object is returned so that further
492 criteria can be specified by chaining methods together.
493
494 .. versionadded:: 7.0
495
496
497 .. method:: SodaOperation.getCursor()
498
499 Returns a :ref:`SODA Document Cursor object <sodadoccur>` that can be used
500 to iterate over the documents that match the criteria.
501
502 .. versionadded:: 7.0
503
504
505 .. method:: SodaOperation.getDocuments()
506
507 Returns a list of :ref:`SODA Document objects <sodadoc>` that match the
508 criteria.
509
510 .. versionadded:: 7.0
511
512
513 .. method:: SodaOperation.getOne()
514
515 Returns a single :ref:`SODA Document object <sodadoc>` that matches the
516 criteria. Note that if multiple documents match the criteria only the first
517 one is returned.
518
519 .. versionadded:: 7.0
520
521
522 .. method:: SodaOperation.key(value)
523
524 Specifies that the document with the specified key should be returned.
525 This causes any previous calls made to this method and
526 :meth:`~SodaOperation.keys()` to be ignored.
527
528 As a convenience, the SodaOperation object is returned so that further
529 criteria can be specified by chaining methods together.
530
531 .. versionadded:: 7.0
532
533
534 .. method:: SodaOperation.keys(seq)
535
536 Specifies that documents that match the keys found in the supplied sequence
537 should be returned. This causes any previous calls made to this method and
538 :meth:`~SodaOperation.key()` to be ignored.
539
540 As a convenience, the SodaOperation object is returned so that further
541 criteria can be specified by chaining methods together.
542
543 .. versionadded:: 7.0
544
545
546 .. method:: SodaOperation.limit(value)
547
548 Specifies that only the specified number of documents should be returned.
549 This method is only usable for read operations such as
550 :meth:`~SodaOperation.getCursor()` and
551 :meth:`~SodaOperation.getDocuments()`. For write operations, any value set
552 using this method is ignored.
553
554 As a convenience, the SodaOperation object is returned so that further
555 criteria can be specified by chaining methods together.
556
557 .. versionadded:: 7.0
558
559
560 .. method:: SodaOperation.remove()
561
562 Removes all of the documents in the collection that match the criteria. The
563 number of documents that have been removed is returned.
564
565 .. versionadded:: 7.0
566
567
568 .. method:: SodaOperation.replaceOne(doc)
569
570 Replaces a single document in the collection with the specified document.
571 The input document can be a dictionary or list or an existing
572 :ref:`SODA document object <sodadoc>`. A boolean indicating if a document
573 was replaced or not is returned.
574
575 Currently the method :meth:`~SodaOperation.key()` must be called before
576 this method can be called.
577
578 .. versionadded:: 7.0
579
580
581 .. method:: SodaOperation.replaceOneAndGet(doc)
582
583 Similarly to :meth:`~SodaOperation.replaceOne()`, this method replaces a
584 single document in the collection with the specified document. The only
585 difference is that it returns a :ref:`SODA document object <sodadoc>`.
586 Note that for performance reasons the returned document does not contain
587 the content.
588
589 .. versionadded:: 7.0
590
591
592 .. method:: SodaOperation.skip(value)
593
594 Specifies the number of documents that match the other criteria that will
595 be skipped. This method is only usable for read operations such as
596 :meth:`~SodaOperation.getCursor()` and
597 :meth:`~SodaOperation.getDocuments()`. For write operations, any value set
598 using this method is ignored.
599
600 As a convenience, the SodaOperation object is returned so that further
601 criteria can be specified by chaining methods together.
602
603 .. versionadded:: 7.0
604
605
606 .. method:: SodaOperation.version(value)
607
608 Specifies that documents with the specified version should be returned.
609 Typically this is used with :meth:`~SodaOperation.key()` to implement
610 optimistic locking, so that the write operation called later does not
611 affect a document that someone else has modified.
612
613 As a convenience, the SodaOperation object is returned so that further
614 criteria can be specified by chaining methods together.
615
616 .. versionadded:: 7.0
0 .. _subscrobj:
1
2 *******************
3 Subscription Object
4 *******************
5
6 .. note::
7
8 This object is an extension the DB API.
9
10
11 .. attribute:: Subscription.callback
12
13 This read-only attribute returns the callback that was registered when the
14 subscription was created.
15
16
17 .. attribute:: Subscription.connection
18
19 This read-only attribute returns the connection that was used to register
20 the subscription when it was created.
21
22
23 .. attribute:: Subscription.id
24
25 This read-only attribute returns the value of ``REGID`` found in the
26 database view ``USER_CHANGE_NOTIFICATION_REGS`` or the value of ``REG_ID``
27 found in the database view ``USER_SUBSCR_REGISTRATIONS``. For AQ
28 subscriptions, the value is 0.
29
30
31 .. attribute:: Subscription.ipAddress
32
33 This read-only attribute returns the IP address used for callback
34 notifications from the database server. If not set during construction,
35 this value is None.
36
37 .. versionadded:: 6.4
38
39
40 .. attribute:: Subscription.name
41
42 This read-only attribute returns the name used to register the subscription
43 when it was created.
44
45 .. versionadded:: 6.4
46
47
48 .. attribute:: Subscription.namespace
49
50 This read-only attribute returns the namespace used to register the
51 subscription when it was created.
52
53
54 .. attribute:: Subscription.operations
55
56 This read-only attribute returns the operations that will send
57 notifications for each table or query that is registered using this
58 subscription.
59
60
61 .. attribute:: Subscription.port
62
63 This read-only attribute returns the port used for callback notifications
64 from the database server. If not set during construction, this value is
65 zero.
66
67
68 .. attribute:: Subscription.protocol
69
70 This read-only attribute returns the protocol used to register the
71 subscription when it was created.
72
73
74 .. attribute:: Subscription.qos
75
76 This read-only attribute returns the quality of service flags used to
77 register the subscription when it was created.
78
79
80 .. method:: Subscription.registerquery(statement, [args])
81
82 Register the query for subsequent notification when tables referenced by
83 the query are changed. This behaves similarly to cursor.execute() but only
84 queries are permitted and the args parameter must be a sequence or
85 dictionary. If the qos parameter included the flag
86 cx_Oracle.SUBSCR_QOS_QUERY when the subscription was created, then the ID
87 for the registered query is returned; otherwise, None is returned.
88
89
90 .. attribute:: Subscription.timeout
91
92 This read-only attribute returns the timeout (in seconds) that was
93 specified when the subscription was created. A value of 0 indicates that
94 there is no timeout.
95
96
97 .. _msgobjects:
98
99 Message Objects
100 ---------------
101
102 .. note::
103
104 This object is created internally when notification is received and passed
105 to the callback procedure specified when a subscription is created.
106
107
108 .. attribute:: Message.consumerName
109
110 This read-only attribute returns the name of the consumer which generated
111 the notification. It will be populated if the subscription was created with
112 the namespace :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ` and the queue is a
113 multiple consumer queue.
114
115 .. versionadded:: 6.4
116
117
118 .. attribute:: Message.dbname
119
120 This read-only attribute returns the name of the database that generated
121 the notification.
122
123
124 .. attribute:: Message.queries
125
126 This read-only attribute returns a list of message query objects that give
127 information about query result sets changed for this notification. This
128 attribute will be None if the qos parameter did not include the flag
129 :data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created.
130
131
132 .. attribute:: Message.queueName
133
134 This read-only attribute returns the name of the queue which generated the
135 notification. It will only be populated if the subscription was created
136 with the namespace :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`.
137
138 .. versionadded:: 6.4
139
140
141 .. attribute:: Message.registered
142
143 This read-only attribute returns whether the subscription which generated
144 this notification is still registered with the database. The subscription
145 is automatically deregistered with the database when the subscription
146 timeout value is reached or when the first notification is sent (when the
147 quality of service flag :data:`cx_Oracle.SUBSCR_QOS_DEREG_NFY` is used).
148
149 .. versionadded:: 6.4
150
151
152 .. attribute:: Message.subscription
153
154 This read-only attribute returns the subscription object for which this
155 notification was generated.
156
157
158 .. attribute:: Message.tables
159
160 This read-only attribute returns a list of message table objects that give
161 information about the tables changed for this notification. This
162 attribute will be None if the qos parameter included the flag
163 :data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created.
164
165
166 .. attribute:: Message.txid
167
168 This read-only attribute returns the id of the transaction that generated
169 the notification.
170
171
172 .. attribute:: Message.type
173
174 This read-only attribute returns the type of message that has been sent.
175 See the constants section on event types for additional information.
176
177
178 Message Table Objects
179 ---------------------
180
181 .. note::
182
183 This object is created internally for each table changed when notification
184 is received and is found in the tables attribute of message objects, and
185 the tables attribute of message query objects.
186
187
188 .. attribute:: MessageTable.name
189
190 This read-only attribute returns the name of the table that was changed.
191
192
193 .. attribute:: MessageTable.operation
194
195 This read-only attribute returns the operation that took place on the table
196 that was changed.
197
198
199 .. attribute:: MessageTable.rows
200
201 This read-only attribute returns a list of message row objects that give
202 information about the rows changed on the table. This value is only filled
203 in if the qos parameter to the :meth:`Connection.subscribe()` method
204 included the flag :data:`~cx_Oracle.SUBSCR_QOS_ROWIDS`.
205
206
207 Message Row Objects
208 -------------------
209
210 .. note::
211
212 This object is created internally for each row changed on a table when
213 notification is received and is found in the rows attribute of message
214 table objects.
215
216
217 .. attribute:: MessageRow.operation
218
219 This read-only attribute returns the operation that took place on the row
220 that was changed.
221
222
223 .. attribute:: MessageRow.rowid
224
225 This read-only attribute returns the rowid of the row that was changed.
226
227
228 Message Query Objects
229 ---------------------
230
231 .. note::
232
233 This object is created internally for each query result set changed when
234 notification is received and is found in the queries attribute of message
235 objects.
236
237
238 .. attribute:: MessageQuery.id
239
240 This read-only attribute returns the query id of the query for which the
241 result set changed. The value will match the value returned by
242 Subscription.registerquery when the related query was registered.
243
244
245 .. attribute:: MessageQuery.operation
246
247 This read-only attribute returns the operation that took place on the query
248 result set that was changed. Valid values for this attribute are
249 :data:`~cx_Oracle.EVENT_DEREG` and :data:`~cx_Oracle.EVENT_QUERYCHANGE`.
250
251
252 .. attribute:: MessageQuery.tables
253
254 This read-only attribute returns a list of message table objects that give
255 information about the table changes that caused the query result set to
256 change for this notification.
0 .. _varobj:
1
2 ****************
3 Variable Objects
4 ****************
5
6 .. note::
7
8 The DB API definition does not define this object.
9
10
11 .. attribute:: Variable.actualElements
12
13 This read-only attribute returns the actual number of elements in the
14 variable. This corresponds to the number of elements in a PL/SQL index-by
15 table for variables that are created using the method
16 :func:`Cursor.arrayvar()`. For all other variables this value will be
17 identical to the attribute :attr:`~Variable.numElements`.
18
19 .. attribute:: Variable.bufferSize
20
21 This read-only attribute returns the size of the buffer allocated for each
22 element in bytes.
23
24
25 .. method:: Variable.getvalue([pos=0])
26
27 Return the value at the given position in the variable. For variables
28 created using the method :func:`Cursor.arrayvar()` the value returned will
29 be a list of each of the values in the PL/SQL index-by table. For variables
30 bound to DML returning statements, the value returned will also be a list
31 corresponding to the returned data for the given execution of the statement
32 (as identified by the pos parameter).
33
34
35 .. attribute:: Variable.inconverter
36
37 This read-write attribute specifies the method used to convert data from
38 Python to the Oracle database. The method signature is converter(value)
39 and the expected return value is the value to bind to the database. If this
40 attribute is None, the value is bound directly without any conversion.
41
42
43 .. attribute:: Variable.numElements
44
45 This read-only attribute returns the number of elements allocated in an
46 array, or the number of scalar items that can be fetched into the variable
47 or bound to the variable.
48
49
50 .. attribute:: Variable.outconverter
51
52 This read-write attribute specifies the method used to convert data from
53 the Oracle database to Python. The method signature is converter(value)
54 and the expected return value is the value to return to Python. If this
55 attribute is None, the value is returned directly without any conversion.
56
57
58 .. method:: Variable.setvalue(pos, value)
59
60 Set the value at the given position in the variable.
61
62
63 .. attribute:: Variable.size
64
65 This read-only attribute returns the size of the variable. For strings this
66 value is the size in characters. For all others, this is same value as the
67 attribute bufferSize.
68
69
70 .. attribute:: Variable.type
71
72 This read-only attribute returns the type of the variable. This will be an
73 :ref:`Oracle Object Type <objecttype>` if the variable binds
74 Oracle objects; otherwise, it will be one of the
75 :ref:`database type constants <dbtypes>`.
76
77 .. versionchanged:: 8.0
78 Database type constants are now used when the variable is not used for
79 binding Oracle objects.
80
81
82 .. attribute:: Variable.values
83
84 This read-only attribute returns a copy of the value of all actual
85 positions in the variable as a list. This is the equivalent of calling
86 :meth:`~Variable.getvalue()` for each valid position and the length will
87 correspond to the value of the :attr:`~Variable.actualElements` attribute.
+0
-221
doc/src/aq.rst less more
0 .. _aq:
1
2 ****************
3 Advanced Queuing
4 ****************
5
6 .. _deqoptions:
7
8 ---------------
9 Dequeue Options
10 ---------------
11
12 .. note::
13
14 This object is an extension to the DB API. It is returned by the
15 :meth:`Connection.deqoptions()` call and is used in calls to
16 :meth:`Connection.deq()`.
17
18
19 .. attribute:: DeqOptions.condition
20
21 This attribute specifies a boolean expression similar to the where clause
22 of a SQL query. The boolean expression can include conditions on message
23 properties, user data properties and PL/SQL or SQL functions. The default
24 is to have no condition specified.
25
26
27 .. attribute:: DeqOptions.consumername
28
29 This attribute specifies the name of the consumer. Only messages matching
30 the consumer name will be accessed. If the queue is not set up for multiple
31 consumers this attribute should not be set. The default is to have no
32 consumer name specified.
33
34
35 .. attribute:: DeqOptions.correlation
36
37 This attribute specifies the correlation identifier of the message to be
38 dequeued. Special pattern-matching characters, such as the percent sign (%)
39 and the underscore (_), can be used. If multiple messages satisfy the
40 pattern, the order of dequeuing is indeterminate. The default is to have no
41 correlation specified.
42
43
44 .. attribute:: DeqOptions.deliverymode
45
46 This write-only attribute specifies what types of messages should be
47 dequeued. It should be one of the values :data:`~cx_Oracle.MSG_PERSISTENT`
48 (default), :data:`~cx_Oracle.MSG_BUFFERED` or
49 :data:`~cx_Oracle.MSG_PERSISTENT_OR_BUFFERED`.
50
51
52 .. attribute:: DeqOptions.mode
53
54 This attribute specifies the locking behaviour associated with the dequeue
55 operation. It should be one of the values :data:`~cx_Oracle.DEQ_BROWSE`,
56 :data:`~cx_Oracle.DEQ_LOCKED`,
57 :data:`~cx_Oracle.DEQ_REMOVE` (default), or
58 :data:`~cx_Oracle.DEQ_REMOVE_NODATA`.
59
60
61 .. attribute:: DeqOptions.msgid
62
63 This attribute specifies the identifier of the message to be dequeued. The
64 default is to have no message identifier specified.
65
66
67 .. attribute:: DeqOptions.navigation
68
69 This attribute specifies the position of the message that is retrieved. It
70 should be one of the values :data:`~cx_Oracle.DEQ_FIRST_MSG`,
71 :data:`~cx_Oracle.DEQ_NEXT_MSG` (default), or
72 :data:`~cx_Oracle.DEQ_NEXT_TRANSACTION`.
73
74
75 .. attribute:: DeqOptions.transformation
76
77 This attribute specifies the name of the transformation that must be
78 applied after the message is dequeued from the database but before it is
79 returned to the calling application. The transformation must be created
80 using dbms_transform. The default is to have no transformation specified.
81
82
83 .. attribute:: DeqOptions.visibility
84
85 This attribute specifies the transactional behavior of the dequeue request.
86 It should be one of the values :data:`~cx_Oracle.DEQ_ON_COMMIT` (default)
87 or :data:`~cx_Oracle.DEQ_IMMEDIATE`. This attribute is ignored when using
88 the :data:`~cx_Oracle.DEQ_BROWSE` mode. Note the value of
89 :attr:`~Connection.autocommit` is always ignored.
90
91
92 .. attribute:: DeqOptions.wait
93
94 This attribute specifies the time to wait, in seconds, for a message
95 matching the search criteria to become available for dequeuing. One of the
96 values :data:`~cx_Oracle.DEQ_NO_WAIT` or
97 :data:`~cx_Oracle.DEQ_WAIT_FOREVER` can also be used. The default is
98 :data:`~cx_Oracle.DEQ_WAIT_FOREVER`.
99
100
101 .. _enqoptions:
102
103 ---------------
104 Enqueue Options
105 ---------------
106
107 .. note::
108
109 This object is an extension to the DB API. It is returned by the
110 :meth:`Connection.enqoptions()` call and is used in calls to
111 :meth:`Connection.enq()`.
112
113
114 .. attribute:: EnqOptions.deliverymode
115
116 This write-only attribute specifies what type of messages should be
117 enqueued. It should be one of the values :data:`~cx_Oracle.MSG_PERSISTENT`
118 (default) or :data:`~cx_Oracle.MSG_BUFFERED`.
119
120
121 .. attribute:: EnqOptions.transformation
122
123 This attribute specifies the name of the transformation that must be
124 applied before the message is enqueued into the database. The
125 transformation must be created using dbms_transform. The default is to have
126 no transformation specified.
127
128
129 .. attribute:: EnqOptions.visibility
130
131 This attribute specifies the transactional behavior of the enqueue request.
132 It should be one of the values :data:`~cx_Oracle.ENQ_ON_COMMIT` (default)
133 or :data:`~cx_Oracle.ENQ_IMMEDIATE`. Note the value of
134 :attr:`~Connection.autocommit` is ignored.
135
136
137 .. _msgproperties:
138
139 ------------------
140 Message Properties
141 ------------------
142
143 .. note::
144
145 This object is an extension to the DB API. It is returned by the
146 :meth:`Connection.msgproperties()` call and is used in calls to
147 :meth:`Connection.deq()` and :meth:`Connection.enq()`.
148
149
150 .. attribute:: MessageProperties.attempts
151
152 This read-only attribute specifies the number of attempts that have been
153 made to dequeue the message.
154
155
156 .. attribute:: MessageProperties.correlation
157
158 This attribute specifies the correlation used when the message was
159 enqueued.
160
161
162 .. attribute:: MessageProperties.delay
163
164 This attribute specifies the number of seconds to delay an enqueued
165 message. Any integer is acceptable but the constant
166 :data:`~cx_Oracle.MSG_NO_DELAY` can also be used indicating that the
167 message is available for immediate dequeuing.
168
169
170 .. attribute:: MessageProperties.deliverymode
171
172 This read-only attribute specifies the type of message that was dequeued.
173 It will be one of the values :data:`~cx_Oracle.MSG_PERSISTENT` or
174 :data:`~cx_Oracle.MSG_BUFFERED`.
175
176
177 .. attribute:: MessageProperties.enqtime
178
179 This read-only attribute specifies the time that the message was enqueued.
180
181
182 .. attribute:: MessageProperties.exceptionq
183
184 This attribute specifies the name of the queue to which the message is
185 moved if it cannot be processed successfully. Messages are moved if the
186 number of unsuccessful dequeue attempts has exceeded the maximum number of
187 retries or if the message has expired. All messages in the exception queue
188 are in the :data:`~cx_Oracle.MSG_EXPIRED` state. The default value is the
189 name of the exception queue associated with the queue table.
190
191
192 .. attribute:: MessageProperties.expiration
193
194 This attribute specifies, in seconds, how long the message is available for
195 dequeuing. This attribute is an offset from the delay attribute. Expiration
196 processing requires the queue monitor to be running. Any integer is
197 accepted but the constant :data:`~cx_Oracle.MSG_NO_EXPIRATION` can also be
198 used indicating that the message never expires.
199
200
201 .. attribute:: MessageProperties.msgid
202
203 This attribute specifies the id of the message in the last queue that
204 generated this message.
205
206
207 .. attribute:: MessageProperties.priority
208
209 This attribute specifies the priority of the message. A smaller number
210 indicates a higher priority. The priority can be any integer, including
211 negative numbers. The default value is zero.
212
213
214 .. attribute:: MessageProperties.state
215
216 This read-only attribute specifies the state of the message at the time of
217 the dequeue. It will be one of the values :data:`~cx_Oracle.MSG_WAITING`,
218 :data:`~cx_Oracle.MSG_READY`, :data:`~cx_Oracle.MSG_PROCESSED` or
219 :data:`~cx_Oracle.MSG_EXPIRED`.
220
3232
3333 # General substitutions.
3434 project = 'cx_Oracle'
35 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'
35 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'
3636 author = 'Oracle'
3737
3838 # The default replacements for |version| and |release|, also used in various
3939 # other places throughout the built documents.
4040 #
4141 # The short X.Y version.
42 version = '7.1'
42 version = '8.1'
4343 # The full version, including alpha/beta/rc tags.
44 release = '7.1.0'
44 release = '8.1.0'
4545
4646 # There are two options for replacing |today|: either, you set today to some
4747 # non-false value, then it is used:
107107 # Output file base name for HTML help builder.
108108 htmlhelp_basename = 'cx_Oracledoc'
109109
110 numfig = True
110111
111112 # Options for LaTeX output
112113 # ------------------------
+0
-652
doc/src/connection.rst less more
0 .. _connobj:
1
2 *****************
3 Connection Object
4 *****************
5
6 .. note::
7
8 Any outstanding changes will be rolled back when the connection object
9 is destroyed or closed.
10
11
12
13 .. method:: Connection.__enter__()
14
15 The entry point for the connection as a context manager. It returns itself.
16
17 .. note::
18
19 This method is an extension to the DB API definition.
20
21
22 .. method:: Connection.__exit__()
23
24 The exit point for the connection as a context manager. This will close
25 the connection and roll back any uncomitted transaction.
26
27 .. note::
28
29 This method is an extension to the DB API definition.
30
31
32 .. attribute:: Connection.action
33
34 This write-only attribute sets the action column in the v$session table. It
35 is a string attribute and cannot be set to None -- use the empty string
36 instead.
37
38 .. note::
39
40 This attribute is an extension to the DB API definition.
41
42
43 .. attribute:: Connection.autocommit
44
45 This read-write attribute determines whether autocommit mode is on or off.
46 When autocommit mode is on, all statements are committed as soon as they
47 have completed executing.
48
49 .. note::
50
51 This attribute is an extension to the DB API definition.
52
53
54 .. method:: Connection.begin([formatId, transactionId, branchId])
55
56 Explicitly begin a new transaction. Without parameters, this explicitly
57 begins a local transaction; otherwise, this explicitly begins a distributed
58 (global) transaction with the given parameters. See the Oracle
59 documentation for more details.
60
61 Note that in order to make use of global (distributed) transactions, the
62 :attr:`~Connection.internal_name` and :attr:`~Connection.external_name`
63 attributes must be set.
64
65 .. note::
66
67 This method is an extension to the DB API definition.
68
69
70 .. attribute:: Connection.callTimeout
71
72 This read-write attribute specifies the amount of time (in milliseconds)
73 that a single round-trip to the database may take before a timeout will
74 occur. A value of 0 means that no timeout will take place.
75
76 .. note::
77
78 This attribute is an extension to the DB API definition and is only
79 available in Oracle Client 18c and higher.
80
81
82 .. method:: Connection.cancel()
83
84 Cancel a long-running transaction.
85
86 .. note::
87
88 This method is an extension to the DB API definition.
89
90
91 .. method:: Connection.changepassword(oldpassword, newpassword)
92
93 Change the password of the logon.
94
95 .. note::
96
97 This method is an extension to the DB API definition.
98
99
100 .. attribute:: Connection.client_identifier
101
102 This write-only attribute sets the client_identifier column in the
103 v$session table.
104
105 .. note::
106
107 This attribute is an extension to the DB API definition.
108
109
110 .. attribute:: Connection.clientinfo
111
112 This write-only attribute sets the client_info column in the v$session
113 table.
114
115 .. note::
116
117 This attribute is an extension to the DB API definition.
118
119
120 .. method:: Connection.close()
121
122 Close the connection now, rather than whenever __del__ is called. The
123 connection will be unusable from this point forward; an Error exception
124 will be raised if any operation is attempted with the connection.
125
126 All open cursors and LOBs created by the connection will be closed and will
127 also no longer be usable.
128
129 Internally, references to the connection are held by cursor objects,
130 LOB objects, subscription objects, etc. Once all of these references are
131 released, the connection itself will be closed automatically. Either
132 control references to these related objects carefully or explicitly close
133 connections in order to ensure sufficient resources are available.
134
135
136 .. method:: Connection.commit()
137
138 Commit any pending transactions to the database.
139
140
141 .. method:: Connection.createlob(lobType)
142
143 Create and return a new temporary :ref:`LOB object <lobobj>` of the
144 specified type. The lobType parameter should be one of
145 :data:`cx_Oracle.CLOB`, :data:`cx_Oracle.BLOB` or :data:`cx_Oracle.NCLOB`.
146
147 .. versionadded:: 6.2
148
149 .. note::
150
151 This method is an extension to the DB API definition.
152
153
154 .. attribute:: Connection.current_schema
155
156 This read-write attribute sets the current schema attribute for the
157 session. Setting this value is the same as executing the SQL statement
158 "ALTER SESSION SET CURRENT_SCHEMA". The attribute is set (and verified) on
159 the next call that does a round trip to the server. The value is placed
160 before unqualified database objects in SQL statements you then execute.
161
162 .. note::
163
164 This attribute is an extension to the DB API definition.
165
166
167 .. method:: Connection.cursor()
168
169 Return a new :ref:`cursor object <cursorobj>` using the connection.
170
171
172 .. attribute:: Connection.dbop
173
174 This write-only attribute sets the database operation that is to be
175 monitored. This can be viewed in the DBOP_NAME column of the V$SQL_MONITOR
176 table.
177
178 .. note::
179
180 This attribute is an extension to the DB API definition.
181
182
183 .. method:: Connection.deq(name, options, msgproperties, payload)
184
185 Returns a message id after successfully dequeuing a message. The options
186 object can be created using :meth:`~Connection.deqoptions()` and the
187 msgproperties object can be created using
188 :meth:`~Connection.msgproperties()`. The payload must be an object created
189 using :meth:`ObjectType.newobject()`.
190
191 .. versionadded:: 5.3
192
193 .. note::
194
195 This method is an extension to the DB API definition.
196
197
198 .. method:: Connection.deqoptions()
199
200 Returns an object specifying the options to use when dequeuing messages.
201 See :ref:`deqoptions` for more information.
202
203 .. versionadded:: 5.3
204
205 .. note::
206
207 This method is an extension to the DB API definition.
208
209
210 .. attribute:: Connection.dsn
211
212 This read-only attribute returns the TNS entry of the database to which a
213 connection has been established.
214
215 .. note::
216
217 This attribute is an extension to the DB API definition.
218
219
220 .. attribute:: Connection.edition
221
222 This read-only attribute gets the session edition and is only available in
223 Oracle Database 11.2 (both client and server must be at this level or
224 higher for this to work).
225
226 .. versionadded:: 5.3
227
228 .. note::
229
230 This attribute is an extension to the DB API definition.
231
232
233 .. attribute:: Connection.encoding
234
235 This read-only attribute returns the IANA character set name of the
236 character set in use by the Oracle client for regular strings.
237
238 .. note::
239
240 This attribute is an extension to the DB API definition.
241
242
243 .. method:: Connection.enq(name, options, msgproperties, payload)
244
245 Returns a message id after successfully enqueuing a message. The options
246 object can be created using :meth:`~Connection.enqoptions()` and the
247 msgproperties object can be created using
248 :meth:`~Connection.msgproperties()`. The payload must be an object created
249 using :meth:`ObjectType.newobject()`.
250
251 .. versionadded:: 5.3
252
253 .. note::
254
255 This method is an extension to the DB API definition.
256
257
258 .. method:: Connection.enqoptions()
259
260 Returns an object specifying the options to use when enqueuing messages.
261 See :ref:`enqoptions` for more information.
262
263 .. versionadded:: 5.3
264
265 .. note::
266
267 This method is an extension to the DB API definition.
268
269
270 .. attribute:: Connection.external_name
271
272 This read-write attribute specifies the external name that is used by the
273 connection when logging distributed transactions.
274
275 .. versionadded:: 5.3
276
277 .. note::
278
279 This attribute is an extension to the DB API definition.
280
281
282 .. method:: Connection.getSodaDatabase()
283
284 Return a :ref:`SodaDatabase <sodadb>` object for Simple Oracle Document
285 Access (SODA). All SODA operations are performed either on the returned
286 SodaDatabase object or from objects created by the returned SodaDatabase
287 object. See `here <http://www.oracle.com/pls/topic/lookup?
288 ctx=dblatest&id=GUID-BE42F8D3-B86B-43B4-B2A3-5760A4DF79FB>`__ for
289 additional information on SODA.
290
291 SODA support in cx_Oracle is in Preview status and should not be used in
292 production. It will be supported with a future version of Oracle Client
293 libraries.
294
295 .. versionadded:: 7.0
296
297 .. note::
298
299 This method is an extension to the DB API definition.
300
301
302 .. method:: Connection.gettype(name)
303
304 Return a :ref:`type object <objecttype>` given its name. This can then be
305 used to create objects which can be bound to cursors created by this
306 connection.
307
308 .. versionadded:: 5.3
309
310 .. note::
311
312 This method is an extension to the DB API definition.
313
314
315 .. attribute:: Connection.handle
316
317 This read-only attribute returns the OCI service context handle for the
318 connection. It is primarily provided to facilitate testing the creation of
319 a connection using the OCI service context handle.
320
321 .. note::
322
323 This attribute is an extension to the DB API definition.
324
325
326 .. attribute:: Connection.inputtypehandler
327
328 This read-write attribute specifies a method called for each value that is
329 bound to a statement executed on any cursor associated with this
330 connection. The method signature is handler(cursor, value, arraysize) and
331 the return value is expected to be a variable object or None in which case
332 a default variable object will be created. If this attribute is None, the
333 default behavior will take place for all values bound to statements.
334
335 .. note::
336
337 This attribute is an extension to the DB API definition.
338
339
340 .. attribute:: Connection.internal_name
341
342 This read-write attribute specifies the internal name that is used by the
343 connection when logging distributed transactions.
344
345 .. versionadded:: 5.3
346
347 .. note::
348
349 This attribute is an extension to the DB API definition.
350
351
352 .. attribute:: Connection.ltxid
353
354 This read-only attribute returns the logical transaction id for the
355 connection. It is used within Oracle Transaction Guard as a means of
356 ensuring that transactions are not duplicated. See the Oracle documentation
357 and the provided sample for more information.
358
359 .. versionadded:: 5.3
360
361 .. note:
362
363 This attribute is an extension to the DB API definition. It is only
364 available when Oracle Database 12.1 or higher is in use on both the
365 server and the client.
366
367
368 .. attribute:: Connection.maxBytesPerCharacter
369
370 This read-only attribute returns the maximum number of bytes each character
371 can use for the client character set.
372
373 .. note::
374
375 This attribute is an extension to the DB API definition.
376
377
378 .. attribute:: Connection.module
379
380 This write-only attribute sets the module column in the v$session table.
381 The maximum length for this string is 48 and if you exceed this length you
382 will get ORA-24960.
383
384 .. note:
385
386 This attribute is an extension to the DB API definition.
387
388
389 .. method:: Connection.msgproperties()
390
391 Returns an object specifying the properties of messages used in advanced
392 queuing. See :ref:`msgproperties` for more information.
393
394 .. versionadded:: 5.3
395
396 .. note::
397
398 This method is an extension to the DB API definition.
399
400
401 .. attribute:: Connection.nencoding
402
403 This read-only attribute returns the IANA character set name of the
404 national character set in use by the Oracle client.
405
406 .. note::
407
408 This attribute is an extension to the DB API definition.
409
410
411 .. attribute:: Connection.outputtypehandler
412
413 This read-write attribute specifies a method called for each column that is
414 going to be fetched from any cursor associated with this connection. The
415 method signature is handler(cursor, name, defaultType, length, precision,
416 scale) and the return value is expected to be a variable object or None in
417 which case a default variable object will be created. If this attribute is
418 None, the default behavior will take place for all columns fetched from
419 cursors.
420
421 .. note::
422
423 This attribute is an extension to the DB API definition.
424
425
426 .. method:: Connection.ping()
427
428 Ping the server which can be used to test if the connection is still
429 active.
430
431 .. note::
432
433 This method is an extension to the DB API definition.
434
435
436 .. method:: Connection.prepare()
437
438 Prepare the distributed (global) transaction for commit. Return a boolean
439 indicating if a transaction was actually prepared in order to avoid the
440 error ORA-24756 (transaction does not exist).
441
442 .. note::
443
444 This method is an extension to the DB API definition.
445
446
447 .. method:: Connection.rollback()
448
449 Rollback any pending transactions.
450
451
452 .. method:: Connection.shutdown([mode])
453
454 Shutdown the database. In order to do this the connection must be connected
455 as :data:`~cx_Oracle.SYSDBA` or :data:`~cx_Oracle.SYSOPER`. Two calls must
456 be made unless the mode specified is :data:`~cx_Oracle.DBSHUTDOWN_ABORT`.
457 An example is shown below:
458
459 ::
460
461 import cx_Oracle
462
463 connection = cx_Oracle.connect(mode = cx_Oracle.SYSDBA)
464 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_IMMEDIATE)
465 cursor = connection.cursor()
466 cursor.execute("alter database close normal")
467 cursor.execute("alter database dismount")
468 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_FINAL)
469
470 .. note::
471
472 This method is an extension to the DB API definition.
473
474
475 .. method:: Connection.startup(force=False, restrict=False)
476
477 Startup the database. This is equivalent to the SQL\*Plus command "startup
478 nomount". The connection must be connected as :data:`~cx_Oracle.SYSDBA` or
479 :data:`~cx_Oracle.SYSOPER` with the :data:`~cx_Oracle.PRELIM_AUTH` option
480 specified for this to work. An example is shown below:
481
482 ::
483
484 import cx_Oracle
485
486 connection = cx_Oracle.connect(
487 mode=cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
488 connection.startup()
489 connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA)
490 cursor = connection.cursor()
491 cursor.execute("alter database mount")
492 cursor.execute("alter database open")
493
494 .. note::
495
496 This method is an extension to the DB API definition.
497
498
499 .. attribute:: Connection.stmtcachesize
500
501 This read-write attribute specifies the size of the statement cache. This
502 value can make a significant difference in performance (up to 100x) if you
503 have a small number of statements that you execute repeatedly.
504
505 .. note::
506
507 This attribute is an extension to the DB API definition.
508
509
510 .. 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)
511
512 Return a new :ref:`subscription object <subscrobj>` that receives
513 notifications for events that take place in the database that match the
514 given parameters.
515
516 The namespace parameter specifies the namespace the subscription uses. It
517 can be one of :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE` or
518 :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`.
519
520 The protocol parameter specifies the protocol to use when notifications are
521 sent. Currently the only valid value is :data:`cx_Oracle.SUBSCR_PROTO_OCI`.
522
523 The callback is expected to be a callable that accepts a single parameter.
524 A :ref:`message object <msgobjects>` is passed to this callback whenever a
525 notification is received.
526
527 The timeout value specifies that the subscription expires after the given
528 time in seconds. The default value of 0 indicates that the subscription
529 never expires.
530
531 The operations parameter enables filtering of the messages that are sent
532 (insert, update, delete). The default value will send notifications for all
533 operations. This parameter is only used when the namespace is set to
534 :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE`.
535
536 The port parameter specifies the listening port for callback notifications
537 from the database server. If not specified, an unused port will be selected
538 by the Oracle Client libraries.
539
540 The qos parameter specifies quality of service options. It should be one or
541 more of the following flags, OR'ed together:
542 :data:`cx_Oracle.SUBSCR_QOS_RELIABLE`,
543 :data:`cx_Oracle.SUBSCR_QOS_DEREG_NFY`,
544 :data:`cx_Oracle.SUBSCR_QOS_ROWIDS`,
545 :data:`cx_Oracle.SUBSCR_QOS_QUERY`,
546 :data:`cx_Oracle.SUBSCR_QOS_BEST_EFFORT`.
547
548 The ipAddress parameter specifies the IP address (IPv4 or IPv6) in standard
549 string notation to bind for callback notifications from the database
550 server. If not specified, the client IP address will be determined by the
551 Oracle Client libraries.
552
553 The groupingClass parameter specifies what type of grouping of
554 notifications should take place. Currently, if set, this value can only be
555 set to the value :data:`cx_Oracle.SUBSCR_GROUPING_CLASS_TIME`, which
556 will group notifications by the number of seconds specified in the
557 groupingValue parameter. The groupingType parameter should be one of the
558 values :data:`cx_Oracle.SUBSCR_GROUPING_TYPE_SUMMARY` (the default) or
559 :data:`cx_Oracle.SUBSCR_GROUPING_TYPE_LAST`.
560
561 The name parameter is used to identify the subscription and is specific to
562 the selected namespace. If the namespace parameter is
563 :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE` then the name is optional and
564 can be any value. If the namespace parameter is
565 :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`, however, the name must be in the
566 format '<QUEUE_NAME>' for single consumer queues and
567 '<QUEUE_NAME>:<CONSUMER_NAME>' for multiple consumer queues, and identifies
568 the queue that will be monitored for messages. The queue name may include
569 the schema, if needed.
570
571 *New in version 6.4:* The parameters ipAddress, groupingClass,
572 groupingValue, groupingType and name were added.
573
574 .. note::
575
576 This method is an extension to the DB API definition.
577
578 .. note::
579
580 The subscription can be deregistered in the database by calling the
581 function :meth:`~Connection.unsubscribe()`. If this method is not
582 called and the connection that was used to create the subscription is
583 explictly closed using the function :meth:`~Connection.close()`, the
584 subscription will not be deregistered in the database.
585
586
587 .. attribute:: Connection.tag
588
589 This read-write attribute initially contains the actual tag of the session
590 that was acquired from a pool by :meth:`SessionPool.acquire()`. If the
591 connection was not acquired from a pool or no tagging parameters were
592 specified (tag and matchanytag) when the connection was acquired from the
593 pool, this value will be None. If the value is changed, it must be a string
594 containing name=value pairs like "k1=v1;k2=v2".
595
596 If this value is not None when the connection is released back to the pool
597 it will be used to retag the session. This value can be overridden in the
598 call to :meth:`SessionPool.release()`.
599
600 .. note::
601
602 This attribute is an extension to the DB API definition.
603
604 .. versionadded:: 7.1
605
606
607 .. attribute:: Connection.tnsentry
608
609 This read-only attribute returns the TNS entry of the database to which a
610 connection has been established.
611
612 .. note::
613
614 This attribute is an extension to the DB API definition.
615
616
617 .. method:: Connection.unsubscribe(subscr)
618
619 Unsubscribe from events in the database that were originally subscribed to
620 using :meth:`~Connection.subscribe()`. The connection used to unsubscribe
621 should be the same one used to create the subscription, or should access
622 the same database and be connected as the same user name.
623
624 .. versionadded:: 6.4
625
626
627 .. attribute:: Connection.username
628
629 This read-only attribute returns the name of the user which established the
630 connection to the database.
631
632 .. note::
633
634 This attribute is an extension to the DB API definition.
635
636
637 .. attribute:: Connection.version
638
639 This read-only attribute returns the version of the database to which a
640 connection has been established.
641
642 .. note::
643
644 This attribute is an extension to the DB API definition.
645
646 .. note::
647
648 If you connect to Oracle Database 18 or higher with client libraries
649 12.2 or lower that you will only receive the base version (such as
650 18.0.0.0.0) instead of the full version (18.3.0.0.0).
651
+0
-557
doc/src/cursor.rst less more
0 .. _cursorobj:
1
2 *************
3 Cursor Object
4 *************
5
6
7 .. method:: Cursor.__enter__()
8
9 The entry point for the cursor as a context manager. It returns itself.
10
11 .. note::
12
13 This method is an extension to the DB API definition.
14
15
16 .. method:: Cursor.__exit__()
17
18 The exit point for the cursor as a context manager. It closes the cursor.
19
20 .. note::
21
22 This method is an extension to the DB API definition.
23
24
25 .. attribute:: Cursor.arraysize
26
27 This read-write attribute specifies the number of rows to fetch at a time
28 internally and is the default number of rows to fetch with the
29 :meth:`~Cursor.fetchmany()` call. It defaults to 100 meaning to fetch 100
30 rows at a time. Note that this attribute can drastically affect the
31 performance of a query since it directly affects the number of network
32 round trips that need to be performed. This is the reason for setting it to
33 100 instead of the 1 that the DB API recommends.
34
35
36 .. attribute:: Cursor.bindarraysize
37
38 This read-write attribute specifies the number of rows to bind at a time
39 and is used when creating variables via :meth:`~Cursor.setinputsizes()` or
40 :meth:`~Cursor.var()`. It defaults to 1 meaning to bind a single row at a
41 time.
42
43 .. note::
44
45 The DB API definition does not define this attribute.
46
47
48 .. method:: Cursor.arrayvar(dataType, value, [size])
49
50 Create an array variable associated with the cursor of the given type and
51 size and return a :ref:`variable object <varobj>`. The value is either an
52 integer specifying the number of elements to allocate or it is a list and
53 the number of elements allocated is drawn from the size of the list. If the
54 value is a list, the variable is also set with the contents of the list. If
55 the size is not specified and the type is a string or binary, 4000 bytes
56 is allocated. This is needed for passing arrays to PL/SQL (in cases where
57 the list might be empty and the type cannot be determined automatically) or
58 returning arrays from PL/SQL.
59
60 .. note::
61
62 The DB API definition does not define this method.
63
64
65 .. method:: Cursor.bindnames()
66
67 Return the list of bind variable names bound to the statement. Note that a
68 statement must have been prepared first.
69
70 .. note::
71
72 The DB API definition does not define this method.
73
74
75 .. attribute:: Cursor.bindvars
76
77 This read-only attribute provides the bind variables used for the last
78 execute. The value will be either a list or a dictionary depending on
79 whether binding was done by position or name. Care should be taken when
80 referencing this attribute. In particular, elements should not be removed
81 or replaced.
82
83 .. note::
84
85 The DB API definition does not define this attribute.
86
87
88 .. method:: Cursor.callfunc(name, returnType, parameters=[], \
89 keywordParameters={})
90
91 Call a function with the given name. The return type is specified in the
92 same notation as is required by :meth:`~Cursor.setinputsizes()`. The
93 sequence of parameters must contain one entry for each parameter that the
94 function expects. Any keyword parameters will be included after the
95 positional parameters. The result of the call is the return value of the
96 function.
97
98 .. note::
99
100 The DB API definition does not define this method.
101
102 .. note::
103
104 If you intend to call :meth:`Cursor.setinputsizes()` on the cursor
105 prior to making this call, then note that the first item in the
106 parameter list refers to the return value of the function.
107
108
109 .. method:: Cursor.callproc(name, parameters=[], keywordParameters={})
110
111 Call a procedure with the given name. The sequence of parameters must
112 contain one entry for each parameter that the procedure expects. The result
113 of the call is a modified copy of the input sequence. Input parameters are
114 left untouched; output and input/output parameters are replaced with
115 possibly new values. Keyword parameters will be included after the
116 positional parameters and are not returned as part of the output sequence.
117
118 .. note::
119
120 The DB API definition does not allow for keyword parameters.
121
122
123 .. method:: Cursor.close()
124
125 Close the cursor now, rather than whenever __del__ is called. The cursor
126 will be unusable from this point forward; an Error exception will be raised
127 if any operation is attempted with the cursor.
128
129
130 .. attribute:: Cursor.connection
131
132 This read-only attribute returns a reference to the connection object on
133 which the cursor was created.
134
135 .. note::
136
137 This attribute is an extension to the DB API definition but it is
138 mentioned in PEP 249 as an optional extension.
139
140
141 .. data:: Cursor.description
142
143 This read-only attribute is a sequence of 7-item sequences. Each of these
144 sequences contains information describing one result column: (name, type,
145 display_size, internal_size, precision, scale, null_ok). This attribute
146 will be None for operations that do not return rows or if the cursor has
147 not had an operation invoked via the :meth:`~Cursor.execute()` method yet.
148
149 The type will be one of the type objects defined at the module level.
150
151
152 .. method:: Cursor.execute(statement, [parameters], \*\*keywordParameters)
153
154 Execute a statement against the database. Parameters may be passed as a
155 dictionary or sequence or as keyword parameters. If the parameters are a
156 dictionary, the values will be bound by name and if the parameters are a
157 sequence the values will be bound by position. Note that if the values are
158 bound by position, the order of the variables is from left to right as they
159 are encountered in the statement and SQL statements are processed
160 differently than PL/SQL statements. For this reason, it is generally
161 recommended to bind parameters by name instead of by position.
162
163 Parameters passed as a dictionary are name and value pairs. The name maps
164 to the bind variable name used by the statement and the value maps to the
165 Python value you wish bound to that bind variable.
166
167 A reference to the statement will be retained by the cursor. If None or the
168 same string object is passed in again, the cursor will execute that
169 statement again without performing a prepare or rebinding and redefining.
170 This is most effective for algorithms where the same statement is used, but
171 different parameters are bound to it (many times). Note that parameters
172 that are not passed in during subsequent executions will retain the value
173 passed in during the last execution that contained them.
174
175 For maximum efficiency when reusing an statement, it is best to use the
176 :meth:`~Cursor.setinputsizes()` method to specify the parameter types and
177 sizes ahead of time; in particular, None is assumed to be a string of
178 length 1 so any values that are later bound as numbers or dates will raise
179 a TypeError exception.
180
181 If the statement is a query, the cursor is returned as a convenience to the
182 caller (so it can be used directly as an iterator over the rows in the
183 cursor); otherwise, ``None`` is returned.
184
185 .. note::
186
187 The DB API definition does not define the return value of this method.
188
189
190 .. method:: Cursor.executemany(statement, parameters, batcherrors=False, \
191 arraydmlrowcounts=False)
192
193 Prepare a statement for execution against a database and then execute it
194 against all parameter mappings or sequences found in the sequence
195 parameters. The statement is managed in the same way as the
196 :meth:`~Cursor.execute()` method manages it. If the size of the buffers
197 allocated for any of the parameters exceeds 2 GB, you will receive the
198 error "DPI-1015: array size of <n> is too large", where <n> varies with the
199 size of each element being allocated in the buffer. If you receive this
200 error, decrease the number of elements in the sequence parameters.
201
202 If there are no parameters, or parameters have previously been bound, the
203 number of iterations can be specified as an integer instead of needing to
204 provide a list of empty mappings or sequences.
205
206 When true, the batcherrors parameter enables batch error support within
207 Oracle and ensures that the call succeeds even if an exception takes place
208 in one or more of the sequence of parameters. The errors can then be
209 retrieved using :meth:`~Cursor.getbatcherrors()`.
210
211 When true, the arraydmlrowcounts parameter enables DML row counts to be
212 retrieved from Oracle after the method has completed. The row counts can
213 then be retrieved using :meth:`~Cursor.getarraydmlrowcounts()`.
214
215 Both the batcherrors parameter and the arraydmlrowcounts parameter can only
216 be true when executing an insert, update, delete or merge statement; in all
217 other cases an error will be raised.
218
219 For maximum efficiency, it is best to use the
220 :meth:`~Cursor.setinputsizes()` method to specify the parameter types and
221 sizes ahead of time; in particular, None is assumed to be a string of
222 length 1 so any values that are later bound as numbers or dates will raise
223 a TypeError exception.
224
225
226 .. method:: Cursor.executemanyprepared(numIters)
227
228 Execute the previously prepared and bound statement the given number of
229 times. The variables that are bound must have already been set to their
230 desired value before this call is made. This method was designed for the
231 case where optimal performance is required as it comes at the expense of
232 compatibility with the DB API.
233
234 .. note::
235
236 The DB API definition does not define this method.
237
238 .. deprecated:: 6.4
239 Use :meth:`~Cursor.executemany()` instead with None for the statement
240 argument and an integer for the parameters argument.
241
242
243 .. method:: Cursor.fetchall()
244
245 Fetch all (remaining) rows of a query result, returning them as a list of
246 tuples. An empty list is returned if no more rows are available. Note that
247 the cursor's arraysize attribute can affect the performance of this
248 operation, as internally reads from the database are done in batches
249 corresponding to the arraysize.
250
251 An exception is raised if the previous call to :meth:`~Cursor.execute()`
252 did not produce any result set or no call was issued yet.
253
254
255 .. method:: Cursor.fetchmany([numRows=cursor.arraysize])
256
257 Fetch the next set of rows of a query result, returning a list of tuples.
258 An empty list is returned if no more rows are available. Note that the
259 cursor's arraysize attribute can affect the performance of this operation.
260
261 The number of rows to fetch is specified by the parameter. If it is not
262 given, the cursor's arrysize attribute determines the number of rows to be
263 fetched. If the number of rows available to be fetched is fewer than the
264 amount requested, fewer rows will be returned.
265
266 An exception is raised if the previous call to :meth:`~Cursor.execute()`
267 did not produce any result set or no call was issued yet.
268
269
270 .. method:: Cursor.fetchone()
271
272 Fetch the next row of a query result set, returning a single tuple or None
273 when no more data is available.
274
275 An exception is raised if the previous call to :meth:`~Cursor.execute()`
276 did not produce any result set or no call was issued yet.
277
278
279 .. method:: Cursor.fetchraw([numRows=cursor.arraysize])
280
281 Fetch the next set of rows of a query result into the internal buffers of
282 the defined variables for the cursor. The number of rows actually fetched
283 is returned. This method was designed for the case where optimal
284 performance is required as it comes at the expense of compatibility with
285 the DB API.
286
287 An exception is raised if the previous call to :meth:`~Cursor.execute()`
288 did not produce any result set or no call was issued yet.
289
290 .. note::
291
292 The DB API definition does not define this method.
293
294
295 .. attribute:: Cursor.fetchvars
296
297 This read-only attribute specifies the list of variables created for the
298 last query that was executed on the cursor. Care should be taken when
299 referencing this attribute. In particular, elements should not be removed
300 or replaced.
301
302 .. note::
303
304 The DB API definition does not define this attribute.
305
306
307 .. method:: Cursor.getarraydmlrowcounts()
308
309 Retrieve the DML row counts after a call to :meth:`~Cursor.executemany()`
310 with arraydmlrowcounts enabled. This will return a list of integers
311 corresponding to the number of rows affected by the DML statement for each
312 element of the array passed to :meth:`~Cursor.executemany()`.
313
314 .. note::
315
316 The DB API definition does not define this method and it is only
317 available for Oracle 12.1 and higher.
318
319
320 .. method:: Cursor.getbatcherrors()
321
322 Retrieve the exceptions that took place after a call to
323 :meth:`~Cursor.executemany()` with batcherors enabled. This will return a
324 list of Error objects, one error for each iteration that failed. The offset
325 can be determined by looking at the offset attribute of the error object.
326
327 .. note::
328
329 The DB API definition does not define this method.
330
331
332 .. method:: Cursor.getimplicitresults()
333
334 Return a list of cursors which correspond to implicit results made
335 available from a PL/SQL block or procedure without the use of OUT ref
336 cursor parameters. The PL/SQL block or procedure opens the cursors and
337 marks them for return to the client using the procedure
338 dbms_sql.return_result. Cursors returned in this fashion should not be
339 closed. They will be closed automatically by the parent cursor when it is
340 closed. Closing the parent cursor will invalidate the cursors returned by
341 this method.
342
343 .. versionadded:: 5.3
344
345 .. note::
346
347 The DB API definition does not define this method and it is only
348 available for Oracle Database 12.1 (both client and server must be at
349 this level or higher). It is most like the DB API method nextset(), but
350 unlike that method (which requires that the next result set overwrite
351 the current result set), this method returns cursors which can be
352 fetched independently of each other.
353
354
355 .. attribute:: Cursor.inputtypehandler
356
357 This read-write attribute specifies a method called for each value that is
358 bound to a statement executed on the cursor and overrides the attribute
359 with the same name on the connection if specified. The method signature is
360 handler(cursor, value, arraysize) and the return value is expected to be a
361 variable object or None in which case a default variable object will be
362 created. If this attribute is None, the value of the attribute with the
363 same name on the connection is used.
364
365 .. note::
366
367 This attribute is an extension to the DB API definition.
368
369
370 .. method:: Cursor.__iter__()
371
372 Returns the cursor itself to be used as an iterator.
373
374 .. note::
375
376 This method is an extension to the DB API definition but it is
377 mentioned in PEP 249 as an optional extension.
378
379
380 .. attribute:: Cursor.outputtypehandler
381
382 This read-write attribute specifies a method called for each column that is
383 to be fetched from this cursor. The method signature is
384 handler(cursor, name, defaultType, length, precision, scale) and the return
385 value is expected to be a variable object or None in which case a default
386 variable object will be created. If this attribute is None, the value of
387 the attribute with the same name on the connection is used instead.
388
389 .. note::
390
391 This attribute is an extension to the DB API definition.
392
393
394 .. method:: Cursor.parse(statement)
395
396 This can be used to parse a statement without actually executing it (this
397 step is done automatically by Oracle when a statement is executed).
398
399 .. note::
400
401 The DB API definition does not define this method.
402
403 .. note::
404
405 You can parse any DML or DDL statement. DDL statements are executed
406 immediately and an implied commit takes place.
407
408
409 .. method:: Cursor.prepare(statement, [tag])
410
411 This can be used before a call to :meth:`~Cursor.execute()` to define the
412 statement that will be executed. When this is done, the prepare phase will
413 not be performed when the call to :meth:`~Cursor.execute()` is made with
414 None or the same string object as the statement. If specified the
415 statement will be returned to the statement cache with the given tag. See
416 the Oracle documentation for more information about the statement cache.
417
418 .. note::
419
420 The DB API definition does not define this method.
421
422
423 .. attribute:: Cursor.rowcount
424
425 This read-only attribute specifies the number of rows that have currently
426 been fetched from the cursor (for select statements) or that have been
427 affected by the operation (for insert, update and delete statements).
428
429
430 .. attribute:: Cursor.rowfactory
431
432 This read-write attribute specifies a method to call for each row that is
433 retrieved from the database. Ordinarily a tuple is returned for each row
434 but if this attribute is set, the method is called with the tuple that
435 would normally be returned, and the result of the method is returned
436 instead.
437
438 .. note::
439
440 The DB API definition does not define this attribute.
441
442
443 .. method:: Cursor.scroll(value=0, mode="relative")
444
445 Scroll the cursor in the result set to a new position according to the
446 mode.
447
448 If mode is "relative" (the default value), the value is taken as an offset
449 to the current position in the result set. If set to "absolute", value
450 states an absolute target position. If set to "first", the cursor is
451 positioned at the first row and if set to "last", the cursor is set to the
452 last row in the result set.
453
454 An error is raised if the mode is "relative" or "absolute" and the scroll
455 operation would position the cursor outside of the result set.
456
457 .. versionadded:: 5.3
458
459 .. note::
460
461 This method is an extension to the DB API definition but it is
462 mentioned in PEP 249 as an optional extension.
463
464
465 .. attribute:: Cursor.scrollable
466
467 This read-write boolean attribute specifies whether the cursor can be
468 scrolled or not. By default, cursors are not scrollable, as the server
469 resources and response times are greater than nonscrollable cursors. This
470 attribute is checked and the corresponding mode set in Oracle when calling
471 the method :meth:`~Cursor.execute()`.
472
473 .. versionadded:: 5.3
474
475 .. note::
476
477 The DB API definition does not define this attribute.
478
479
480 .. method:: Cursor.setinputsizes(\*args, \*\*keywordArgs)
481
482 This can be used before a call to :meth:`~Cursor.execute()`,
483 :meth:`~Cursor.callfunc()` or :meth:`~Cursor.callproc()` to predefine
484 memory areas for the operation's parameters. Each parameter should be a
485 type object corresponding to the input that will be used or it should be an
486 integer specifying the maximum length of a string parameter. Use keyword
487 parameters when binding by name and positional parameters when binding by
488 position. The singleton None can be used as a parameter when using
489 positional parameters to indicate that no space should be reserved for that
490 position.
491
492 .. note::
493
494 If you plan to use :meth:`~Cursor.callfunc()` then be aware that the
495 first parameter in the list refers to the return value of the function.
496
497
498 .. method:: Cursor.setoutputsize(size, [column])
499
500 This method does nothing and is retained solely for compatibility with the
501 DB API. The module automatically allocates as much space as needed to fetch
502 LONG and LONG RAW columns (or CLOB as string and BLOB as bytes).
503
504
505 .. attribute:: Cursor.statement
506
507 This read-only attribute provides the string object that was previously
508 prepared with :meth:`~Cursor.prepare()` or executed with
509 :meth:`~Cursor.execute()`.
510
511 .. note::
512
513 The DB API definition does not define this attribute.
514
515
516 .. method:: Cursor.var(dataType, [size, arraysize, inconverter, outconverter, \
517 typename, encodingErrors])
518
519 Create a variable with the specified charactistics. This method was
520 designed for use with PL/SQL in/out variables where the length or type
521 cannot be determined automatically from the Python object passed in or for
522 use in input and output type handlers defined on cursors or connections.
523
524 The dataType parameter specifies the type of data that should be stored in
525 the variable. This should be one of the types defined at the module level
526 (such as :data:`cx_Oracle.STRING`) or a Python type that cx_Oracle knows
527 how to process (such as str) or an object type returned from the method
528 :meth:`Connection.gettype()`.
529
530 The size parameter specifies the length of string and raw variables and is
531 ignored in all other cases. If not specified for string and raw variables,
532 the value 4000 is used.
533
534 The arraysize parameter specifies the number of elements the variable will
535 have. If not specified the bind array size (usually 1) is used. When a
536 variable is created in an output type handler this parameter should be set
537 to the cursor's array size.
538
539 The inconverter and outconverter parameters specify methods used for
540 converting values to/from the database. More information can be found in
541 the section on :ref:`variable objects<varobj>`.
542
543 The typename parameter specifies the name of a SQL object type and must be
544 specified when using type :data:`cx_Oracle.OBJECT` unless the type object
545 was passed directly as the first parameter.
546
547 The encodingErrors parameter specifies what should happen when decoding
548 byte strings fetched from the database into strings (Python 3) or unicode
549 objects (Python 2). It should be one of the values noted in the builtin
550 `decode <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__
551 function.
552
553 .. note::
554
555 The DB API definition does not define this method.
556
0
10 Welcome to cx_Oracle's documentation!
21 =====================================
32
43 **cx_Oracle** is a module that enables access to Oracle Database and conforms
54 to the Python database API specification. This module is currently tested
6 against Oracle Client 11.2, 12.1, 12.2 and 18.3 and Python 2.7, 3.5, 3.6 and
7 3.7.
5 against Oracle Client 21c, 19c, 18c, 12c, and 11.2, and Python 3.6, 3.7, 3.8
6 and 3.9. Older versions of cx_Oracle may be used with previous Python
7 releases.
88
99 **cx_Oracle** is distributed under an open-source :ref:`license <license>`
10 (the BSD license).
10 (the BSD license). A detailed description of cx_Oracle changes can be found in
11 the :ref:`release notes <releasenotes>`.
1112
1213 Contents:
1314
15 User Guide
16 ==========
17
1418 .. toctree::
15 :maxdepth: 2
19 :maxdepth: 3
1620
17 installation.rst
18 module.rst
19 connection.rst
20 cursor.rst
21 variable.rst
22 session_pool.rst
23 subscription.rst
24 lob.rst
25 objecttype.rst
26 aq.rst
27 soda.rst
28 whatsnew.rst
29 releasenotes.rst
30 license.rst
21 user_guide/introduction.rst
22 user_guide/installation.rst
23 user_guide/initialization.rst
24 user_guide/connection_handling.rst
25 user_guide/sql_execution.rst
26 user_guide/plsql_execution.rst
27 user_guide/bind.rst
28 user_guide/lob_data.rst
29 user_guide/json_data_type.rst
30 user_guide/soda.rst
31 user_guide/xml_data_type.rst
32 user_guide/batch_statement.rst
33 user_guide/exception_handling.rst
34 user_guide/aq.rst
35 user_guide/cqn.rst
36 user_guide/txn_management.rst
37 user_guide/tuning.rst
38 user_guide/globalization.rst
39 user_guide/startup.rst
40 user_guide/ha.rst
41 user_guide/tracing_sql.rst
42
43 API Manual
44 ==========
45
46 .. toctree::
47 :maxdepth: 3
48
49 api_manual/module.rst
50 api_manual/connection.rst
51 api_manual/cursor.rst
52 api_manual/variable.rst
53 api_manual/session_pool.rst
54 api_manual/subscription.rst
55 api_manual/lob.rst
56 api_manual/object_type.rst
57 api_manual/aq.rst
58 api_manual/soda.rst
3159
3260
3361 Indices and tables
3664 * :ref:`genindex`
3765 * :ref:`modindex`
3866 * :ref:`search`
39
+0
-667
doc/src/installation.rst less more
0 .. _installation:
1
2 ************************
3 cx_Oracle 7 Installation
4 ************************
5
6 .. contents:: :local:
7
8 Overview
9 ========
10
11 To use cx_Oracle 7 with Python and Oracle Database you need:
12
13 - Python 2.7 or 3.5 and higher. Older versions of cx_Oracle may work
14 with older versions of Python.
15
16 - Oracle client libraries. These can be from the free `Oracle Instant
17 Client
18 <http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__,
19 or those included in Oracle Database if Python is on the same
20 machine as the database. Oracle client libraries versions 18, 12,
21 and 11.2 are supported on Linux, Windows and macOS. Users have
22 also reported success with other platforms.
23
24 - An Oracle Database. Oracle's standard client-server version
25 interoperability allows cx_Oracle to connect to both older and newer
26 databases.
27
28
29 Quick Start cx_Oracle Installation
30 ==================================
31
32 - An installation of `Python <https://www.python.org/downloads>`__ is
33 needed. Python 2.7 and Python 3.5 and higher are supported by cx_Oracle 7.
34
35 - Install cx_Oracle from `PyPI
36 <https://pypi.python.org/pypi/cx_Oracle>`__ with::
37
38 python -m pip install cx_Oracle --upgrade
39
40 Note: if a binary wheel package is not available for your platform,
41 the source package will be downloaded instead. This will be compiled
42 and the resulting binary installed.
43
44 - Add Oracle 18, 12 or 11.2 client libraries to your operating
45 system library search path such as ``PATH`` on Windows or
46 ``LD_LIBRARY_PATH`` on Linux. On macOS move the files to ``~/lib``
47 or ``/usr/local/lib``.
48
49 - If your database is on a remote computer, then download and unzip the client
50 libraries from the free `Oracle Instant Client
51 <http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__
52 "Basic" or "Basic Light" package for your operating system
53 architecture.
54
55 Instant Client on Windows requires an appropriate `Microsoft
56 Windows Redistributables
57 <https://oracle.github.io/odpi/doc/installation.html#windows>`__.
58 On Linux, the ``libaio`` (sometimes called ``libaio1``) package
59 is needed.
60
61 - Alternatively use the client libraries already available in a
62 locally installed database such as the free `Oracle XE
63 <https://www.oracle.com/database/technologies/appdev/xe.html>`__
64 release.
65
66 Version 18 and 12.2 client libraries can connect to Oracle Database 11.2 or
67 greater. Version 12.1 client libraries can connect to Oracle Database
68 10.2 or greater. Version 11.2 client libraries can connect to Oracle
69 Database 9.2 or greater.
70
71 The database abstraction layer in cx_Oracle is `ODPI-C
72 <https://github.com/oracle/odpi>`__, which means that the `ODPI-C
73 installation instructions
74 <https://oracle.github.io/odpi/doc/installation.html>`__ can be useful
75 to review.
76
77 - Create a script like the one below::
78
79 # myscript.py
80
81 from __future__ import print_function
82
83 import cx_Oracle
84
85 # Connect as user "hr" with password "welcome" to the "oraclepdb" service running on this computer.
86 connection = cx_Oracle.connect("hr", "welcome", "localhost/orclpdb")
87
88 cursor = connection.cursor()
89 cursor.execute("""
90 SELECT first_name, last_name
91 FROM employees
92 WHERE department_id = :did AND employee_id > :eid""",
93 did = 50,
94 eid = 190)
95 for fname, lname in cursor:
96 print("Values:", fname, lname)
97
98 Locate your Oracle Database username and password, and the database
99 connection string. The connection string is commonly of the format
100 ``hostname/servicename``, using the hostname where the database is
101 running, and the service name of the Oracle Database instance.
102
103 Substitute your username, password and connection string in the
104 code. Run the Python script, for example::
105
106 python myscript.py
107
108 You can learn how to use cx_Oracle from the :ref:`API documentation <module>`
109 and `samples
110 <https://github.com/oracle/python-cx_Oracle/blob/master/samples>`__.
111
112 If you run into installation trouble, check out the section on `Troubleshooting`_.
113
114
115 Oracle Client and Oracle Database Interoperability
116 ==================================================
117
118 cx_Oracle requires Oracle Client libraries. The libraries provide the
119 necessary network connectivity to access an Oracle Database instance.
120 They also provide basic and advanced connection management and data
121 features to cx_Oracle.
122
123 The simplest way to get Oracle Client libraries is to install the free
124 `Oracle Instant Client
125 <http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__
126 "Basic" or "Basic Light" package. The libraries are also available in
127 any Oracle Database installation or full Oracle Client installation.
128
129 Oracle's standard client-server network interoperability allows
130 connections between different versions of Oracle Client libraries and
131 Oracle Database. For certified configurations see Oracle Support's
132 `Doc ID 207303.1
133 <https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__.
134 In summary, Oracle Client 18 and 12.2 can connect to Oracle Database 11.2 or
135 greater. Oracle Client 12.1 can connect to Oracle Database 10.2 or
136 greater. Oracle Client 11.2 can connect to Oracle Database 9.2 or
137 greater. The technical restrictions on creating connections may be
138 more flexible. For example Oracle Client 12.2 can successfully
139 connect to Oracle Database 10.2.
140
141 cx_Oracle uses the shared library loading mechanism available on each
142 supported platform to load the Oracle Client libraries at runtime. It
143 does not need to be rebuilt for different versions of the libraries.
144 Since a single cx_Oracle binary can use different client versions and
145 also access multiple database versions, it is important your
146 application is tested in your intended release environments. Newer
147 Oracle clients support new features, such as the `oraaccess.xml
148 <http://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__ external configuration
149 file available with 12.1 or later clients, session pool improvements,
150 call timeouts with 18 or later clients, and `other enhancements
151 <http://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D60519C3-406F-4588-8DA1-D475D5A3E1F6>`__.
152
153 The cx_Oracle function :func:`~cx_Oracle.clientversion()` can be used
154 to determine which Oracle Client version is in use and the attribute
155 :attr:`Connection.version` can be used to determine which Oracle
156 Database version a connection is accessing. These can then be used to
157 adjust application behavior accordingly. Attempts to use some Oracle
158 features that are not supported by a particular client/server
159 combination may result in runtime errors. These include:
160
161 - when attempting to access attributes that are not supported by the
162 current Oracle Client library you will get the error "ORA-24315: illegal
163 attribute type"
164
165 - when attempting to use implicit results with Oracle Client 11.2
166 against Oracle Database 12c you will get the error "ORA-29481:
167 Implicit results cannot be returned to client"
168
169 - when attempting to get array DML row counts with Oracle Client
170 11.2 you will get the error "DPI-1050: Oracle Client library must be at
171 version 12.1 or higher"
172
173
174 Installing cx_Oracle on Linux
175 =============================
176
177 This section discusses the generic installation method on Linux.
178 Using Python and cx_Oracle RPM packages on Oracle Linux is discussed
179 in :ref:`oraclelinux`.
180
181 Install cx_Oracle
182 -----------------
183
184 The generic way to install cx_Oracle on Linux is to use Python's `Pip
185 <http://pip.readthedocs.io/en/latest/installing/>`__ package to
186 install cx_Oracle from `PyPI
187 <https://pypi.python.org/pypi/cx_Oracle>`__::
188
189 python -m pip install cx_Oracle --upgrade
190
191 This will download and install a pre-compiled binary `if one is
192 available <https://pypi.python.org/pypi/cx_Oracle>`__ for your
193 architecture. If a pre-compiled binary is not available, the source
194 will be downloaded, compiled, and the resulting binary installed.
195 Compiling cx_Oracle requires the `Python.h` header file. If you are
196 using the default python package, this file is in the ``python-devel``
197 package or equivalent.
198
199 Install Oracle Client
200 ---------------------
201
202 Using cx_Oracle requires Oracle Client libraries to be installed.
203 These provide the necessary network connectivity allowing cx_Oracle
204 to access an Oracle Database instance. Oracle Client versions 18,
205 12 and 11.2 are supported.
206
207 - If your database is on a remote computer, then download the free `Oracle
208 Instant Client
209 <http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__
210 "Basic" or "Basic Light" package for your operating system
211 architecture. Use the RPM or ZIP packages, based on your
212 preferences.
213
214 - Alternatively use the client libraries already available in a
215 locally installed database such as the free `Oracle XE
216 <https://www.oracle.com/database/technologies/appdev/xe.html>`__
217 release.
218
219 Oracle Instant Client Zip Files
220 +++++++++++++++++++++++++++++++
221
222 To use cx_Oracle with Oracle Instant Client zip files:
223
224 1. Download an Oracle 18, 12, or 11.2 "Basic" or "Basic Light" zip file: `64-bit
225 <http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html>`__
226 or `32-bit
227 <http://www.oracle.com/technetwork/topics/linuxsoft-082809.html>`__, matching your
228 Python architecture.
229
230 2. Unzip the package into a single directory that is accessible to your
231 application. For example::
232
233 mkdir -p /opt/oracle
234 cd /opt/oracle
235 unzip instantclient-basic-linux.x64-18.3.0.0.0dbru.zip
236
237 3. Install the ``libaio`` package with sudo or as the root user. For example::
238
239 sudo yum install libaio
240
241 On some Linux distributions this package is called ``libaio1`` instead.
242
243 4. If there is no other Oracle software on the machine that will be
244 impacted, permanently add Instant Client to the runtime link
245 path. For example, with sudo or as the root user::
246
247 sudo sh -c "echo /opt/oracle/instantclient_18_3 > /etc/ld.so.conf.d/oracle-instantclient.conf"
248 sudo ldconfig
249
250 Alternatively, set the environment variable ``LD_LIBRARY_PATH`` to
251 the appropriate directory for the Instant Client version. For
252 example::
253
254 export LD_LIBRARY_PATH=/opt/oracle/instantclient_18_3:$LD_LIBRARY_PATH
255
256 5. If you intend to co-locate optional Oracle configuration files such
257 as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with
258 Instant Client, then put them in the ``network/admin``
259 subdirectory. With Instant Client 12.2 or earlier, create this
260 manually. For example::
261
262 mkdir -p /opt/oracle/instantclient_12_2/network/admin
263
264 This is the default Oracle configuration directory for executables
265 linked with this Instant Client.
266
267 Alternatively, Oracle configuration files can be put in another,
268 accessible directory. Then set the environment variable
269 ``TNS_ADMIN`` to that directory name.
270
271 Oracle Instant Client RPMs
272 ++++++++++++++++++++++++++
273
274 To use cx_Oracle with Oracle Instant Client RPMs:
275
276 1. Download an Oracle 18, 12, or 11.2 "Basic" or "Basic Light" RPM: `64-bit
277 <http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html>`__
278 or `32-bit
279 <http://www.oracle.com/technetwork/topics/linuxsoft-082809.html>`__, matching your
280 Python architecture.
281
282 Oracle's yum server has `Instant Client RPMs for Oracle Linux 7
283 <http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/index.html>`__
284 and `Instant Client RPMs for Oracle Linux 6
285 <http://yum.oracle.com/repo/OracleLinux/OL6/oracle/instantclient/x86_64/index.html>`__
286 that can be downloaded without needing a click-through.
287
288 2. Install the downloaded RPM with sudo or as the root user. For example::
289
290 sudo yum install oracle-instantclient18.3-basic-18.3.0.0.0-1.x86_64.rpm
291
292 Yum will automatically install required dependencies, such as ``libaio``.
293
294 3. If there is no other Oracle software on the machine that will be
295 impacted, permanently add Instant Client to the runtime link
296 path. For example, with sudo or as the root user::
297
298 sudo sh -c "echo /usr/lib/oracle/18.3/client64/lib > /etc/ld.so.conf.d/oracle-instantclient.conf"
299 sudo ldconfig
300
301 Alternatively, set the environment variable ``LD_LIBRARY_PATH`` to
302 the appropriate directory for the Instant Client version. For
303 example::
304
305 export LD_LIBRARY_PATH=/usr/lib/oracle/18.3/client64/lib:$LD_LIBRARY_PATH
306
307 4. If you intend to co-locate optional Oracle configuration files such
308 as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with
309 Instant Client, then put them in the ``network/admin`` subdirectory
310 under ``lib/``. With Instant Client 12.2 or earlier, create this
311 manually. For example::
312
313 sudo mkdir -p /usr/lib/oracle/12.2/client64/lib/network/admin
314
315 This is the default Oracle configuration directory for executables
316 linked with this Instant Client.
317
318 Alternatively, Oracle configuration files can be put in another,
319 accessible directory. Then set the environment variable
320 ``TNS_ADMIN`` to that directory name.
321
322 Local Database or Full Oracle Client
323 ++++++++++++++++++++++++++++++++++++
324
325 cx_Oracle applications can use Oracle Client 18, 12, or 11.2 libraries
326 from a local Oracle Database or full Oracle Client installation.
327
328 The libraries must be either 32-bit or 64-bit, matching your
329 Python architecture.
330
331 1. Set required Oracle environment variables by running the Oracle environment
332 script. For example::
333
334 source /usr/local/bin/oraenv
335
336 For Oracle Database XE, run::
337
338 source /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh
339
340 2. Optional Oracle configuration files such as ``tnsnames.ora``,
341 ``sqlnet.ora`` or ``oraaccess.xml`` can be placed in
342 ``$ORACLE_HOME/network/admin``.
343
344 Alternatively, Oracle configuration files can be put in another,
345 accessible directory. Then set the environment variable
346 ``TNS_ADMIN`` to that directory name.
347
348
349 .. _oraclelinux:
350
351 Installing cx_Oracle RPMs on Oracle Linux
352 =========================================
353
354 Python and cx_Oracle RPM packages are available from the `Oracle Linux yum server
355 <http://yum.oracle.com/>`__. Various versions of Python are easily installed.
356 Using the yum server makes it easy to keep up to date.
357
358 Installation instructions are at `Oracle Linux for Python
359 Developers <https://yum.oracle.com/oracle-linux-python.html>`__.
360
361 Installing cx_Oracle on Windows
362 ===============================
363
364 Install cx_Oracle
365 -----------------
366
367 Use Python's `Pip <http://pip.readthedocs.io/en/latest/installing/>`__
368 package to install cx_Oracle from `PyPI
369 <https://pypi.python.org/pypi/cx_Oracle>`__::
370
371 python -m pip install cx_Oracle --upgrade
372
373 This will download and install a pre-compiled binary `if one is
374 available <https://pypi.python.org/pypi/cx_Oracle>`__ for your
375 architecture. If a pre-compiled binary is not available, the source
376 will be downloaded, compiled, and the resulting binary installed.
377
378 Install Oracle Client
379 ---------------------
380
381 Using cx_Oracle requires Oracle Client libraries to be installed.
382 These provide the necessary network connectivity allowing cx_Oracle
383 to access an Oracle Database instance. Oracle Client versions 18,
384 12 and 11.2 are supported.
385
386 - If your database is on a remote computer, then download the free `Oracle
387 Instant Client
388 <http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__
389 "Basic" or "Basic Light" package for your operating system
390 architecture.
391
392 - Alternatively use the client libraries already available in a
393 locally installed database such as the free `Oracle XE
394 <https://www.oracle.com/database/technologies/appdev/xe.html>`__
395 release.
396
397
398 Oracle Instant Client Zip Files
399 +++++++++++++++++++++++++++++++
400
401 To use cx_Oracle with Oracle Instant Client zip files:
402
403 1. Download an Oracle 18, 12, or 11.2 "Basic" or "Basic Light" zip
404 file: `64-bit
405 <http://www.oracle.com/technetwork/topics/winx64soft-089540.html>`__
406 or `32-bit
407 <http://www.oracle.com/technetwork/topics/winsoft-085727.html>`__, matching your
408 Python architecture.
409
410 2. Unzip the package into a single directory that is accessible to your
411 application, for example ``C:\oracle\instantclient_18_3``.
412
413 3. Set the environment variable ``PATH`` to include the path that you
414 created in step 2. For example, on Windows 7, update ``PATH`` in
415 Control Panel -> System -> Advanced System Settings -> Advanced ->
416 Environment Variables -> System Variables -> PATH. Alternatively
417 use ``SET`` to change your ``PATH`` in each command prompt window
418 before you run python.
419
420 If you have other Oracle software installed, then when you use
421 Python you will need to make sure that the Instant Client
422 directory, e.g. ``C:\oracle\instantclient_18_3``, occurs in
423 ``PATH`` before any other Oracle directories.
424
425 Restart any open command prompt windows.
426
427 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:
428
429 - For Instant Client 18 or 12.2 install `VS 2013 <https://support.microsoft.com/en-us/kb/2977003#bookmark-vs2013>`__
430 - For Instant Client 12.1 install `VS 2010 <https://support.microsoft.com/en-us/kb/2977003#bookmark-vs2010>`__
431 - 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>`__
432
433 5. If you intend to co-locate optional Oracle configuration files such
434 as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with
435 Instant Client, then create a ``network\admin`` subdirectory, for example
436 ``C:\oracle\instantclient_18_3\network\admin``.
437
438 This is the default Oracle configuration directory for executables
439 linked with this Instant Client.
440
441 Alternatively, Oracle configuration files can be put in another,
442 accessible directory. Then set the environment variable
443 ``TNS_ADMIN`` to that directory name.
444
445
446 Local Database or Full Oracle Client
447 ++++++++++++++++++++++++++++++++++++
448
449 cx_Oracle applications can use Oracle Client 18, 12, or 11.2
450 libraries libraries from a local Oracle Database or full Oracle
451 Client.
452
453 The Oracle libraries must be either 32-bit or 64-bit, matching your
454 Python architecture.
455
456 1. Set the environment variable ``PATH`` to include the path that
457 contains OCI.dll, if it is not already set. For example, on Windows
458 7, update ``PATH`` in Control Panel -> System -> Advanced System
459 Settings -> Advanced -> Environment Variables -> System Variables
460 -> PATH.
461
462 Restart any open command prompt windows.
463
464 2. Optional Oracle configuration files such as ``tnsnames.ora``,
465 ``sqlnet.ora`` or ``oraaccess.xml`` can be placed in the
466 ``network/admin`` subdirectory of the Oracle Database software
467 installation.
468
469 Alternatively, Oracle configuration files can be put in another,
470 accessible directory. Then set the environment variable
471 ``TNS_ADMIN`` to that directory name.
472
473
474 Installing cx_Oracle on macOS
475 =============================
476
477 Install Python
478 --------------
479
480 Make sure you are not using the bundled Python. This has restricted
481 entitlements and will fail to load Oracle client libraries. Instead
482 use `Homebrew <https://brew.sh>`__ or `Python.org
483 <https://www.python.org/downloads>`__.
484
485 Install cx_Oracle
486 -----------------
487
488 Use Python's `Pip <http://pip.readthedocs.io/en/latest/installing/>`__
489 package to install cx_Oracle from `PyPI
490 <https://pypi.python.org/pypi/cx_Oracle>`__::
491
492 python -m pip install cx_Oracle --upgrade
493
494 The source will be downloaded, compiled, and the resulting binary
495 installed.
496
497
498 Install Oracle Instant Client
499 -----------------------------
500
501 cx_Oracle requires Oracle Client libraries, which are found in Oracle
502 Instant Client for macOS. These provide the necessary network
503 connectivity allowing cx_Oracle to access an Oracle Database
504 instance. Oracle Client versions 18, 12 and 11.2 are supported.
505
506 To use cx_Oracle with Oracle Instant Client zip files:
507
508 1. Download the Oracle 12 or 11.2 "Basic" or "Basic Light" zip file from `here
509 <http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html>`__.
510 Choose either a 64-bit or 32-bit package, matching your
511 Python architecture.
512
513 2. Unzip the package into a single directory that is accessible to your
514 application. For example::
515
516 mkdir -p /opt/oracle
517 unzip instantclient-basic-macos.x64-12.2.0.1.0.zip
518
519 3. Add links to ``$HOME/lib`` or ``/usr/local/lib`` to enable
520 applications to find the library. For example::
521
522 mkdir ~/lib
523 ln -s /opt/oracle/instantclient_12_2/libclntsh.dylib ~/lib/
524
525 Alternatively, copy the required OCI libraries. For example::
526
527 mkdir ~/lib
528 cp /opt/oracle/instantclient_12_2/{libclntsh.dylib.12.1,libclntshcore.dylib.12.1,libons.dylib,libnnz12.dylib,libociei.dylib} ~/lib/
529
530 For Instant Client 11.2, the OCI libraries must be copied. For example::
531
532 mkdir ~/lib
533 cp /opt/oracle/instantclient_11_2/{libclntsh.dylib.11.1,libnnz11.dylib,libociei.dylib} ~/lib/
534
535 4. If you intend to co-locate optional Oracle configuration files such
536 as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with
537 Instant Client, then create a ``network/admin`` subdirectory. For
538 example::
539
540 mkdir -p /opt/oracle/instantclient_12_2/network/admin
541
542 This is the default Oracle configuration directory for executables
543 linked with this Instant Client.
544
545 Alternatively, Oracle configuration files can be put in another,
546 accessible directory. Then set the environment variable
547 ``TNS_ADMIN`` to that directory name.
548
549
550 Install Using GitHub
551 ====================
552
553 In order to install using the source on GitHub, use the following commands::
554
555 git clone https://github.com/oracle/python-cx_Oracle.git cx_Oracle
556 cd cx_Oracle
557 git submodule init
558 git submodule update
559 python setup.py install
560
561 Note that if you download a source zip file directly from GitHub then
562 you will also need to download an `ODPI-C
563 <https://github.com/oracle/odpi>`__ source zip file and extract it
564 inside the directory called "odpi".
565
566 cx_Oracle source code is also available from oss.oracle.com. This can
567 be cloned with::
568
569 git clone git://oss.oracle.com/git/oracle/python-cx_Oracle.git cx_Oracle
570 cd cx_Oracle
571 git submodule init
572 git submodule update
573
574
575 Install Using Source from PyPI
576 ==============================
577
578 The source package can be downloaded manually from
579 `PyPI <https://pypi.python.org/pypi/cx_Oracle>`__ and extracted, after
580 which the following commands should be run::
581
582 python setup.py build
583 python setup.py install
584
585 Upgrading from cx_Oracle 6
586 ==========================
587
588 Review the `release notes
589 <http://cx-oracle.readthedocs.io/en/latest/releasenotes.html>`__ for
590 deprecations and modify any affected code.
591
592 Upgrading from cx_Oracle 5
593 ==========================
594
595 If you are upgrading from cx_Oracle 5 note these installation changes:
596
597 - When using Oracle Instant Client, you should not set ``ORACLE_HOME``.
598
599 - On Linux, cx_Oracle 6 no longer uses Instant Client RPMs automatically.
600 You must set ``LD_LIBRARY_PATH`` or use ``ldconfig`` to locate the Oracle
601 client library.
602
603 - PyPI no longer allows Windows installers or Linux RPMs to be
604 hosted. Use the supplied cx_Oracle Wheels instead, or use RPMs
605 from Oracle, see :ref:`oraclelinux`.
606
607 Installing cx_Oracle 5.3
608 ========================
609
610 If you require cx_Oracle 5.3, download a Windows installer from `PyPI
611 <https://pypi.python.org/pypi/cx_Oracle>`__ or use ``python -m pip
612 install cx-oracle==5.3`` to install from source.
613
614 Very old versions of cx_Oracle can be found in the files section at
615 `SourceForce <https://sourceforge.net/projects/cx-oracle/files/>`__.
616
617
618 Troubleshooting
619 ===============
620
621 If installation fails:
622
623 - Use option ``-v`` with pip. Review your output and logs. Try to install
624 using a different method. **Google anything that looks like an error.**
625 Try some potential solutions.
626
627 - Was there a network connection error? Do you need to see the environment
628 variables ``http_proxy`` and/or ``https_proxy``?
629
630 - Do you get the error "``No module named pip``"? The pip module is builtin
631 to Python from version 2.7.9 but is sometimes removed by the OS. Use the
632 venv module (builtin to Python 3.x) or virtualenv module (Python 2.x)
633 instead.
634
635 - Do you get the error "``fatal error: dpi.h: No such file or directory``"
636 when building from source code? Ensure that your source installation has a
637 subdirectory called "odpi" containing files. If missing, review the
638 section on `Install Using GitHub`_.
639
640 If using cx_Oracle fails:
641
642 - Do you get the error "``DPI-1047: Oracle Client library cannot be
643 loaded``"?
644
645 - Check that Python, cx_Oracle and your Oracle Client libraries
646 are all 64-bit or all 32-bit. The ``DPI-1047`` message will
647 tell you whether the 64-bit or 32-bit Oracle Client is needed
648 for your Python.
649 - On Windows, restart your command prompt and use ``set PATH``
650 to check the environment variable has the correct Oracle
651 Client listed before any other Oracle directories.
652 - On Windows, use the ``DIR`` command on the directory set in
653 ``PATH``. Verify that ``OCI.DLL`` exists there.
654 - On Windows, check that the correct `Windows Redistributables
655 <https://oracle.github.io/odpi/doc/installation.html#windows>`__ have
656 been installed.
657 - On Linux, check the ``LD_LIBRARY_PATH`` environment variable
658 contains the Oracle Client library directory.
659 - On macOS, make sure Oracle Instant Client is in ``~/lib`` or
660 ``/usr/local/lib`` and that you are not using the bundled Python (use
661 `Homebrew <https://brew.sh>`__ or `Python.org
662 <https://www.python.org/downloads>`__ instead).
663
664 - If you have both Python 2 and 3 installed, make sure you are
665 using the correct python and pip (or python3 and pip3)
666 executables.
0 :orphan:
1
02 .. _license:
13
24 *******
79
810 .. centered:: **LICENSE AGREEMENT FOR CX_ORACLE**
911
10 Copyright |copy| 2016, 2018, Oracle and/or its affiliates. All rights reserved.
12 Copyright |copy| 2016, 2020, Oracle and/or its affiliates. All rights reserved.
1113
1214 Copyright |copy| 2007-2015, Anthony Tuininga. All rights reserved.
1315
+0
-89
doc/src/lob.rst less more
0 .. _lobobj:
1
2 ***********
3 LOB Objects
4 ***********
5
6 .. note::
7
8 This object is an extension the DB API. It is returned whenever Oracle
9 :data:`CLOB`, :data:`BLOB` and :data:`BFILE` columns are fetched.
10
11
12 .. method:: LOB.close()
13
14 Close the LOB. Call this when writing is completed so that the indexes
15 associated with the LOB can be updated -- but only if :meth:`~LOB.open()`
16 was called first.
17
18
19 .. method:: LOB.fileexists()
20
21 Return a boolean indicating if the file referenced by the BFILE type LOB
22 exists.
23
24
25 .. method:: LOB.getchunksize()
26
27 Return the chunk size for the internal LOB. Reading and writing to the LOB
28 in chunks of multiples of this size will improve performance.
29
30
31 .. method:: LOB.getfilename()
32
33 Return a two-tuple consisting of the directory alias and file name for a
34 BFILE type LOB.
35
36
37 .. method:: LOB.isopen()
38
39 Return a boolean indicating if the LOB has been opened using the method
40 :meth:`~LOB.open()`.
41
42
43 .. method:: LOB.open()
44
45 Open the LOB for writing. This will improve performance when writing to a
46 LOB in chunks and there are functional or extensible indexes associated
47 with the LOB. If this method is not called, each write will perform an open
48 internally followed by a close after the write has been completed.
49
50
51 .. method:: LOB.read([offset=1, [amount]])
52
53 Return a portion (or all) of the data in the LOB object. Note that the
54 amount and offset are in bytes for BLOB and BFILE type LOBs and in UCS-2
55 code points for CLOB and NCLOB type LOBs. UCS-2 code points are equivalent
56 to characters for all but supplemental characters. If supplemental
57 characters are in the LOB, the offset and amount will have to be chosen
58 carefully to avoid splitting a character.
59
60
61 .. method:: LOB.setfilename(dirAlias, name)
62
63 Set the directory alias and name of the BFILE type LOB.
64
65
66 .. method:: LOB.size()
67
68 Returns the size of the data in the LOB object. For BLOB and BFILE type
69 LOBs this is the number of bytes. For CLOB and NCLOB type LOBs this is the
70 number of UCS-2 code points. UCS-2 code points are equivalent to characters
71 for all but supplemental characters.
72
73
74 .. method:: LOB.trim([newSize=0])
75
76 Trim the LOB to the new size.
77
78
79 .. method:: LOB.write(data, [offset=1])
80
81 Write the data to the LOB object at the given offset. The offset is in
82 bytes for BLOB type LOBs and in UCS-2 code points for CLOB and NCLOB type
83 LOBs. UCS-2 code points are equivalent to characters for all but
84 supplemental characters. If supplemental characters are in the LOB, the
85 offset will have to be chosen carefully to avoid splitting a character.
86 Note that if you want to make the LOB value smaller, you must use the
87 :meth:`~LOB.trim()` function.
88
+0
-1288
doc/src/module.rst less more
0 .. module:: cx_Oracle
1
2 .. _module:
3
4 ****************
5 Module Interface
6 ****************
7
8 .. data:: __future__
9
10 Special object which contains attributes which control the behavior of
11 cx_Oracle, allowing for opting in for new features. No attributes are
12 currently supported so all attributes will silently ignore being set and
13 will always appear to have the value None.
14
15 .. note::
16
17 This method is an extension to the DB API definition.
18
19 .. versionadded:: 6.2
20
21
22 .. function:: Binary(string)
23
24 Construct an object holding a binary (long) string value.
25
26
27 .. function:: clientversion()
28
29 Return the version of the client library being used as a 5-tuple. The five
30 values are the major version, minor version, update number, patch number
31 and port update number.
32
33 .. note::
34
35 This method is an extension to the DB API definition.
36
37
38 .. function:: Connection(user=None, password=None, dsn=None, mode=None, \
39 handle=None, pool=None, threaded=False, events=False, cclass=None, \
40 purity=None, newpassword=None, encoding=None, nencoding=None, \
41 edition=None, appcontext=[], tag=None, matchanytag=False, \
42 shardingkey=[], supershardingkey=[])
43 connect(user=None, password=None, dsn=None, mode=None, handle=None, \
44 pool=None, threaded=False, events=False, cclass=None, purity=None, \
45 newpassword=None, encoding=None, nencoding=None, edition=None, \
46 appcontext=[], tag=None, matchanytag=None, shardingkey=[], \
47 supershardingkey=[])
48
49 Constructor for creating a connection to the database. Return a
50 :ref:`connection object <connobj>`. All parameters are optional and can be
51 specified as keyword parameters.
52
53 The dsn (data source name) is the TNS entry (from the Oracle names server
54 or tnsnames.ora file) or is a string like the one returned from
55 :meth:`~cx_Oracle.makedsn()`. If only one parameter is passed, a connect
56 string is assumed which is to be of the format ``user/password@dsn``, the
57 same format accepted by Oracle applications such as SQL\*Plus.
58
59 If the mode is specified, it must be one of :data:`~cx_Oracle.SYSDBA`,
60 :data:`~cx_Oracle.SYSASM`, :data:`~cx_Oracle.SYSOPER`,
61 :data:`~cx_Oracle.SYSBKP`, :data:`~cx_Oracle.SYSDGD`,
62 :data:`~cx_Oracle.SYSKMT` or :data:`~cx_Oracle.SYSRAC` which are defined
63 at the module level; otherwise, it defaults to the normal mode of
64 connecting.
65
66 If the handle is specified, it must be of type OCISvcCtx\* and is only of
67 use when embedding Python in an application (like PowerBuilder) which has
68 already made the connection.
69
70 The pool parameter is expected to be a
71 :ref:`session pool object <sesspool>` and the use of this parameter is the
72 equivalent of calling :meth:`SessionPool.acquire()`. Parameters not
73 acecpted by that method are ignored.
74
75 The threaded parameter is expected to be a boolean expression which
76 indicates whether or not Oracle should wrap accesses to connections with a
77 mutex. Doing so in single threaded applications imposes a performance
78 penalty of about 10-15% which is why the default is False.
79
80 The events parameter is expected to be a boolean expression which indicates
81 whether or not to initialize Oracle in events mode. This is required for
82 continuous query notification and high availablity event notifications.
83
84 The cclass parameter is expected to be a string and defines the connection
85 class for database resident connection pooling (DRCP).
86
87 The purity parameter is expected to be one of
88 :data:`~cx_Oracle.ATTR_PURITY_NEW`, :data:`~cx_Oracle.ATTR_PURITY_SELF`, or
89 :data:`~cx_Oracle.ATTR_PURITY_DEFAULT`.
90
91 The newpassword parameter is expected to be a string if specified and sets
92 the password for the logon during the connection process.
93
94 The encoding parameter is expected to be a string if specified and sets the
95 encoding to use for regular database strings. If not specified, the
96 environment variable NLS_LANG is used. If the environment variable NLS_LANG
97 is not set, ASCII is used.
98
99 The nencoding parameter is expected to be a string if specified and sets
100 the encoding to use for national character set database strings. If not
101 specified, the environment variable NLS_NCHAR is used. If the environment
102 variable NLS_NCHAR is not used, the environment variable NLS_LANG is used
103 instead, and if the environment variable NLS_LANG is not set, ASCII is
104 used.
105
106 The edition parameter is expected to be a string if specified and sets the
107 edition to use for the session. It is only relevant if both the client and
108 the database are at least Oracle Database 11.2. If this parameter is used
109 with the cclass parameter the exception "DPI-1058: edition not supported
110 with connection class" will be raised.
111
112 The appcontext parameter is expected to be a list of 3-tuples, if specified,
113 and sets the application context for the connection. Application context
114 is available in the database by using the sys_context() PL/SQL method and
115 can be used within a logon trigger as well as any other PL/SQL procedures.
116 Each entry in the list is expected to contain three strings: the namespace,
117 the name and the value.
118
119 The tag parameter, if specified, is expected to be a string and will limit
120 the sessions that can be returned from a session pool unless the
121 matchanytag parameter is set to True. In that case sessions with the
122 specified tag will be preferred over others, but if no such sessions are
123 available a session with a different tag may be returned instead. In any
124 case, untagged sessions will always be returned if no sessions with the
125 specified tag are available. Sessions are tagged when they are
126 :meth:`released <SessionPool.release>` back to the pool.
127
128 The shardingkey and supershardingkey parameters, if specified, are expected
129 to be a sequence of values which will be used to identify the database
130 shard to connect to. Currently only strings are supported for the key
131 values.
132
133
134 .. function:: Cursor(connection)
135
136 Constructor for creating a cursor. Return a new
137 :ref:`cursor object <cursorobj>` using the connection.
138
139 .. note::
140
141 This method is an extension to the DB API definition.
142
143
144 .. function:: Date(year, month, day)
145
146 Construct an object holding a date value.
147
148
149 .. function:: DateFromTicks(ticks)
150
151 Construct an object holding a date value from the given ticks value (number
152 of seconds since the epoch; see the documentation of the standard Python
153 time module for details).
154
155
156 .. function:: makedsn(host, port, sid=None, service_name=None, region=None, \
157 sharding_key=None, super_sharding_key=None)
158
159 Return a string suitable for use as the dsn parameter for
160 :meth:`~cx_Oracle.connect()`. This string is identical to the strings that
161 are defined by the Oracle names server or defined in the tnsnames.ora file.
162
163 .. note::
164
165 This method is an extension to the DB API definition.
166
167
168 .. function:: SessionPool(user=None, password=None, dsn=None, min=1, max=2, \
169 increment=1, connectiontype=cx_Oracle.Connection, threaded=False, \
170 getmode=cx_Oracle.SPOOL_ATTRVAL_NOWAIT, events=False, \
171 homogeneous=True, externalauth=False, encoding=None, nencoding=None, \
172 edition=None, timeout=0, waitTimeout=0, maxLifetimeSession=0, \
173 sessionCallback=None)
174
175 Create and return a :ref:`session pool object <sesspool>`. This
176 allows for very fast connections to the database and is of primary use in a
177 server where the same connection is being made multiple times in rapid
178 succession (a web server, for example).
179
180 If the connection type is specified, all calls to
181 :meth:`~SessionPool.acquire()` will create connection objects of that type,
182 rather than the base type defined at the module level.
183
184 The threaded parameter is expected to be a boolean expression which
185 indicates whether Oracle should wrap accesses to connections with a mutex.
186 Doing so in single threaded applications imposes a performance penalty of
187 about 10-15% which is why the default is False.
188
189 The events parameter is expected to be a boolean expression which indicates
190 whether or not to initialize Oracle in events mode. This is required for
191 continuous query notification and high availability event notifications.
192
193 The homogeneous parameter is expected to be a boolean expression which
194 indicates whether or not to create a homogeneous pool. A homogeneous pool
195 requires that all connections in the pool use the same credentials. As such
196 proxy authentication and external authentication is not possible with a
197 homogeneous pool.
198
199 The externalauth parameter is expected to be a boolean expression which
200 indicates whether or not external authentication should be used. External
201 authentication implies that something other than the database is
202 authenticating the user to the database. This includes the use of operating
203 system authentication and Oracle wallets.
204
205 The encoding parameter is expected to be a string, if specified, and sets
206 the encoding to use for regular database strings. If not specified, the
207 environment variable NLS_LANG is used. If the environment variable NLS_LANG
208 is not set, ASCII is used.
209
210 The nencoding parameter is expected to be a string, if specified, and sets
211 the encoding to use for national character set database strings. If not
212 specified, the environment variable NLS_NCHAR is used. If the environment
213 variable NLS_NCHAR is not used, the environment variable NLS_LANG is used
214 instead, and if the environment variable NLS_LANG is not set, ASCII is
215 used.
216
217 The edition parameter is expected to be a string, if specified, and sets
218 the edition to use for the sessions in the pool. It is only relevant if
219 both the client and the server are at least Oracle Database 11.2.
220
221 The timeout parameter is expected to be an integer, if specified, and sets
222 the length of time (in seconds) after which idle sessions in the pool are
223 terminated. Note that termination only occurs when the pool is accessed.
224 The default value of 0 means that no idle sessions are terminated.
225
226 The waitTimeout parameter is expected to be an integer, if specified, and
227 sets the length of time (in milliseconds) that the caller should wait for
228 a session to become available in the pool before returning with an error.
229 This value is only used if the getmode parameter is set to the value
230 :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`.
231
232 The maxLifetimeSession parameter is expected to be an integer, if
233 specified, and sets the maximum length of time (in seconds) a pooled
234 session may exist. Sessions that are in use will not be closed. They become
235 candidates for termination only when they are released back to the pool and
236 have existed for longer than maxLifetimeSession seconds. Note that
237 termination only occurs when the pool is accessed. The default value is 0
238 which means that there is no maximum length of time that a pooled session
239 may exist.
240
241 The sessionCallback parameter is expected to be either a string or a
242 callable. If the parameter is a string, this refers to a PL/SQL procedure
243 that will be called when :func:`SessionPool.acquire()` requests a tag and
244 that tag does not match the connection's actual tag. Support for the PL/SQL
245 procedure requires Oracle Client libraries 12.2 or later. See the
246 `OCI documentation <https://www.oracle.com/pls/topic/lookup?
247 ctx=dblatest&id=GUID-B853A020-752F-494A-8D88-D0396EF57177>`__ for more
248 information. If the sessionCallback parameter is a callable, however, it
249 will be called when a newly created connection is returned from the pool
250 or when a tag is requested and that tag does not match the connection's
251 actual tag. The callable will be invoked with the connection and the
252 requested tag as its only parameters.
253
254 .. note::
255
256 This method is an extension to the DB API definition.
257
258
259 .. function:: Time(hour, minute, second)
260
261 Construct an object holding a time value.
262
263 .. note::
264
265 The time only data type is not supported by Oracle. Calling this
266 function will raise a NotSupportedError exception.
267
268
269
270 .. function:: TimeFromTicks(ticks)
271
272 Construct an object holding a time value from the given ticks value (number
273 of seconds since the epoch; see the documentation of the standard Python
274 time module for details).
275
276 .. note::
277
278 The time only data type is not supported by Oracle. Calling this
279 function will raise a NotSupportedError exception.
280
281
282 .. function:: Timestamp(year, month, day, hour, minute, second)
283
284 Construct an object holding a time stamp value.
285
286
287 .. function:: TimestampFromTicks(ticks)
288
289 Construct an object holding a time stamp value from the given ticks value
290 (number of seconds since the epoch; see the documentation of the standard
291 Python time module for details).
292
293
294
295 .. _constants:
296
297 Constants
298 =========
299
300 General
301 -------
302
303 .. data:: apilevel
304
305 String constant stating the supported DB API level. Currently '2.0'.
306
307
308 .. data:: buildtime
309
310 String constant stating the time when the binary was built.
311
312 .. note::
313
314 This constant is an extension to the DB API definition.
315
316
317 .. data:: paramstyle
318
319 String constant stating the type of parameter marker formatting expected by
320 the interface. Currently 'named' as in 'where name = :name'.
321
322
323 .. data:: threadsafety
324
325 Integer constant stating the level of thread safety that the interface
326 supports. Currently 2, which means that threads may share the module and
327 connections, but not cursors. Sharing means that a thread may use a
328 resource without wrapping it using a mutex semaphore to implement resource
329 locking.
330
331 Note that in order to make use of multiple threads in a program which
332 intends to connect and disconnect in different threads, the threaded
333 parameter to :meth:`connect()` or :meth:`SessionPool()` must be true.
334
335
336 .. data:: version
337 .. data:: __version__
338
339 String constant stating the version of the module. Currently '|release|'.
340
341 .. note::
342
343 This attribute is an extension to the DB API definition.
344
345
346 Advanced Queuing: Delivery Modes
347 --------------------------------
348
349 These constants are extensions to the DB API definition. They are possible
350 values for the :attr:`~DeqOptions.deliverymode` attribute of the
351 :ref:`dequeue options object <deqoptions>` passed as the options parameter to
352 the :meth:`Connection.deq()` method as well as the
353 :attr:`~EnqOptions.deliverymode` attribute of the
354 :ref:`enqueue options object <enqoptions>` passed as the options parameter to
355 the :meth:`Connection.enq()` method. They are also possible values for the
356 :attr:`~MessageProperties.deliverymode` attribute of the
357 :ref:`message properties object <msgproperties>` passed as the msgproperties
358 parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()` methods.
359
360
361 .. data:: MSG_BUFFERED
362
363 This constant is used to specify that enqueue/dequeue operations should
364 enqueue or dequeue buffered messages.
365
366
367 .. data:: MSG_PERSISTENT
368
369 This constant is used to specify that enqueue/dequeue operations should
370 enqueue or dequeue persistent messages. This is the default value.
371
372
373 .. data:: MSG_PERSISTENT_OR_BUFFERED
374
375 This constant is used to specify that dequeue operations should dequeue
376 either persistent or buffered messages.
377
378
379 Advanced Queuing: Dequeue Modes
380 -------------------------------
381
382 These constants are extensions to the DB API definition. They are possible
383 values for the :attr:`~DeqOptions.mode` attribute of the
384 :ref:`dequeue options object <deqoptions>`. This object is the options
385 parameter for the :meth:`Connection.deq()` method.
386
387
388 .. data:: DEQ_BROWSE
389
390 This constant is used to specify that dequeue should read the message
391 without acquiring any lock on the message (eqivalent to a select
392 statement).
393
394
395 .. data:: DEQ_LOCKED
396
397 This constant is used to specify that dequeue should read and obtain a
398 write lock on the message for the duration of the transaction (equivalent
399 to a select for update statement).
400
401
402 .. data:: DEQ_REMOVE
403
404 This constant is used to specify that dequeue should read the message and
405 update or delete it. This is the default value.
406
407
408 .. data:: DEQ_REMOVE_NODATA
409
410 This constant is used to specify that dequeue should confirm receipt of the
411 message but not deliver the actual message content.
412
413
414 Advanced Queuing: Dequeue Navigation Modes
415 ------------------------------------------
416
417 These constants are extensions to the DB API definition. They are possible
418 values for the :attr:`~DeqOptions.navigation` attribute of the
419 :ref:`dequeue options object <deqoptions>`. This object is the options
420 parameter for the :meth:`Connection.deq()` method.
421
422
423 .. data:: DEQ_FIRST_MSG
424
425 This constant is used to specify that dequeue should retrieve the first
426 available message that matches the search criteria. This resets the
427 position to the beginning of the queue.
428
429
430 .. data:: DEQ_NEXT_MSG
431
432 This constant is used to specify that dequeue should retrieve the next
433 available message that matches the search criteria. If the previous message
434 belongs to a message group, AQ retrieves the next available message that
435 matches the search criteria and belongs to the message group. This is the
436 default.
437
438
439 .. data:: DEQ_NEXT_TRANSACTION
440
441 This constant is used to specify that dequeue should skip the remainder of
442 the transaction group and retrieve the first message of the next
443 transaction group. This option can only be used if message grouping is
444 enabled for the current queue.
445
446
447 Advanced Queuing: Dequeue Visibility Modes
448 ------------------------------------------
449
450 These constants are extensions to the DB API definition. They are possible
451 values for the :attr:`~DeqOptions.visibility` attribute of the
452 :ref:`dequeue options object <deqoptions>`. This object is the options
453 parameter for the :meth:`Connection.deq()` method.
454
455
456 .. data:: DEQ_IMMEDIATE
457
458 This constant is used to specify that dequeue should perform its work as
459 part of an independent transaction.
460
461
462 .. data:: DEQ_ON_COMMIT
463
464 This constant is used to specify that dequeue should be part of the current
465 transaction. This is the default value.
466
467
468 Advanced Queuing: Dequeue Wait Modes
469 ------------------------------------
470
471 These constants are extensions to the DB API definition. They are possible
472 values for the :attr:`~DeqOptions.wait` attribute of the
473 :ref:`dequeue options object <deqoptions>`. This object is the options
474 parameter for the :meth:`Connection.deq()` method.
475
476
477 .. data:: DEQ_NO_WAIT
478
479 This constant is used to specify that dequeue not wait for messages to be
480 available for dequeuing.
481
482
483 .. data:: DEQ_WAIT_FOREVER
484
485 This constant is used to specify that dequeue should wait forever for
486 messages to be available for dequeuing. This is the default value.
487
488
489 Advanced Queuing: Enqueue Visibility Modes
490 ------------------------------------------
491
492 These constants are extensions to the DB API definition. They are possible
493 values for the :attr:`~EnqOptions.visibility` attribute of the
494 :ref:`enqueue options object <enqoptions>`. This object is the options
495 parameter for the :meth:`Connection.enq()` method.
496
497
498 .. data:: ENQ_IMMEDIATE
499
500 This constant is used to specify that enqueue should perform its work as
501 part of an independent transaction.
502
503
504 .. data:: ENQ_ON_COMMIT
505
506 This constant is used to specify that enqueue should be part of the current
507 transaction. This is the default value.
508
509
510 Advanced Queuing: Message States
511 --------------------------------
512
513 These constants are extensions to the DB API definition. They are possible
514 values for the :attr:`~MessageProperties.state` attribute of the
515 :ref:`message properties object <msgproperties>`. This object is the
516 msgproperties parameter for the :meth:`Connection.deq()` and
517 :meth:`Connection.enq()` methods.
518
519
520 .. data:: MSG_EXPIRED
521
522 This constant is used to specify that the message has been moved to the
523 exception queue.
524
525
526 .. data:: MSG_PROCESSED
527
528 This constant is used to specify that the message has been processed and
529 has been retained.
530
531
532 .. data:: MSG_READY
533
534 This constant is used to specify that the message is ready to be processed.
535
536
537 .. data:: MSG_WAITING
538
539 This constant is used to specify that the message delay has not yet been
540 reached.
541
542
543 Advanced Queuing: Other
544 -----------------------
545
546 These constants are extensions to the DB API definition. They are special
547 constants used in advanced queuing.
548
549
550 .. data:: MSG_NO_DELAY
551
552 This constant is a possible value for the :attr:`~MessageProperties.delay`
553 attribute of the :ref:`message properties object <msgproperties>` passed
554 as the msgproperties parameter to the :meth:`Connection.deq()` and
555 :meth:`Connection.enq()` methods. It specifies that no delay should be
556 imposed and the message should be immediately available for dequeuing. This
557 is also the default value.
558
559
560 .. data:: MSG_NO_EXPIRATION
561
562 This constant is a possible value for the
563 :attr:`~MessageProperties.expiration` attribute of the
564 :ref:`message properties object <msgproperties>` passed as the msgproperties
565 parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()`
566 methods. It specifies that the message never expires. This is also the
567 default value.
568
569
570 Connection Authorization Modes
571 ------------------------------
572
573 These constants are extensions to the DB API definition. They are possible
574 values for the mode parameter of the :meth:`connect()` method.
575
576
577 .. data:: PRELIM_AUTH
578
579 This constant is used to specify that preliminary authentication is to be
580 used. This is needed for performing database startup and shutdown.
581
582
583 .. data:: SYSASM
584
585 This constant is used to specify that SYSASM access is to be acquired.
586
587
588 .. data:: SYSBKP
589
590 This constant is used to specify that SYSBACKUP access is to be acquired.
591
592
593 .. data:: SYSDBA
594
595 This constant is used to specify that SYSDBA access is to be acquired.
596
597
598 .. data:: SYSDGD
599
600 This constant is used to specify that SYSDG access is to be acquired.
601
602
603 .. data:: SYSKMT
604
605 This constant is used to specify that SYSKM access is to be acquired.
606
607
608 .. data:: SYSOPER
609
610 This constant is used to specify that SYSOPER access is to be acquired.
611
612
613 .. data:: SYSRAC
614
615 This constant is used to specify that SYSRAC access is to be acquired.
616
617
618 Database Shutdown Modes
619 -----------------------
620
621 These constants are extensions to the DB API definition. They are possible
622 values for the mode parameter of the :meth:`Connection.shutdown()` method.
623
624
625 .. data:: DBSHUTDOWN_ABORT
626
627 This constant is used to specify that the caller should not wait for
628 current processing to complete or for users to disconnect from the
629 database. This should only be used in unusual circumstances since database
630 recovery may be necessary upon next startup.
631
632
633 .. data:: DBSHUTDOWN_FINAL
634
635 This constant is used to specify that the instance can be truly halted.
636 This should only be done after the database has been shutdown with one of
637 the other modes (except abort) and the database has been closed and
638 dismounted using the appropriate SQL commands.
639
640
641 .. data:: DBSHUTDOWN_IMMEDIATE
642
643 This constant is used to specify that all uncommitted transactions should
644 be rolled back and any connected users should be disconnected.
645
646
647 .. data:: DBSHUTDOWN_TRANSACTIONAL
648
649 This constant is used to specify that further connections to the database
650 should be prohibited and no new transactions should be allowed. It then
651 waits for all active transactions to complete.
652
653
654 .. data:: DBSHUTDOWN_TRANSACTIONAL_LOCAL
655
656 This constant is used to specify that further connections to the database
657 should be prohibited and no new transactions should be allowed. It then
658 waits for only local active transactions to complete.
659
660
661 Event Types
662 -----------
663
664 These constants are extensions to the DB API definition. They are possible
665 values for the :attr:`Message.type` attribute of the messages that are sent
666 for subscriptions created by the :meth:`Connection.subscribe()` method.
667
668
669 .. data:: EVENT_AQ
670
671 This constant is used to specify that one or more messages are available
672 for dequeuing on the queue specified when the subscription was created.
673
674
675 .. data:: EVENT_DEREG
676
677 This constant is used to specify that the subscription has been
678 deregistered and no further notifications will be sent.
679
680
681 .. data:: EVENT_NONE
682
683 This constant is used to specify no information is available about the
684 event.
685
686
687 .. data:: EVENT_OBJCHANGE
688
689 This constant is used to specify that a database change has taken place on
690 a table registered with the :meth:`Subscription.registerquery()` method.
691
692
693 .. data:: EVENT_QUERYCHANGE
694
695 This constant is used to specify that the result set of a query registered
696 with the :meth:`Subscription.registerquery()` method has been changed.
697
698
699 .. data:: EVENT_SHUTDOWN
700
701 This constant is used to specify that the instance is in the process of
702 being shut down.
703
704
705 .. data:: EVENT_SHUTDOWN_ANY
706
707 This constant is used to specify that any instance (when running RAC) is in
708 the process of being shut down.
709
710
711 .. data:: EVENT_STARTUP
712
713 This constant is used to specify that the instance is in the process of
714 being started up.
715
716
717 Operation Codes
718 ---------------
719
720 These constants are extensions to the DB API definition. They are possible
721 values for the operations parameter for the :meth:`Connection.subscribe()`
722 method. One or more of these values can be OR'ed together. These values are
723 also used by the :attr:`MessageTable.operation` or
724 :attr:`MessageQuery.operation` attributes of the messages that are sent.
725
726
727 .. data:: OPCODE_ALLOPS
728
729 This constant is used to specify that messages should be sent for all
730 operations.
731
732
733 .. data:: OPCODE_ALLROWS
734
735 This constant is used to specify that the table or query has been
736 completely invalidated.
737
738
739 .. data:: OPCODE_ALTER
740
741 This constant is used to specify that messages should be sent when a
742 registered table has been altered in some fashion by DDL, or that the
743 message identifies a table that has been altered.
744
745
746 .. data:: OPCODE_DELETE
747
748 This constant is used to specify that messages should be sent when data is
749 deleted, or that the message identifies a row that has been deleted.
750
751
752 .. data:: OPCODE_DROP
753
754 This constant is used to specify that messages should be sent when a
755 registered table has been dropped, or that the message identifies a table
756 that has been dropped.
757
758
759 .. data:: OPCODE_INSERT
760
761 This constant is used to specify that messages should be sent when data is
762 inserted, or that the message identifies a row that has been inserted.
763
764
765 .. data:: OPCODE_UPDATE
766
767 This constant is used to specify that messages should be sent when data is
768 updated, or that the message identifies a row that has been updated.
769
770
771 Session Pool Get Modes
772 ----------------------
773
774 These constants are extensions to the DB API definition. They are possible
775 values for the getmode parameter of the :meth:`SessionPool()` method.
776
777
778 .. data:: SPOOL_ATTRVAL_FORCEGET
779
780 This constant is used to specify that a new connection will be returned if
781 there are no free sessions available in the pool.
782
783
784 .. data:: SPOOL_ATTRVAL_NOWAIT
785
786 This constant is used to specify that an exception should be raised if
787 there are no free sessions available in the pool. This is the default
788 value.
789
790
791 .. data:: SPOOL_ATTRVAL_WAIT
792
793 This constant is used to specify that the caller should wait until a
794 session is available if there are no free sessions available in the pool.
795
796
797 .. data:: SPOOL_ATTRVAL_TIMEDWAIT
798
799 This constant is used to specify that the caller should wait for a period
800 of time (defined by the waitTimeout parameter) for a session to become
801 available before returning with an error.
802
803
804 Session Pool Purity
805 -------------------
806
807 These constants are extensions to the DB API definition. They are possible
808 values for the purity parameter of the :meth:`connect()` method, which is used
809 in database resident connection pooling (DRCP).
810
811
812 .. data:: ATTR_PURITY_DEFAULT
813
814 This constant is used to specify that the purity of the session is the
815 default value identified by Oracle (see Oracle's documentation for more
816 information). This is the default value.
817
818
819 .. data:: ATTR_PURITY_NEW
820
821 This constant is used to specify that the session acquired from the pool
822 should be new and not have any prior session state.
823
824
825 .. data:: ATTR_PURITY_SELF
826
827 This constant is used to specify that the session acquired from the pool
828 need not be new and may have prior session state.
829
830
831 Subscription Grouping Classes
832 -----------------------------
833
834 These constants are extensions to the DB API definition. They are possible
835 values for the groupingClass parameter of the :meth:`Connection.subscribe()`
836 method.
837
838 .. data:: SUBSCR_GROUPING_CLASS_TIME
839
840 This constant is used to specify that events are to be grouped by the
841 period of time in which they are received.
842
843
844 Subscription Grouping Types
845 ---------------------------
846
847 These constants are extensions to the DB API definition. They are possible
848 values for the groupingType parameter of the :meth:`Connection.subscribe()`
849 method.
850
851 .. data:: SUBSCR_GROUPING_TYPE_SUMMARY
852
853 This constant is used to specify that when events are grouped a summary of
854 the events should be sent instead of the individual events. This is the
855 default value.
856
857 .. data:: SUBSCR_GROUPING_TYPE_LAST
858
859 This constant is used to specify that when events are grouped the last
860 event that makes up the group should be sent instead of the individual
861 events.
862
863
864 Subscription Namespaces
865 -----------------------
866
867 These constants are extensions to the DB API definition. They are possible
868 values for the namespace parameter of the :meth:`Connection.subscribe()`
869 method.
870
871 .. data:: SUBSCR_NAMESPACE_AQ
872
873 This constant is used to specify that notifications should be sent when a
874 queue has messages available to dequeue.
875
876 .. data:: SUBSCR_NAMESPACE_DBCHANGE
877
878 This constant is used to specify that database change notification or query
879 change notification messages are to be sent. This is the default value.
880
881
882 Subscription Protocols
883 ----------------------
884
885 These constants are extensions to the DB API definition. They are possible
886 values for the protocol parameter of the :meth:`Connection.subscribe()` method.
887
888
889 .. data:: SUBSCR_PROTO_HTTP
890
891 This constant is used to specify that notifications will be sent to an
892 HTTP URL when a message is generated. This value is currently not
893 supported.
894
895
896 .. data:: SUBSCR_PROTO_MAIL
897
898 This constant is used to specify that notifications will be sent to an
899 e-mail address when a message is generated. This value is currently not
900 supported.
901
902
903 .. data:: SUBSCR_PROTO_OCI
904
905 This constant is used to specify that notifications will be sent to the
906 callback routine identified when the subscription was created. It is the
907 default value and the only value currently supported.
908
909
910 .. data:: SUBSCR_PROTO_SERVER
911
912 This constant is used to specify that notifications will be sent to a
913 PL/SQL procedure when a message is generated. This value is currently not
914 supported.
915
916
917 Subscription Quality of Service
918 -------------------------------
919
920 These constants are extensions to the DB API definition. They are possible
921 values for the qos parameter of the :meth:`Connection.subscribe()` method. One
922 or more of these values can be OR'ed together.
923
924 .. data:: SUBSCR_QOS_BEST_EFFORT
925
926 This constant is used to specify that best effort filtering for query
927 result set changes is acceptable. False positive notifications may be
928 received. This behaviour may be suitable for caching applications.
929
930
931 .. data:: SUBSCR_QOS_DEREG_NFY
932
933 This constant is used to specify that the subscription should be
934 automatically unregistered after the first notification is received.
935
936
937 .. data:: SUBSCR_QOS_QUERY
938
939 This constant is used to specify that notifications should be sent if the
940 result set of the registered query changes. By default no false positive
941 notifications will be generated.
942
943
944 .. data:: SUBSCR_QOS_RELIABLE
945
946 This constant is used to specify that notifications should not be lost in
947 the event of database failure.
948
949
950 .. data:: SUBSCR_QOS_ROWIDS
951
952 This constant is used to specify that the rowids of the inserted, updated
953 or deleted rows should be included in the message objects that are sent.
954
955
956 Types
957 =====
958
959 .. data:: BINARY
960
961 This type object is used to describe columns in a database that contain
962 binary data. In Oracle this is RAW columns.
963
964
965 .. data:: BFILE
966
967 This type object is used to describe columns in a database that are BFILEs.
968
969 .. note::
970
971 This type is an extension to the DB API definition.
972
973
974 .. data:: BLOB
975
976 This type object is used to describe columns in a database that are BLOBs.
977
978 .. note::
979
980 This type is an extension to the DB API definition.
981
982
983 .. data:: BOOLEAN
984
985 This type object is used to represent PL/SQL booleans.
986
987 .. versionadded:: 5.2.1
988
989 .. note::
990
991 This type is an extension to the DB API definition. It is only
992 available in Oracle 12.1 and higher and only within PL/SQL. It cannot
993 be used in columns.
994
995
996 .. data:: CLOB
997
998 This type object is used to describe columns in a database that are CLOBs.
999
1000 .. note::
1001
1002 This type is an extension to the DB API definition.
1003
1004
1005 .. data:: CURSOR
1006
1007 This type object is used to describe columns in a database that are cursors
1008 (in PL/SQL these are known as ref cursors).
1009
1010 .. note::
1011
1012 This type is an extension to the DB API definition.
1013
1014
1015 .. data:: DATETIME
1016
1017 This type object is used to describe columns in a database that are dates.
1018
1019
1020 .. data:: FIXED_CHAR
1021
1022 This type object is used to describe columns in a database that are fixed
1023 length strings (in Oracle these is CHAR columns); these behave differently
1024 in Oracle than varchar2 so they are differentiated here even though the DB
1025 API does not differentiate them.
1026
1027 .. note::
1028
1029 This attribute is an extension to the DB API definition.
1030
1031
1032 .. data:: FIXED_NCHAR
1033
1034 This type object is used to describe columns in a database that are NCHAR
1035 columns in Oracle; these behave differently in Oracle than nvarchar2 so
1036 they are differentiated here even though the DB API does not differentiate
1037 them.
1038
1039 .. note::
1040
1041 This type is an extension to the DB API definition.
1042
1043
1044 .. data:: INTERVAL
1045
1046 This type object is used to describe columns in a database that are of type
1047 interval day to second.
1048
1049 .. note::
1050
1051 This type is an extension to the DB API definition.
1052
1053
1054 .. data:: LOB
1055
1056 This type object is the Python type of :data:`BLOB` and :data:`CLOB` data
1057 that is returned from cursors.
1058
1059 .. note::
1060
1061 This type is an extension to the DB API definition.
1062
1063
1064 .. data:: LONG_BINARY
1065
1066 This type object is used to describe columns in a database that are long
1067 binary (in Oracle these are LONG RAW columns).
1068
1069 .. note::
1070
1071 This type is an extension to the DB API definition.
1072
1073
1074 .. data:: LONG_STRING
1075
1076 This type object is used to describe columns in a database that are long
1077 strings (in Oracle these are LONG columns).
1078
1079 .. note::
1080
1081 This type is an extension to the DB API definition.
1082
1083
1084 .. data:: NATIVE_FLOAT
1085
1086 This type object is used to describe columns in a database that are of type
1087 binary_double or binary_float.
1088
1089 .. note::
1090
1091 This type is an extension to the DB API definition.
1092
1093
1094 .. data:: NATIVE_INT
1095
1096 This type object is used to bind integers using Oracle's native integer
1097 support, rather than the standard number support.
1098
1099 .. versionadded:: 5.3
1100
1101 .. note::
1102
1103 This type is an extension to the DB API definition.
1104
1105
1106 .. data:: NCHAR
1107
1108 This type object is used to describe national character strings (NVARCHAR2)
1109 in Oracle.
1110
1111 .. note::
1112
1113 This type is an extension to the DB API definition.
1114
1115
1116 .. data:: NCLOB
1117
1118 This type object is used to describe columns in a database that are NCLOBs.
1119
1120 .. note::
1121
1122 This type is an extension to the DB API definition.
1123
1124
1125 .. data:: NUMBER
1126
1127 This type object is used to describe columns in a database that are
1128 numbers.
1129
1130
1131 .. data:: OBJECT
1132
1133 This type object is used to describe columns in a database that are
1134 objects.
1135
1136 .. note::
1137
1138 This type is an extension to the DB API definition.
1139
1140
1141 .. data:: ROWID
1142
1143 This type object is used to describe the pseudo column "rowid".
1144
1145
1146 .. data:: STRING
1147
1148 This type object is used to describe columns in a database that are strings
1149 (in Oracle this is VARCHAR2 columns).
1150
1151
1152 .. data:: TIMESTAMP
1153
1154 This type object is used to describe columns in a database that are
1155 timestamps.
1156
1157 .. note::
1158
1159 This attribute is an extension to the DB API definition.
1160
1161
1162 .. _exceptions:
1163
1164 Exceptions
1165 ==========
1166
1167 .. exception:: Warning
1168
1169 Exception raised for important warnings and defined by the DB API but not
1170 actually used by cx_Oracle.
1171
1172
1173 .. exception:: Error
1174
1175 Exception that is the base class of all other exceptions defined by
1176 cx_Oracle and is a subclass of the Python StandardError exception (defined
1177 in the module exceptions).
1178
1179
1180 .. exception:: InterfaceError
1181
1182 Exception raised for errors that are related to the database interface
1183 rather than the database itself. It is a subclass of Error.
1184
1185
1186 .. exception:: DatabaseError
1187
1188 Exception raised for errors that are related to the database. It is a
1189 subclass of Error.
1190
1191
1192 .. exception:: DataError
1193
1194 Exception raised for errors that are due to problems with the processed
1195 data. It is a subclass of DatabaseError.
1196
1197
1198 .. exception:: OperationalError
1199
1200 Exception raised for errors that are related to the operation of the
1201 database but are not necessarily under the control of the progammer. It is
1202 a subclass of DatabaseError.
1203
1204
1205 .. exception:: IntegrityError
1206
1207 Exception raised when the relational integrity of the database is affected.
1208 It is a subclass of DatabaseError.
1209
1210
1211 .. exception:: InternalError
1212
1213 Exception raised when the database encounters an internal error. It is a
1214 subclass of DatabaseError.
1215
1216
1217 .. exception:: ProgrammingError
1218
1219 Exception raised for programming errors. It is a subclass of DatabaseError.
1220
1221
1222 .. exception:: NotSupportedError
1223
1224 Exception raised when a method or database API was used which is not
1225 supported by the database. It is a subclass of DatabaseError.
1226
1227
1228 Exception handling
1229 ==================
1230
1231 .. note::
1232
1233 PEP 249 (Python Database API Specification v2.0) says the following about
1234 exception values:
1235
1236 [...] The values of these exceptions are not defined. They should
1237 give the user a fairly good idea of what went wrong, though. [...]
1238
1239 With cx_Oracle every exception object has exactly one argument in the
1240 ``args`` tuple. This argument is a ``cx_Oracle._Error`` object which has
1241 the following five read-only attributes.
1242
1243 .. attribute:: _Error.code
1244
1245 Integer attribute representing the Oracle error number (ORA-XXXXX).
1246
1247 .. attribute:: _Error.offset
1248
1249 Integer attribute representing the error offset when applicable.
1250
1251 .. attribute:: _Error.message
1252
1253 String attribute representing the Oracle message of the error. This
1254 message is localized by the environment of the Oracle connection.
1255
1256 .. attribute:: _Error.context
1257
1258 String attribute representing the context in which the exception was
1259 raised.
1260
1261 .. attribute:: _Error.isrecoverable
1262
1263 Boolean attribute representing whether the error is recoverable or not.
1264 This is False in all cases unless Oracle Database 12.1 is being used on
1265 both the server and the client.
1266
1267 .. versionadded:: 5.3
1268
1269
1270 This allows you to use the exceptions for example in the following way:
1271
1272 ::
1273
1274 from __future__ import print_function
1275
1276 import cx_Oracle
1277
1278 connection = cx_Oracle.connect("cx_Oracle/dev@localhost/orclpdb")
1279 cursor = connection.cursor()
1280
1281 try:
1282 cursor.execute("select 1 / 0 from dual")
1283 except cx_Oracle.DatabaseError as exc:
1284 error, = exc.args
1285 print("Oracle-Error-Code:", error.code)
1286 print("Oracle-Error-Message:", error.message)
1287
+0
-152
doc/src/objecttype.rst less more
0 .. _objecttype:
1
2 *******************
3 Object Type Objects
4 *******************
5
6 .. note::
7
8 This object is an extension to the DB API. It is returned by the
9 :meth:`Connection.gettype()` call and is available as the
10 :data:`Variable.type` for variables containing Oracle objects.
11
12
13 .. method:: ObjectType([sequence])
14
15 The object type may be called directly and serves as an alternative way of
16 calling :meth:`~ObjectType.newobject()`.
17
18
19 .. attribute:: ObjectType.attributes
20
21 This read-only attribute returns a list of the attributes that make up the
22 object type. Each attribute has a name attribute on it.
23
24
25 .. attribute:: ObjectType.iscollection
26
27 This read-only attribute returns a boolean indicating if the object type
28 refers to a collection or not.
29
30
31 .. attribute:: ObjectType.name
32
33 This read-only attribute returns the name of the type.
34
35
36 .. method:: ObjectType.newobject([sequence])
37
38 Return a new Oracle object of the given type. This object can then be
39 modified by setting its attributes and then bound to a cursor for
40 interaction with Oracle. If the object type refers to a collection, a
41 sequence may be passed and the collection will be initialized with the
42 items in that sequence.
43
44
45 .. attribute:: ObjectType.schema
46
47 This read-only attribute returns the name of the schema that owns the type.
48
49
50 Object Objects
51 ==============
52
53 .. note::
54
55 This object is an extension to the DB API. It is returned by the
56 :meth:`ObjectType.newobject()` call and can be bound to variables of
57 type :data:`~cx_Oracle.OBJECT`. Attributes can be retrieved and set
58 directly.
59
60 .. method:: Object.append(element)
61
62 Append an element to the collection object. If no elements exist in the
63 collection, this creates an element at index 0; otherwise, it creates an
64 element immediately following the highest index available in the
65 collection.
66
67
68 .. method:: Object.asdict()
69
70 Return a dictionary where the collection's indexes are the keys and the
71 elements are its values.
72
73
74 .. method:: Object.aslist()
75
76 Return a list of each of the collection's elements in index order.
77
78
79 .. method:: Object.copy()
80
81 Create a copy of the object and return it.
82
83
84 .. method:: Object.delete(index)
85
86 Delete the element at the specified index of the collection. If the
87 element does not exist or is otherwise invalid, an error is raised. Note
88 that the indices of the remaining elements in the collection are not
89 changed. In other words, the delete operation creates holes in the
90 collection.
91
92
93 .. method:: Object.exists(index)
94
95 Return True or False indicating if an element exists in the collection at
96 the specified index.
97
98
99 .. method:: Object.extend(sequence)
100
101 Append all of the elements in the sequence to the collection. This is
102 the equivalent of performing :meth:`~Object.append()` for each element
103 found in the sequence.
104
105
106 .. method:: Object.first()
107
108 Return the index of the first element in the collection. If the collection
109 is empty, None is returned.
110
111
112 .. method:: Object.getelement(index)
113
114 Return the element at the specified index of the collection. If no element
115 exists at that index, an exception is raised.
116
117
118 .. method:: Object.last()
119
120 Return the index of the last element in the collection. If the collection
121 is empty, None is returned.
122
123
124 .. method:: Object.next(index)
125
126 Return the index of the next element in the collection following the
127 specified index. If there are no elements in the collection following the
128 specified index, None is returned.
129
130
131 .. method:: Object.prev(index)
132
133 Return the index of the element in the collection preceding the specified
134 index. If there are no elements in the collection preceding the
135 specified index, None is returned.
136
137
138 .. method:: Object.setelement(index, value)
139
140 Set the value in the collection at the specified index to the given value.
141
142
143 .. method:: Object.size()
144
145 Return the number of elements in the collection.
146
147
148 .. method:: Object.trim(num)
149
150 Remove the specified number of elements from the end of the collection.
151
0 :orphan:
1
2 .. _releasenotes:
3
4 cx_Oracle Release Notes
5 =======================
6
7 Version 8.1 (December 2020)
8 ---------------------------
9
10 #) Updated embedded ODPI-C to `version 4.1.0
11 <https://oracle.github.io/odpi/doc/releasenotes.html#
12 version-4-1-december-8-2020>`__.
13 #) Added support for new JSON data type available in Oracle Client and
14 Database 21 and higher.
15 #) Dropped support for Python 3.5. Added support for Python 3.9.
16 #) Added internal methods for getting/setting OCI attributes that are
17 otherwise not supported by cx_Oracle. These methods should only be used as
18 directed by Oracle.
19 #) Minor code improvement supplied by Alex Henrie
20 (`PR 472 <https://github.com/oracle/python-cx_Oracle/pull/472>`__).
21 #) Builds are now done with setuptools and most metadata has moved from
22 `setup.py` to `setup.cfg` in order to take advantage of Python packaging
23 improvements.
24 #) The ability to pickle/unpickle Database and API types has been restored.
25 #) Tests can now be run with tox in order to automate testing of the different
26 environments that are supported.
27 #) The value of prefetchrows for REF CURSOR variables is now honored.
28 #) Improved documentation, samples and test suite.
29
30
31 Version 8.0.1 (August 2020)
32 ---------------------------
33
34 #) Updated embedded ODPI-C to `version 4.0.2
35 <https://oracle.github.io/odpi/doc/releasenotes.html#
36 version-4-0-2-august-31-2020>`__. This includes the fix for binding and
37 fetching numbers with 39 or 40 decimal digits
38 (`issue 459 <https://github.com/oracle/python-cx_Oracle/issues/459>`__).
39 #) Added build metadata specifying that Python 3.5 and higher is required in
40 order to avoid downloading and failing to install with Python 2. The
41 exception message when running ``setup.py`` directly was updated to inform
42 those using Python 2 to use version 7.3 instead.
43 #) Documentation improvements.
44
45
46 Version 8.0 (June 2020)
47 -----------------------
48
49 #) Dropped support for Python 2. For those still requiring Python 2, see
50 :ref:`python2`.
51 #) Updated embedded ODPI-C to `version 4.0.1
52 <https://oracle.github.io/odpi/doc/releasenotes.html#
53 version-4-0-1-june-26-2020>`__.
54 #) Reworked type management to clarify and simplify code
55
56 - Added :ref:`constants <dbtypes>` for all database types. The database
57 types :data:`cx_Oracle.DB_TYPE_BINARY_FLOAT`,
58 :data:`cx_Oracle.DB_TYPE_INTERVAL_YM`,
59 :data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` and
60 :data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` are completely new. The other
61 types were found in earlier releases under a different name. These types
62 will be found in :data:`Cursor.description` and passed as the defaultType
63 parameter to the :data:`Connection.outputtypehandler` and
64 :data:`Cursor.outputtypehandler` functions.
65 - Added :ref:`synonyms <dbtypesynonyms>` from the old type names to the new
66 type names for backwards compatibility. They are deprecated and will be
67 removed in a future version of cx_Oracle.
68 - The DB API :ref:`constants <types>` are now a specialized constant that
69 matches to the corresponding database types, as recommended by the DB
70 API.
71 - The variable attribute :data:`~Variable.type` now refers to one of the
72 new database type constants if the variable does not contain objects
73 (previously it was None in that case).
74 - The attribute :data:`~LOB.type` was added to LOB values.
75 - The attribute :data:`~ObjectAttribute.type` was added to attributes of
76 object types.
77 - The attribute :data:`~ObjectType.element_type` was added to object types.
78 - :ref:`Object types <objecttype>` now compare equal if they were created
79 by the same connection or session pool and their schemas and names match.
80 - All variables are now instances of the same class (previously each type
81 was an instance of a separate variable type). The attribute
82 :data:`~Variable.type` can be examined to determine the database type it
83 is associated with.
84 - The string representation of variables has changed to include the type
85 in addition to the value.
86
87 #) Added function :meth:`cx_Oracle.init_oracle_client()` in order to enable
88 programmatic control of the initialization of the Oracle Client library.
89 #) The default encoding for all character data is now UTF-8 and any character
90 set specified in the environment variable ``NLS_LANG`` is ignored.
91 #) Added functions :meth:`SodaCollection.save()`,
92 :meth:`SodaCollection.saveAndGet()` and :meth:`SodaCollection.truncate()`
93 available in Oracle Client 20 and higher.
94 #) Added function :meth:`SodaOperation.fetchArraySize()` available in Oracle
95 Client 19.5 and higher.
96 #) Added attribute :attr:`Cursor.prefetchrows` to control the number of rows
97 that the Oracle Client library fetches into internal buffers when a query
98 is executed.
99 #) Internally make use of new mode available in Oracle Client 20 and higher in
100 order to avoid a round-trip when accessing :attr:`Connection.version` for
101 the first time.
102 #) Added support for starting up a database using a parameter file (PFILE),
103 as requested
104 (`issue 295 <https://github.com/oracle/python-cx_Oracle/issues/295>`__).
105 #) Fixed overflow issue when calling :meth:`Cursor.getbatcherrors()` with
106 row offsets exceeding 65536.
107 #) Eliminated spurious error when accessing :attr:`Cursor.lastrowid` after
108 executing an INSERT ALL statement.
109 #) Miscellaneous improvements supplied by Alex Henrie (pull requests
110 `419 <https://github.com/oracle/python-cx_Oracle/pull/419>`__,
111 `420 <https://github.com/oracle/python-cx_Oracle/pull/420>`__,
112 `421 <https://github.com/oracle/python-cx_Oracle/pull/421>`__,
113 `422 <https://github.com/oracle/python-cx_Oracle/pull/422>`__,
114 `423 <https://github.com/oracle/python-cx_Oracle/pull/423>`__,
115 `437 <https://github.com/oracle/python-cx_Oracle/pull/437>`__ and
116 `438 <https://github.com/oracle/python-cx_Oracle/pull/438>`__).
117 #) Python objects bound to boolean variables are now converted to True or
118 False based on whether they would be considered True or False in a Python
119 if statement. Previously, only True was treated as True and all other
120 Python values (including 1, 1.0, and "foo") were treated as False
121 (pull request
122 `435 <https://github.com/oracle/python-cx_Oracle/pull/435>`__).
123 #) Documentation, samples and test suite improvements.
124
125
126 Version 7.3 (December 2019)
127 ---------------------------
128
129 #) Added support for Python 3.8.
130 #) Updated embedded ODPI-C to `version 3.3
131 <https://oracle.github.io/odpi/doc/releasenotes.html#
132 version-3-3-december-2-2019>`__.
133 #) Added support for CQN and other subscription client initiated connections
134 to the database (as opposed to the default server initiated connections)
135 created by calling :meth:`Connection.subscribe()`.
136 #) Added :attr:`support <Cursor.lastrowid>` for returning the rowid of the
137 last row modified by an operation on a cursor (or None if no row was
138 modified).
139 #) Added support for setting the maxSessionsPerShard attribute when
140 :meth:`creating session pools <cx_Oracle.SessionPool>`.
141 #) Added check to ensure sharding key is specified when a super sharding key
142 is specified.
143 #) Improved error message when the Oracle Client library is loaded
144 successfully but the attempt to detect the version of that library fails,
145 either due to the fact that the library is too old or the method could not
146 be called for some reason (`node-oracledb issue 1168
147 <https://github.com/oracle/node-oracledb/issues/1168>`__).
148 #) Adjusted support for creating a connection using an existing OCI service
149 context handle. In order to avoid potential memory corruption and
150 unsupported behaviors, the connection will now use the same encoding as the
151 existing OCI service context handle when it was created.
152 #) Added ``ORA-3156: OCI call timed out`` to the list of error messages that
153 result in error DPI-1067.
154 #) Adjusted samples and the test suite so that they can be run against Oracle
155 Cloud databases.
156 #) Fixed bug when attempting to create a scrollable cursor on big endian
157 platforms like AIX on PPC.
158 #) Eliminated reference leak and ensure that memory is properly initialized in
159 case of error when using sharding keys.
160 #) Eliminated reference leak when splitting the password and DSN components
161 out of a full connect string.
162 #) Corrected processing of DATE sharding keys (sharding requires a slightly
163 different format to be passed to the server).
164 #) Eliminated reference leak when
165 :meth:`creating message property objects <Connection.msgproperties()>`.
166 #) Attempting to use proxy authentication with a homogeneous pool will now
167 raise a ``DatabaseError`` exception with the message
168 ``DPI-1012: proxy authentication is not possible with homogeneous pools``
169 instead of a ``ProgrammingError`` exception with the message
170 ``pool is homogeneous. Proxy authentication is not possible.`` since this
171 check is done by ODPI-C. An empty string (or None) for the user name will
172 no longer generate an exception.
173 #) Exception ``InterfaceError: not connected`` is now always raised when an
174 operation is attempted with a closed connection. Previously, a number of
175 different exceptions were raised depending on the operation.
176 #) Added ``ORA-40479: internal JSON serializer error`` to the list of
177 exceptions that result in ``cx_Oracle.IntegrityError``.
178 #) Improved documentation.
179
180
181 Version 7.2.3 (October 2019)
182 ----------------------------
183
184 #) Updated embedded ODPI-C to `version 3.2.2
185 <https://oracle.github.io/odpi/doc/releasenotes.html#
186 version-3-2-2-october-1-2019>`__.
187 #) Restored support for setting numeric bind variables with boolean values.
188 #) Ensured that sharding keys are dedicated to the connection that is acquired
189 using them in order to avoid possible hangs, crashes or unusual errors.
190 #) Corrected support for PLS_INTEGER and BINARY_INTEGER types when used in
191 PL/SQL records
192 (`ODPI-C issue 112 <https://github.com/oracle/odpi/issues/112>`__).
193 #) Improved documentation.
194
195
196 Version 7.2.2 (August 2019)
197 ---------------------------
198
199 #) Updated embedded ODPI-C to `version 3.2.1
200 <https://oracle.github.io/odpi/doc/releasenotes.html#
201 version-3-2-1-august-12-2019>`__.
202 #) A more meaningful error is now returned when calling
203 :meth:`SodaCollection.insertMany()` with an empty list.
204 #) A more meaningful error is now returned when calling
205 :meth:`Subscription.registerquery()` with SQL that is not a SELECT
206 statement.
207 #) Eliminated segfault when a connection is closed after being created by a
208 call to :meth:`cx_Oracle.connect()` with the parameter ``cclass`` set to
209 a non-empty string.
210 #) Added user guide documentation.
211 #) Updated default connect strings to use 19c and XE 18c defaults.
212
213
214 Version 7.2.1 (July 2019)
215 -------------------------
216
217 #) Resolved ``MemoryError`` exception on Windows when using an output type
218 handler
219 (`issue 330 <https://github.com/oracle/python-cx_Oracle/issues/330>`__).
220 #) Improved test suite and samples.
221 #) Improved documentation.
222
223
224 Version 7.2 (July 2019)
225 -----------------------
226
227 #) Updated embedded ODPI-C to `version 3.2
228 <https://oracle.github.io/odpi/doc/releasenotes.html#
229 version-3-2-july-1-2019>`__.
230 #) Improved AQ support
231
232 - added support for enqueue and dequeue of RAW payloads
233 - added support for bulk enqueue and dequeue of messages
234 - added new method :meth:`Connection.queue()` which creates a new
235 :ref:`queue object <queue>` in order to simplify AQ usage
236 - enhanced method :meth:`Connection.msgproperties()` to allow the writable
237 properties of the newly created object to be initialized.
238 - the original methods for enqueueing and dequeuing (Connection.deq(),
239 Connection.deqoptions(), Connection.enq() and Connection.enqoptions())
240 are now deprecated and will be removed in a future version.
241
242 #) Removed preview status from existing SODA functionality. See
243 `this tracking issue
244 <https://github.com/oracle/python-cx_Oracle/issues/309>`__ for known issues
245 with SODA.
246 #) Added support for a preview of SODA bulk insert, available in Oracle Client
247 18.5 and higher.
248 #) Added support for setting LOB object attributes, as requested
249 (`issue 299 <https://github.com/oracle/python-cx_Oracle/issues/299>`__).
250 #) Added mode :data:`cx_Oracle.DEFAULT_AUTH` as requested
251 (`issue 293 <https://github.com/oracle/python-cx_Oracle/issues/293>`__).
252 #) Added support for setting the LOB prefetch length indicator in order to
253 reduce the number of round trips when fetching LOBs and then subsequently
254 calling :meth:`LOB.size()`, :meth:`LOB.getchunksize()` or
255 :meth:`LOB.read()`.
256 #) Added support for types BINARY_INTEGER, PLS_INTEGER, ROWID, LONG and LONG
257 RAW when used in PL/SQL.
258 #) Eliminated deprecation of attribute :attr:`Subscription.id`. It is now
259 populated with the value of ``REGID`` found in the database view
260 ``USER_CHANGE_NOTIFICATION_REGS`` or the value of ``REG_ID`` found in the
261 database view ``USER_SUBSCR_REGISTRATIONS``. For AQ subscriptions, the
262 value is 0.
263 #) Enabled PY_SSIZE_T_CLEAN, as required by Python 3.8
264 (`issue 317 <https://github.com/oracle/python-cx_Oracle/issues/317>`__).
265 #) Eliminated memory leak when fetching objects that are atomically null
266 (`issue 298 <https://github.com/oracle/python-cx_Oracle/issues/298>`__).
267 #) Eliminated bug when processing the string representation of numbers like
268 1e-08 and 1e-09 (`issue 300
269 <https://github.com/oracle/python-cx_Oracle/issues/300>`__).
270 #) Improved error message when the parent cursor is closed before a fetch is
271 attempted from an implicit result cursor.
272 #) Improved test suite and samples.
273 #) Improved documentation.
274
275
276 Version 7.1.3 (April 2019)
277 --------------------------
278
279 #) Updated to `ODPI-C 3.1.4
280 <https://oracle.github.io/odpi/doc/releasenotes.html#
281 version-3-1-4-april-24-2019>`__.
282 #) Added support for getting the row count for PL/SQL statements
283 (`issue 285 <https://github.com/oracle/python-cx_Oracle/issues/285>`__).
284 #) Corrected parsing of connect string so that the last @ symbol is searched
285 for instead of the first @ symbol; otherwise, passwords containing an @
286 symbol will result in the incorrect DSN being extracted
287 (`issue 290 <https://github.com/oracle/python-cx_Oracle/issues/290>`__).
288 #) Adjusted return value of cursor.callproc() to follow documentation (only
289 positional arguments are returned since the order of keyword parameters
290 cannot be guaranteed in any case)
291 (`PR 287 <https://github.com/oracle/python-cx_Oracle/pull/287>`__).
292 #) Corrected code getting sample and test parameters by user input when using
293 Python 2.7.
294
295
296 Version 7.1.2 (March 2019)
297 --------------------------
298
299 #) Updated to `ODPI-C 3.1.3
300 <https://oracle.github.io/odpi/doc/releasenotes.html#
301 version-3-1-3-march-12-2019>`__.
302 #) Ensured that the strings "-0" and "-0.0" are correctly handled as zero
303 values
304 (`issue 274 <https://github.com/oracle/python-cx_Oracle/issues/274>`__).
305 #) Eliminated error when startup and shutdown events are generated
306 (`ODPI-C issue 102 <https://github.com/oracle/odpi/issues/102>`__).
307 #) Enabled the types specified in :meth:`Cursor.setinputsizes()` and
308 :meth:`Cursor.callfunc()` to be an object type in addition to a Python
309 type, just like in :meth:`Cursor.var()`.
310 #) Reverted changes to return decimal numbers when the numeric precision was
311 too great to be returned accurately as a floating point number. This change
312 had too great an impact on existing functionality and an output type
313 handler can be used to return decimal numbers where that is desirable
314 (`issue 279 <https://github.com/oracle/python-cx_Oracle/issues/279>`__).
315 #) Eliminated discrepancies in character sets between an external connection
316 handle and the newly created connection handle that references the external
317 connection handle
318 (`issue 273 <https://github.com/oracle/python-cx_Oracle/issues/273>`__).
319 #) Eliminated memory leak when receiving messages received from subscriptions.
320 #) Improved test suite and documentation.
321
322
323 Version 7.1.1 (February 2019)
324 -----------------------------
325
326 #) Updated to `ODPI-C 3.1.2
327 <https://oracle.github.io/odpi/doc/releasenotes.html#
328 version-3-1-2-february-19-2019>`__.
329 #) Corrected code for freeing CQN message objects when multiple queries are
330 registered
331 (`ODPI-C issue 96 <https://github.com/oracle/odpi/issues/96>`__).
332 #) Improved error messages and installation documentation.
333
334
335 Version 7.1 (February 2019)
336 ---------------------------
337
338 #) Updated to `ODPI-C 3.1
339 <https://oracle.github.io/odpi/doc/releasenotes.html#
340 version-3-1-january-21-2019>`__.
341 #) Improved support for session tagging in session pools by allowing a
342 session callback to be specified when creating a pool via
343 :meth:`cx_Oracle.SessionPool()`. Callbacks can be written in Python or in
344 PL/SQL and can be used to improve performance by decreasing round trips to
345 the database needed to set session state. Callbacks written in Python will
346 be invoked for brand new connections (that have never been acquired from
347 the pool before) or when the tag assigned to the connection doesn't match
348 the one that was requested. Callbacks written in PL/SQL will only be
349 invoked when the tag assigned to the connection doesn't match the one that
350 was requested.
351 #) Added attribute :attr:`Connection.tag` to provide access to the actual tag
352 assigned to the connection. Setting this attribute will cause the
353 connection to be retagged when it is released back to the pool.
354 #) Added support for fetching SYS.XMLTYPE values as strings, as requested
355 (`issue 14 <https://github.com/oracle/python-cx_Oracle/issues/14>`__).
356 Note that this support is limited to the size of VARCHAR2 columns in the
357 database (either 4000 or 32767 bytes).
358 #) Added support for allowing the typename parameter in method
359 :meth:`Cursor.var()` to be None or a valid object type created by the
360 method :meth:`Connection.gettype()`, as requested
361 (`issue 231 <https://github.com/oracle/python-cx_Oracle/issues/231>`__).
362 #) Added support for getting and setting attributes of type RAW on Oracle
363 objects, as requested
364 (`ODPI-C issue 72 <https://github.com/oracle/odpi/issues/72>`__).
365 #) Added support for performing external authentication with proxy for
366 standalone connections.
367 #) Added support for mixing integers, floating point and decimal values in
368 data passed to :meth:`Cursor.executemany()`
369 (`issue 241 <https://github.com/oracle/python-cx_Oracle/issues/241>`__).
370 The error message raised when a value cannot be converted to an Oracle
371 number was also improved.
372 #) Adjusted fetching of numeric values so that no precision is lost. If an
373 Oracle number cannot be represented by a Python floating point number a
374 decimal value is automatically returned instead.
375 #) Corrected handling of multiple calls to method
376 :meth:`Cursor.executemany()` where all of the values in one of the columns
377 passed to the first call are all None and a subsequent call has a value
378 other than None in the same column
379 (`issue 236 <https://github.com/oracle/python-cx_Oracle/issues/236>`__).
380 #) Added additional check for calling :meth:`Cursor.setinputsizes()` with an
381 empty dictionary in order to avoid the error "cx_Oracle.ProgrammingError:
382 positional and named binds cannot be intermixed"
383 (`issue 199 <https://github.com/oracle/python-cx_Oracle/issues/199>`__).
384 #) Corrected handling of values that exceed the maximum value of a plain
385 integer object on Python 2 on Windows
386 (`issue 257 <https://github.com/oracle/python-cx_Oracle/issues/257>`__).
387 #) Added error message when attempting external authentication with proxy
388 without placing the user name in [] (proxy authentication was previously
389 silently ignored).
390 #) Exempted additional error messages from forcing a statement to be dropped
391 from the cache
392 (`ODPI-C issue 76 <https://github.com/oracle/odpi/issues/76>`__).
393 #) Improved dead session detection when using session pools for Oracle Client
394 12.2 and higher.
395 #) Ensured that the connection returned from a pool after a failed ping (such
396 as due to a killed session) is not itself marked as needing to be dropped
397 from the pool.
398 #) Eliminated memory leak under certain circumstances when pooled connections
399 are released back to the pool.
400 #) Eliminated memory leak when connections are dropped from the pool.
401 #) Eliminated memory leak when calling :meth:`Connection.close()` after
402 fetching collections from the database.
403 #) Adjusted order in which memory is freed when the last references to SODA
404 collections, documents, document cursors and collection cursors are
405 released, in order to prevent a segfault under certain circumstances.
406 #) Improved code preventing a statement from binding itself, in order to avoid
407 a potential segfault under certain circumstances.
408 #) Worked around OCI bug when attempting to free objects that are PL/SQL
409 records, in order to avoid a potential segfault.
410 #) Improved test suite and samples. Note that default passwords are no longer
411 supplied. New environment variables can be set to specify passwords if
412 desired, or the tests and samples will prompt for the passwords when
413 needed. In addition, a Python script is now available to create and drop
414 the schemas used for the tests and samples.
415 #) Improved documentation.
416
417
418 Version 7.0 (September 2018)
419 ----------------------------
420
421 #) Update to `ODPI-C 3.0
422 <https://oracle.github.io/odpi/doc/releasenotes.html#
423 version-3-0-0-september-13-2018>`__.
424 #) Added support for Oracle Client 18 libraries.
425 #) Added support for SODA (as preview). See :ref:`SODA Database <sodadb>`,
426 :ref:`SODA Collection <sodacoll>` and :ref:`SODA Document <sodadoc>` for
427 more information.
428 #) Added support for call timeouts available in Oracle Client 18.1 and
429 higher. See :attr:`Connection.callTimeout`.
430 #) Added support for getting the contents of a SQL collection object as a
431 dictionary, where the keys are the indices of the collection and the values
432 are the elements of the collection. See function :meth:`Object.asdict()`.
433 #) Added support for closing a session pool via the function
434 :meth:`SessionPool.close()`. Once closed, further attempts to use any
435 connection that was acquired from the pool will result in the error
436 "DPI-1010: not connected".
437 #) Added support for setting a LOB attribute of an object with a string or
438 bytes (instead of requiring a temporary LOB to be created).
439 #) Added support for the packed decimal type used by object attributes with
440 historical types DECIMAL and NUMERIC
441 (`issue 212 <https://github.com/oracle/python-cx_Oracle/issues/212>`__).
442 #) On Windows, first attempt to load oci.dll from the same directory as
443 the cx_Oracle module.
444 #) SQL objects that are created or fetched from the database are now tracked
445 and marked unusable when a connection is closed. This was done in order
446 to avoid a segfault under certain circumstances.
447 #) Re-enabled dead session detection functionality when using pools for Oracle
448 Client 12.2 and higher in order to handle classes of connection errors such
449 as resource profile limits.
450 #) Improved error messages when the Oracle Client or Oracle Database need to
451 be at a minimum version in order to support a particular feature.
452 #) When a connection is used as a context manager, the connection is now
453 closed when the block ends. Attempts to set
454 ``cx_Oracle.__future__.ctx_mgr_close`` are now ignored.
455 #) When a DML returning statement is executed, variables bound to it will
456 return an array when calling :meth:`Variable.getvalue()`. Attempts to set
457 ``cx_Oracle.__future__.dml_ret_array_val`` are now ignored.
458 #) Support for Python 3.4 has been dropped.
459 #) Added additional test cases.
460 #) Improved documentation.
461
462
463 Version 6.4.1 (July 2018)
464 -------------------------
465
466 #) Update to `ODPI-C 2.4.2
467 <https://oracle.github.io/odpi/doc/releasenotes.html#
468 version-2-4-2-july-9-2018>`__.
469
470 - Avoid buffer overrun due to improper calculation of length byte when
471 converting some negative 39 digit numbers from string to the internal
472 Oracle number format
473 (`ODPI-C issue 67 <https://github.com/oracle/odpi/issues/67>`__).
474
475 #) Prevent error "cx_Oracle.ProgrammingError: positional and named binds
476 cannot be intermixed" when calling cursor.setinputsizes() without any
477 parameters and then calling cursor.execute() with named bind parameters
478 (`issue 199 <https://github.com/oracle/python-cx_Oracle/issues/199>`__).
479
480
481 Version 6.4 (July 2018)
482 -----------------------
483
484 #) Update to `ODPI-C 2.4.1
485 <https://oracle.github.io/odpi/doc/releasenotes.html#
486 version-2-4-1-july-2-2018>`__.
487
488 - Added support for grouping subscriptions. See parameters groupingClass,
489 groupingValue and groupingType to function
490 :meth:`Connection.subscribe()`.
491 - Added support for specifying the IP address a subscription should use
492 instead of having the Oracle Client library determine the IP address on
493 its own. See parameter ipAddress to function
494 :meth:`Connection.subscribe()`.
495 - Added support for subscribing to notifications when messages are
496 available to dequeue in an AQ queue. The new constant
497 :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ` should be passed to the namespace
498 parameter of function :meth:`Connection.subscribe()` in order to get this
499 functionality. Attributes :attr:`Message.queueName` and
500 :attr:`Message.consumerName` will be populated in notification messages
501 that are received when this namespace is used.
502 - Added attribute :attr:`Message.registered` to let the notification
503 callback know when the subscription that generated the notification is no
504 longer registered with the database.
505 - Added support for timed waits when acquiring a session from a session
506 pool. Use the new constant :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT` in
507 the parameter getmode to function :meth:`cx_Oracle.SessionPool` along
508 with the new parameter waitTimeout.
509 - Added support for specifying the timeout and maximum lifetime session for
510 session pools when they are created using function
511 :meth:`cx_Oracle.SessionPool`. Previously the pool had to be created
512 before these values could be changed.
513 - Avoid memory leak when dequeuing from an empty queue.
514 - Ensure that the row count for queries is reset to zero when the statement
515 is executed
516 (`issue 193 <https://github.com/oracle/python-cx_Oracle/issues/193>`__).
517 - If the statement should be deleted from the statement cache, first check
518 to see that there is a statement cache currently being used; otherwise,
519 the error "ORA-24300: bad value for mode" will be raised under certain
520 conditions.
521
522 #) Added support for using the cursor as a context manager
523 (`issue 190 <https://github.com/oracle/python-cx_Oracle/issues/190>`__).
524 #) Added parameter "encodingErrors" to function :meth:`Cursor.var()` in order
525 to add support for specifying the "errors" parameter to the decode() that
526 takes place internally when fetching strings from the database
527 (`issue 162 <https://github.com/oracle/python-cx_Oracle/issues/162>`__).
528 #) Added support for specifying an integer for the parameters argument to
529 :meth:`Cursor.executemany()`. This allows for batch execution when no
530 parameters are required or when parameters have previously been bound. This
531 replaces Cursor.executemanyprepared() (which is now deprecated and will be
532 removed in cx_Oracle 7).
533 #) Adjusted the binding of booleans so that outside of PL/SQL they are bound
534 as integers
535 (`issue 181 <https://github.com/oracle/python-cx_Oracle/issues/181>`__).
536 #) Added support for binding decimal.Decimal values to cx_Oracle.NATIVE_FLOAT
537 as requested
538 (`issue 184 <https://github.com/oracle/python-cx_Oracle/issues/184>`__).
539 #) Added checks on passing invalid type parameters to methods
540 :meth:`Cursor.arrayvar()`, :meth:`Cursor.callfunc()` and
541 :meth:`Cursor.setinputsizes()`.
542 #) Corrected handling of cursors and rowids in DML Returning statements.
543 #) Added sample from David Lapp demonstrating the use of GeoPandas with
544 SDO_GEOMETRY and a sample for demonstrating the use of REF cursors.
545 #) Adjusted samples and documentation for clarity.
546 #) Added additional test cases.
547
548
549 Version 6.3.1 (May 2018)
550 ------------------------
551
552 #) Update to `ODPI-C 2.3.2
553 <https://oracle.github.io/odpi/doc/releasenotes.html#
554 version-2-3-2-may-7-2018>`__.
555
556 - Ensure that a call to unregister a subscription only occurs if the
557 subscription is still registered.
558 - Ensure that before a statement is executed any buffers used for DML
559 returning statements are reset.
560
561 #) Ensure that behavior with cx_Oracle.__future__.dml_ret_array_val not
562 set or False is the same as the behavior in cx_Oracle 6.2
563 (`issue 176 <https://github.com/oracle/python-cx_Oracle/issues/176>`__).
564
565
566 Version 6.3 (April 2018)
567 ------------------------
568
569 #) Update to `ODPI-C 2.3.1
570 <https://oracle.github.io/odpi/doc/releasenotes.html#
571 version-2-3-1-april-25-2018>`__.
572
573 - Fixed binding of LONG data (values exceeding 32KB) when using the
574 function :meth:`Cursor.executemany()`.
575 - Added code to verify that a CQN subscription is open before permitting it
576 to be used. Error "DPI-1060: subscription was already closed" will now be
577 raised if an attempt is made to use a subscription that was closed
578 earlier.
579 - Stopped attempting to unregister a CQN subscription before it was
580 completely registered. This prevents errors encountered during
581 registration from being masked by an error stating that the subscription
582 has not been registered!
583 - Added error "DPI-1061: edition is not supported when a new password is
584 specified" to clarify the fact that specifying an edition and a new
585 password at the same time is not supported when creating a connection.
586 Previously the edition value was simply ignored.
587 - Improved error message when older OCI client libraries are being used
588 that don't have the method OCIClientVersion().
589 - Fixed the handling of ANSI types REAL and DOUBLE PRECISION as
590 implemented by Oracle. These types are just subtypes of NUMBER and are
591 different from BINARY_FLOAT and BINARY_DOUBLE
592 (`issue 163 <https://github.com/oracle/python-cx_Oracle/issues/163>`__).
593 - Fixed support for true heterogeneous session pools that use different
594 user/password combinations for each session acquired from the pool.
595 - Added error message indicating that setting either of the parameters
596 arraydmlrowcounts and batcherrors to True in :meth:`Cursor.executemany()`
597 is only supported with insert, update, delete and merge statements.
598
599 #) Fixed support for getting the OUT values of bind variables bound to a DML
600 Returning statement when calling the function :meth:`Cursor.executemany()`.
601 Note that the attribute dml_ret_array_val in :attr:`cx_Oracle.__future__`
602 must be set to True first.
603 #) Added support for binding integers and floats as cx_Oracle.NATIVE_FLOAT.
604 #) A :attr:`cx_Oracle._Error` object is now the value of all cx_Oracle
605 exceptions raised by cx_Oracle.
606 (`issue 51 <https://github.com/oracle/python-cx_Oracle/issues/51>`__).
607 #) Added support for building cx_Oracle with a pre-compiled version of ODPI-C,
608 as requested
609 (`issue 103 <https://github.com/oracle/python-cx_Oracle/issues/103>`__).
610 #) Default values are now provided for all parameters to
611 :meth:`cx_Oracle.SessionPool`.
612 #) Improved error message when an unsupported Oracle type is encountered.
613 #) The Python GIL is now prevented from being held while performing a round
614 trip for the call to get the attribute :attr:`Connection.version`
615 (`issue 158 <https://github.com/oracle/python-cx_Oracle/issues/158>`__).
616 #) Added check for the validity of the year for Python 2.x since it doesn't do
617 that itself like Python 3.x does
618 (`issue 166 <https://github.com/oracle/python-cx_Oracle/issues/166>`__).
619 #) Adjusted documentation to provide additional information on the use of
620 :meth:`Cursor.executemany()` as requested
621 (`issue 153 <https://github.com/oracle/python-cx_Oracle/issues/153>`__).
622 #) Adjusted documentation to state that batch errors and array DML row counts
623 can only be used with insert, update, delete and merge statements
624 (`issue 31 <https://github.com/oracle/python-cx_Oracle/issues/31>`__).
625 #) Updated tutorial to import common connection information from files in
626 order to make setup a bit more generic.
627
628
629 Version 6.2.1 (March 2018)
630 --------------------------
631
632 #) Make sure cxoModule.h is included in the source archive
633 (`issue 155 <https://github.com/oracle/python-cx_Oracle/issues/155>`__).
634
635
636 Version 6.2 (March 2018)
637 ------------------------
638
639 #) Update to `ODPI-C 2.2.1
640 <https://oracle.github.io/odpi/doc/releasenotes.html#
641 version-2-2-1-march-5-2018>`__.
642
643 - eliminate error "DPI-1054: connection cannot be closed when open
644 statements or LOBs exist" (`issue 138
645 <https://github.com/oracle/python-cx_Oracle/issues/138>`__).
646 - avoid a round trip to the database when a connection is released back to
647 the pool by preventing a rollback from being called when no transaction
648 is in progress.
649 - improve error message when the use of bind variables is attempted with
650 DLL statements, which is not supported by Oracle.
651 - if an Oracle object is retrieved from an attribute of another Oracle
652 object or a collection, prevent the "owner" from being destroyed until
653 the object that was retrieved has itself been destroyed.
654 - correct handling of boundary numbers 1e126 and -1e126
655 - eliminate memory leak when calling :meth:`Connection.enq()` and
656 :meth:`Connection.deq()`
657 - eliminate memory leak when setting NCHAR and NVARCHAR attributes of
658 objects.
659 - eliminate memory leak when fetching collection objects from the database.
660
661 #) Added support for creating a temporary CLOB, BLOB or NCLOB via the method
662 :meth:`Connection.createlob()`.
663 #) Added support for binding a LOB value directly to a cursor.
664 #) Added support for closing the connection when reaching the end of a
665 ``with`` code block controlled by the connection as a context manager, but
666 in a backwards compatible way
667 (`issue 113 <https://github.com/oracle/python-cx_Oracle/issues/113>`__).
668 See :data:`cx_Oracle.__future__` for more information.
669 #) Reorganized code to simplify continued maintenance and consolidate
670 transformations to/from Python objects.
671 #) Ensure that the number of elements in the array is not lost when the
672 buffer size is increased to accommodate larger strings.
673 #) Corrected support in Python 3.x for cursor.parse() by permitting a string
674 to be passed, instead of incorrectly requiring a bytes object.
675 #) Eliminate reference leak with LOBs acquired from attributes of objects or
676 elements of collections.
677 #) Eliminate reference leak when extending an Oracle collection.
678 #) Documentation improvements.
679 #) Added test cases to the test suite.
680
681
682 Version 6.1 (December 2017)
683 ---------------------------
684
685 #) Update to `ODPI-C 2.1
686 <https://oracle.github.io/odpi/doc/releasenotes.html#
687 version-2-1-december-12-2017>`__.
688
689 - Support was added for accessing sharded databases via sharding keys (new
690 in Oracle 12.2). NOTE: the underlying OCI library has a bug when using
691 standalone connections. There is a small memory leak proportional to the
692 number of connections created/dropped. There is no memory leak when using
693 session pools, which is recommended.
694 - Added options for authentication with SYSBACKUP, SYSDG, SYSKM and SYSRAC,
695 as requested (`issue 101
696 <https://github.com/oracle/python-cx_Oracle/issues/101>`__).
697 - Attempts to release statements or free LOBs after the connection has been
698 closed (by, for example, killing the session) are now prevented.
699 - An error message was added when specifying an edition and a connection
700 class since this combination is not supported.
701 - Attempts to close the session for connections created with an external
702 handle are now prevented.
703 - Attempting to ping a database earlier than 10g results in ORA-1010:
704 invalid OCI operation, but that implies a response from the database and
705 therefore a successful ping, so treat it that way!
706 (see `<https://github.com/rana/ora/issues/224>`__ for more information).
707 - Support was added for converting numeric values in an object type
708 attribute to integer and text, as requested (`ODPI-C issue 35
709 <https://github.com/oracle/odpi/issues/35>`__).
710 - Setting attributes :attr:`DeqOptions.msgId` and
711 :attr:`MessageProperties.msgId` now works as expected.
712 - The overflow check when using double values (Python floats) as input
713 to float attributes of objects or elements of collections was removed as
714 it didn't work anyway and is a well-known issue that cannot be prevented
715 without removing desired functionality. The developer should ensure that
716 the source value falls within the limits of floats, understand the
717 consequent precision loss or use a different data type.
718 - Variables of string/raw types are restricted to 2 bytes less than 1 GB
719 (1,073,741,822 bytes), since OCI cannot handle more than that currently.
720 - Support was added for identifying the id of the transaction which spawned
721 a CQN subscription message, as requested
722 (`ODPI-C issue 32 <https://github.com/oracle/odpi/issues/32>`__).
723 - Corrected use of subscription port number (`issue 115
724 <https://github.com/oracle/python-cx_Oracle/issues/115>`__).
725 - Problems reported with the usage of FormatMessage() on Windows were
726 addressed (`ODPI-C issue 47
727 <https://github.com/oracle/odpi/issues/47>`__).
728 - On Windows, if oci.dll cannot be loaded because it is the wrong
729 architecture (32-bit vs 64-bit), attempt to find the offending DLL and
730 include the full path of the DLL in the message, as suggested.
731 (`ODPI-C issue 49 <https://github.com/oracle/odpi/issues/49>`__).
732 - Force OCI prefetch to always use the value 2; the OCI default is 1 but
733 setting the ODPI-C default to 2 ensures that single row fetches don't
734 require an extra round trip to determine if there are more rows to fetch;
735 this change also reduces the potential memory consumption when
736 fetchArraySize was set to a large value and also avoids performance
737 issues discovered with larger values of prefetch.
738
739 #) Fix build with PyPy 5.9.0-alpha0 in libpython mode
740 (`PR 54 <https://github.com/oracle/python-cx_Oracle/pull/54>`__).
741 #) Ensure that the edition is passed through to the database when a session
742 pool is created.
743 #) Corrected handling of Python object references when an invalid keyword
744 parameter is passed to :meth:`cx_Oracle.SessionPool`.
745 #) Corrected handling of :attr:`Connection.handle` and the handle parameter
746 to :meth:`cx_Oracle.connect` on Windows.
747 #) Documentation improvements.
748 #) Added test cases to the test suite.
749
750
751 Version 6.0.3 (November 2017)
752 -----------------------------
753
754 #) Update to `ODPI-C 2.0.3
755 <https://oracle.github.io/odpi/doc/releasenotes.html#
756 version-2-0-3-november-6-2017>`__.
757
758 - Prevent use of uninitialized data in certain cases (`issue 77
759 <https://github.com/oracle/python-cx_Oracle/issues/77>`__).
760 - Attempting to ping a database earlier than 10g results in error
761 "ORA-1010: invalid OCI operation", but that implies a response from the
762 database and therefore a successful ping, so treat it that way!
763 - Correct handling of conversion of some numbers to NATIVE_FLOAT.
764 - Prevent use of NaN with Oracle numbers since it produces corrupt data
765 (`issue 91 <https://github.com/oracle/python-cx_Oracle/issues/91>`__).
766 - Verify that Oracle objects bound to cursors, fetched from cursors, set in
767 object attributes or appended to collection objects are of the correct
768 type.
769 - Correct handling of NVARCHAR2 when used as attributes of Oracle objects
770 or as elements of collections.
771
772 #) Ensure that a call to setinputsizes() with an invalid type prior to a call
773 to executemany() does not result in a type error, but instead gracefully
774 ignores the call to setinputsizes() as required by the DB API
775 (`issue 75 <https://github.com/oracle/python-cx_Oracle/issues/75>`__).
776 #) Check variable array size when setting variable values and raise
777 IndexError, as is already done for getting variable values.
778
779
780 Version 6.0.2 (August 2017)
781 ---------------------------
782
783 #) Update to `ODPI-C 2.0.2
784 <https://oracle.github.io/odpi/doc/releasenotes.html
785 #version-2-0-2-august-30-2017>`__.
786
787 - Don't prevent connection from being explicitly closed when a fatal error
788 has taken place (`issue 67
789 <https://github.com/oracle/python-cx_Oracle/issues/67>`__).
790 - Correct handling of objects when dynamic binding is performed.
791 - Process deregistration events without an error.
792 - Eliminate memory leak when creating objects.
793
794 #) Added missing type check to prevent coercion of decimal to float
795 (`issue 68 <https://github.com/oracle/python-cx_Oracle/issues/68>`__).
796 #) On Windows, sizeof(long) = 4, not 8, which meant that integers between 10
797 and 18 digits were not converted to Python correctly
798 (`issue 70 <https://github.com/oracle/python-cx_Oracle/issues/70>`__).
799 #) Eliminate memory leak when repeatedly executing the same query.
800 #) Eliminate segfault when attempting to reuse a REF cursor that has been
801 closed.
802 #) Updated documentation.
803
804
805 Version 6.0.1 (August 2017)
806 ---------------------------
807
808 #) Update to `ODPI-C 2.0.1
809 <https://oracle.github.io/odpi/doc/releasenotes.html
810 #version-2-0-1-august-18-2017>`__.
811
812 - Ensure that queries registered via :meth:`Subscription.registerquery()`
813 do not prevent the associated connection from being closed
814 (`ODPI-C issue 27 <https://github.com/oracle/odpi/issues/27>`__).
815 - Deprecated attribute :attr:`Subscription.id` as it was never intended to
816 be exposed (`ODPI-C issue 28
817 <https://github.com/oracle/odpi/issues/28>`__). It will be dropped in
818 version 6.1.
819 - Add support for DML Returning statements that require dynamically
820 allocated variable data (such as CLOBs being returned as strings).
821
822 #) Correct packaging of Python 2.7 UCS4 wheels on Linux
823 (`issue 64 <https://github.com/oracle/python-cx_Oracle/issues/64>`__).
824 #) Updated documentation.
825
826
827 Version 6.0 (August 2017)
828 -------------------------
829
830 #) Update to `ODPI-C 2.0 <https://oracle.github.io/odpi/doc/releasenotes.html
831 #version-2-0-august-14-2017>`__.
832
833 - Prevent closing the connection when there are any open statements or
834 LOBs and add new error "DPI-1054: connection cannot be closed when open
835 statements or LOBs exist" when this situation is detected; this is
836 needed to prevent crashes under certain conditions when statements or
837 LOBs are being acted upon while at the same time (in another thread) a
838 connection is being closed; it also prevents leaks of statements and
839 LOBs when a connection is returned to a session pool.
840 - On platforms other than Windows, if the regular method for loading the
841 Oracle Client libraries fails, try using $ORACLE_HOME/lib/libclntsh.so
842 (`ODPI-C issue 20 <https://github.com/oracle/odpi/issues/20>`__).
843 - Use the environment variable DPI_DEBUG_LEVEL at runtime, not compile
844 time.
845 - Added support for DPI_DEBUG_LEVEL_ERRORS (reports errors and has the
846 value 8) and DPI_DEBUG_LEVEL_SQL (reports prepared SQL statement text
847 and has the value 16) in order to further improve the ability to debug
848 issues.
849 - Correct processing of :meth:`Cursor.scroll()` in some circumstances.
850
851 #) Delay initialization of the ODPI-C library until the first standalone
852 connection or session pool is created so that manipulation of the
853 environment variable NLS_LANG can be performed after the module has been
854 imported; this also has the added benefit of reducing the number of errors
855 that can take place when the module is imported.
856 #) Prevent binding of null values from generating the exception "ORA-24816:
857 Expanded non LONG bind data supplied after actual LONG or LOB column" in
858 certain circumstances
859 (`issue 50 <https://github.com/oracle/python-cx_Oracle/issues/50>`__).
860 #) Added information on how to run the test suite
861 (`issue 33 <https://github.com/oracle/python-cx_Oracle/issues/33>`__).
862 #) Documentation improvements.
863
864
865 Version 6.0 rc 2 (July 2017)
866 ----------------------------
867
868 #) Update to `ODPI-C rc 2 <https://oracle.github.io/odpi/doc/releasenotes.html
869 #version-2-0-0-rc-2-july-20-2017>`__.
870
871 - Provide improved error message when OCI environment cannot be created,
872 such as when the oraaccess.xml file cannot be processed properly.
873 - On Windows, convert system message to Unicode first, then to UTF-8;
874 otherwise, the error message returned could be in a mix of encodings
875 (`issue 40 <https://github.com/oracle/python-cx_Oracle/issues/40>`__).
876 - Corrected support for binding decimal values in object attribute values
877 and collection element values.
878 - Corrected support for binding PL/SQL boolean values to PL/SQL
879 procedures with Oracle client 11.2.
880
881 #) Define exception classes on the connection object in addition to at module
882 scope in order to simplify error handling in multi-connection environments,
883 as specified in the Python DB API.
884 #) Ensure the correct encoding is used for setting variable values.
885 #) Corrected handling of CLOB/NCLOB when using different encodings.
886 #) Corrected handling of TIMESTAMP WITH TIME ZONE attributes on objects.
887 #) Ensure that the array position passed to var.getvalue() does not exceed the
888 number of elements allocated in the array.
889 #) Reworked test suite and samples so that they are independent of each other
890 and so that the SQL scripts used to create/drop schemas are easily adjusted
891 to use different schema names, if desired.
892 #) Updated DB API test suite stub to support Python 3.
893 #) Added additional test cases and samples.
894 #) Documentation improvements.
895
896
897 Version 6.0 rc 1 (June 2017)
898 ----------------------------
899
900 #) Update to `ODPI-C rc 1 <https://oracle.github.io/odpi/doc/releasenotes.html
901 #version-2-0-0-rc-1-june-16-2017>`__.
902 #) The method :meth:`Cursor.setoutputsize` no longer needs to do anything,
903 since ODPI-C automatically manages buffer sizes of LONG and LONG RAW
904 columns.
905 #) Handle case when both precision and scale are zero, as occurs when
906 retrieving numeric expressions (`issue 34
907 <https://github.com/oracle/python-cx_Oracle/issues/34>`__).
908 #) OCI requires that both encoding and nencoding have values or that both
909 encoding and encoding do not have values. These parameters are used in
910 functions :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool`. The
911 missing value is set to its default value if one of the values is set and
912 the other is not (`issue 36
913 <https://github.com/oracle/python-cx_Oracle/issues/36>`__).
914 #) Permit use of both string and unicode for Python 2.7 for creating session
915 pools and for changing passwords (`issue 23
916 <https://github.com/oracle/python-cx_Oracle/issues/23>`__).
917 #) Corrected handling of BFILE LOBs.
918 #) Add script for dropping test schemas.
919 #) Documentation improvements.
920
921
922 Version 6.0 beta 2 (May 2017)
923 -----------------------------
924
925 #) Added support for getting/setting attributes of objects or element values
926 in collections that contain LOBs, BINARY_FLOAT values, BINARY_DOUBLE values
927 and NCHAR and NVARCHAR2 values. The error message for any types that are
928 not supported has been improved as well.
929 #) Enable temporary LOB caching in order to avoid disk I/O as
930 `suggested <https://github.com/oracle/odpi/issues/10>`__.
931 #) Added support for setting the debug level in ODPI-C, if desirable, by
932 setting environment variable DPI_DEBUG_LEVEL prior to building cx_Oracle.
933 #) Correct processing of strings in :meth:`Cursor.executemany` when a
934 larger string is found after a shorter string in the list of data bound to
935 the statement.
936 #) Correct handling of long Python integers that cannot fit inside a 64-bit C
937 integer (`issue 18
938 <https://github.com/oracle/python-cx_Oracle/issues/18>`__).
939 #) Correct creation of pool using external authentication.
940 #) Handle edge case when an odd number of zeroes trail the decimal point in a
941 value that is effectively zero (`issue 22
942 <https://github.com/oracle/python-cx_Oracle/issues/22>`__).
943 #) Prevent segfault under load when the attempt to create an error fails.
944 #) Eliminate resource leak when a standalone connection or pool is freed.
945 #) Correct `typo <https://github.com/oracle/python-cx_Oracle/issues/24>`__.
946 #) Correct handling of REF cursors when the array size is manipulated.
947 #) Prevent attempts from binding the cursor being executed to itself.
948 #) Correct reference count handling of parameters when creating a cursor.
949 #) Correct determination of the names of the bind variables in prepared SQL
950 statements (which behaves a little differently from PL/SQL statements).
951
952
953 Version 6.0 beta 1 (April 2017)
954 -------------------------------
955
956 #) Simplify building cx_Oracle considerably by use of
957 `ODPI-C <https://oracle.github.io/odpi>`__. This means that cx_Oracle can
958 now be built without Oracle Client header files or libraries and that at
959 runtime cx_Oracle can adapt to Oracle Client 11.2, 12.1 or 12.2 libraries
960 without needing to be rebuilt. This also means that wheels can now be
961 produced and installed via pip.
962 #) Added attribute :attr:`SessionPool.stmtcachesize` to support getting and
963 setting the default statement cache size for connections in the pool.
964 #) Added attribute :attr:`Connection.dbop` to support setting the database
965 operation that is to be monitored.
966 #) Added attribute :attr:`Connection.handle` to facilitate testing the
967 creation of a connection using a OCI service context handle.
968 #) Added parameters tag and matchanytag to the :meth:`cx_Oracle.connect`
969 and :meth:`SessionPool.acquire` methods and added parameters tag and retag
970 to the :meth:`SessionPool.release` method in order to support session
971 tagging.
972 #) Added parameter edition to the :meth:`cx_Oracle.SessionPool` method.
973 #) Added support for
974 `universal rowids <https://github.com/oracle/python-cx_Oracle/blob/master/
975 samples/UniversalRowids.py>`__.
976 #) Added support for `DML Returning of multiple rows
977 <https://github.com/oracle/python-cx_Oracle/blob/master/samples/
978 DMLReturningMultipleRows.py>`__.
979 #) Added attributes :attr:`Variable.actualElements` and
980 :attr:`Variable.values` to variables.
981 #) Added parameters region, sharding_key and super_sharding_key to the
982 :meth:`cx_Oracle.makedsn()` method to support connecting to a sharded
983 database (new in Oracle Database 12.2).
984 #) Added support for smallint and float data types in Oracle objects, as
985 `requested <https://github.com/oracle/python-cx_Oracle/issues/4>`__.
986 #) An exception is no longer raised when a collection is empty for methods
987 :meth:`Object.first()` and :meth:`Object.last()`. Instead, the value None
988 is returned to be consistent with the methods :meth:`Object.next()` and
989 :meth:`Object.prev()`.
990 #) If the environment variables NLS_LANG and NLS_NCHAR are being used, they
991 must be set before the module is imported. Using the encoding and nencoding
992 parameters to the :meth:`cx_Oracle.connect` and
993 :meth:`cx_Oracle.SessionPool` methods is a simpler alternative to setting
994 these environment variables.
995 #) Removed restriction on fetching LOBs across round trips to the database
996 (eliminates error "LOB variable no longer valid after subsequent fetch").
997 #) Removed requirement for specifying a maximum size when fetching LONG or
998 LONG raw columns. This also allows CLOB, NCLOB, BLOB and BFILE columns to
999 be fetched as strings or bytes without needing to specify a maximum size.
1000 #) Dropped deprecated parameter twophase from the :meth:`cx_Oracle.connect`
1001 method. Applications should set the :attr:`Connection.internal_name` and
1002 :attr:`Connection.external_name` attributes instead to a value appropriate
1003 to the application.
1004 #) Dropped deprecated parameters action, module and clientinfo from the
1005 :meth:`cx_Oracle.connect` method. The appcontext parameter should be used
1006 instead as shown in this `sample <https://github.com/oracle/
1007 python-cx_Oracle/blob/master/samples/AppContext.py>`__.
1008 #) Dropped deprecated attribute numbersAsString from
1009 :ref:`cursor objects <cursorobj>`. Use an output type handler instead as
1010 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
1011 master/samples/ReturnNumbersAsDecimals.py>`__.
1012 #) Dropped deprecated attributes cqqos and rowids from
1013 :ref:`subscription objects <subscrobj>`. Use the qos attribute instead as
1014 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
1015 master/samples/CQN.py>`__.
1016 #) Dropped deprecated parameters cqqos and rowids from the
1017 :meth:`Connection.subscribe()` method. Use the qos parameter instead as
1018 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
1019 master/samples/CQN.py>`__.
1020
1021
1022 Version 5.3 (March 2017)
1023 ------------------------
1024
1025 #) Added support for Python 3.6.
1026 #) Dropped support for Python versions earlier than 2.6.
1027 #) Dropped support for Oracle clients earlier than 11.2.
1028 #) Added support for
1029 :meth:`fetching implicit results<Cursor.getimplicitresults()>`
1030 (available in Oracle 12.1)
1031 #) Added support for :attr:`Transaction Guard <Connection.ltxid>` (available
1032 in Oracle 12.1).
1033 #) Added support for setting the
1034 :attr:`maximum lifetime <SessionPool.max_lifetime_session>` of pool
1035 connections (available in Oracle 12.1).
1036 #) Added support for large row counts (larger than 2 ** 32, available in
1037 Oracle 12.1)
1038 #) Added support for :meth:`advanced queuing <Connection.deq()>`.
1039 #) Added support for :meth:`scrollable cursors <Cursor.scroll()>`.
1040 #) Added support for :attr:`edition based redefinition <Connection.edition>`.
1041 #) Added support for :meth:`creating <ObjectType.newobject()>`, modifying and
1042 binding user defined types and collections.
1043 #) Added support for creating, modifying and binding PL/SQL records and
1044 collections (available in Oracle 12.1).
1045 #) Added support for binding :data:`native integers <cx_Oracle.NATIVE_INT>`.
1046 #) Enabled statement caching.
1047 #) Removed deprecated variable attributes maxlength and allocelems.
1048 #) Corrected support for setting the encoding and nencoding parameters when
1049 :meth:`creating a connection <cx_Oracle.Connection>` and added support for
1050 setting these when creating a session pool. These can now be used instead
1051 of setting the environment variables NLS_LANG and NLS_NCHAR.
1052 #) Use None instead of 0 for items in the :attr:`Cursor.description` attribute
1053 that do not have any validity.
1054 #) Changed driver name to match informal driver name standard used by Oracle
1055 for other drivers.
1056 #) Add check for maximum of 10,000 parameters when calling a stored procedure
1057 or function in order to prevent a possible improper memory access from
1058 taking place.
1059 #) Removed -mno-cygwin compile flag since it is no longer used in newer
1060 versions of the gcc compiler for Cygwin.
1061 #) Simplified test suite by combining Python 2 and 3 scripts into one script
1062 and separated out 12.1 features into a single script.
1063 #) Updated samples to use code that works on both Python 2 and 3
1064 #) Added support for pickling/unpickling error objects
1065 (`Issue #23 <https://bitbucket.org/anthony_tuininga/cx_oracle/issues/23>`__)
1066 #) Dropped support for callbacks on OCI functions.
1067 #) Removed deprecated types UNICODE, FIXED_UNICODE and LONG_UNICODE (use
1068 NCHAR, FIXED_NCHAR and LONG_NCHAR instead).
1069 #) Increased default array size to 100 (from 50) to match other drivers.
1070 #) Added support for setting the :attr:`~Connection.internal_name` and
1071 :attr:`~Connection.external_name` on the connection directly. The use of
1072 the twophase parameter is now deprecated. Applications should set the
1073 internal_name and external_name attributes directly to a value appropriate
1074 to the application.
1075 #) Added support for using application context when
1076 :meth:`creating a connection <cx_Oracle.Connection>`. This should be used
1077 in preference to the module, action and clientinfo parameters which are now
1078 deprecated.
1079 #) Reworked database change notification and continuous query notification to
1080 more closely align with the PL/SQL implementation and prepare for sending
1081 notifications for AQ messages. The following changes were made:
1082
1083 - added constant :data:`~cx_Oracle.SUBSCR_QOS_BEST_EFFORT` to replace
1084 deprecated constant SUBSCR_CQ_QOS_BEST_EFFORT
1085 - added constant :data:`~cx_Oracle.SUBSCR_QOS_QUERY` to replace
1086 deprecated constant SUBSCR_CQ_QOS_QUERY
1087 - added constant :data:`~cx_Oracle.SUBSCR_QOS_DEREG_NFY` to replace
1088 deprecated constant SUBSCR_QOS_PURGE_ON_NTFN
1089 - added constant :data:`~cx_Oracle.SUBSCR_QOS_ROWIDS` to replace parameter
1090 rowids for method :meth:`Connection.subscribe()`
1091 - deprecated parameter cqqos for method :meth:`Connection.subscribe()`. The
1092 qos parameter should be used instead.
1093 - dropped constants SUBSCR_CQ_QOS_CLQRYCACHE, SUBSCR_QOS_HAREG,
1094 SUBSCR_QOS_MULTICBK, SUBSCR_QOS_PAYLOAD, SUBSCR_QOS_REPLICATE, and
1095 SUBSCR_QOS_SECURE since they were never actually used
1096 #) Deprecated use of the numbersAsStrings attribute on cursors. An output type
1097 handler should be used instead.
1098
1099
1100 Version 5.2.1 (January 2016)
1101 ----------------------------
1102
1103 #) Added support for Python 3.5.
1104 #) Removed password attribute from connection and session pool objects in
1105 order to promote best security practices (if stored in RAM in cleartext it
1106 can be read in process dumps, for example). For those who would like to
1107 retain this feature, a subclass of Connection could be used to store the
1108 password.
1109 #) Added optional parameter externalauth to SessionPool() which enables wallet
1110 based or other external authentication mechanisms to be used.
1111 #) Use the national character set encoding when required (when char set form
1112 is SQLCS_NCHAR); otherwise, the wrong encoding would be used if the
1113 environment variable NLS_NCHAR is set.
1114 #) Added support for binding boolean values to PL/SQL blocks and stored
1115 procedures (available in Oracle 12.1).
1116
1117
1118 Version 5.2 (June 2015)
1119 -----------------------
1120
1121 #) Added support for strings up to 32k characters (new in Oracle 12c).
1122 #) Added support for getting array DML row counts (new in Oracle 12c).
1123 #) Added support for fetching batch errors.
1124 #) Added support for LOB values larger than 4 GB.
1125 #) Added support for connections as SYSASM.
1126 #) Added support for building without any configuration changes to the machine
1127 when using instant client RPMs on Linux.
1128 #) Added types NCHAR, FIXED_NCHAR and LONG_NCHAR to replace the types UNICODE,
1129 FIXED_UNICODE and LONG_UNICODE (which are now deprecated). These types are
1130 available in Python 3 as well so they can be used to specify the use of
1131 NCHAR type fields when binding or using setinputsizes().
1132 #) Fixed binding of booleans in Python 3.x.
1133 #) Test suite now sets NLS_LANG if not already set.
1134 #) Enhanced documentation for connection.action attribute and added note
1135 on cursor.parse() method to make clear that DDL statements are executed
1136 when parsed.
1137 #) Removed remaining remnants of support Oracle 9i.
1138 #) Added __version__ attribute to conform with PEP 396.
1139 #) Ensure that sessions are released to the pool when calling
1140 connection.close()
1141 (`Issue #2 <https://bitbucket.org/anthony_tuininga/cx_oracle/issue/2>`__)
1142 #) Fixed handling of datetime intervals
1143 (`Issue #7 <https://bitbucket.org/anthony_tuininga/cx_oracle/issue/7>`__)
1144
1145
1146 Version 5.1.3 (May 2014)
1147 ------------------------
1148
1149 #) Added support for Oracle 12c.
1150 #) Added support for Python 3.4.
1151 #) Added support for query result set change notification. Thanks to Glen
1152 Walker for the patch.
1153 #) Ensure that in Python 3.x that NCHAR and NVARCHAR2 and NCLOB columns are
1154 retrieved properly without conversion issues. Thanks to Joakim Andersson
1155 for pointing out the issue and the possible solution.
1156 #) Fix bug when an exception is caught and then another exception is raised
1157 while handling that exception in Python 3.x. Thanks to Boris Dzuba for
1158 pointing out the issue and providing a test case.
1159 #) Enhance performance returning integers between 10 and 18 digits on 64-bit
1160 platforms that support it. Thanks for Shai Berger for the initial patch.
1161 #) Fixed two memory leaks.
1162 #) Fix to stop current_schema from throwing a MemoryError on 64-bit platforms
1163 on occasion. Thanks to Andrew Horton for the fix.
1164 #) Class name of cursors changed to real name cx_Oracle.Cursor.
1165
1166
1167 Version 5.1.2 (July 2012)
1168 -------------------------
1169
1170 #) Added support for LONG_UNICODE which is a type used to handle long unicode
1171 strings. These are not explicitly supported in Oracle but can be used to
1172 bind to NCLOB, for example, without getting the error "unimplemented or
1173 unreasonable conversion requested".
1174 #) Set the row number in a cursor when executing PL/SQL blocks as requested
1175 by Robert Ritchie.
1176 #) Added support for setting the module, action and client_info attributes
1177 during connection so that logon triggers will see the supplied values, as
1178 requested by Rodney Barnett.
1179
1180
1181 Version 5.1.1 (October 2011)
1182 ----------------------------
1183
1184 #) Simplify management of threads for callbacks performed by database change
1185 notification and eliminate a crash that occurred under high load in
1186 certain situations. Thanks to Calvin S. for noting the issue and suggesting
1187 a solution and testing the patch.
1188 #) Force server detach on close so that the connection is completely closed
1189 and not just the session as before.
1190 #) Force use of OCI_UTF16ID for NCLOBs as using the default character set
1191 would result in ORA-03127 with Oracle 11.2.0.2 and UTF8 character set.
1192 #) Avoid attempting to clear temporary LOBs a second time when destroying the
1193 variable as in certain situations this results in spurious errors.
1194 #) Added additional parameter service_name to makedsn() which can be used to
1195 use the service_name rather than the SID in the DSN string that is
1196 generated.
1197 #) Fix cursor description in test suite to take into account the number of
1198 bytes per character.
1199 #) Added tests for NCLOBS to the test suite.
1200 #) Removed redundant code in setup.py for calculating the library path.
1201
1202
1203 Version 5.1 (March 2011)
1204 ------------------------
1205
1206 #) Remove support for UNICODE mode and permit Unicode to be passed through in
1207 everywhere a string may be passed in. This means that strings will be
1208 passed through to Oracle using the value of the NLS_LANG environment
1209 variable in Python 3.x as well. Doing this eliminated a bunch of problems
1210 that were discovered by using UNICODE mode and also removed an unnecessary
1211 restriction in Python 2.x that Unicode could not be used in connect strings
1212 or SQL statements, for example.
1213 #) Added support for creating an empty object variable via a named type, the
1214 first step to adding full object support.
1215 #) Added support for Python 3.2.
1216 #) Account for lib64 used on x86_64 systems. Thanks to Alex Wood for supplying
1217 the patch.
1218 #) Clear up potential problems when calling cursor.close() ahead of the
1219 cursor being freed by going out of scope.
1220 #) Avoid compilation difficulties on AIX5 as OCIPing does not appear to be
1221 available on that platform under Oracle 10g Release 2. Thanks to
1222 Pierre-Yves Fontaniere for the patch.
1223 #) Free temporary LOBs prior to each fetch in order to avoid leaking them.
1224 Thanks to Uwe Hoffmann for the initial patch.
1225
1226
1227 Version 5.0.4 (July 2010)
1228 -------------------------
1229
1230 #) Added support for Python 2.7.
1231 #) Added support for new parameter (port) for subscription() call which allows
1232 the client to specify the listening port for callback notifications from
1233 the database server. Thanks to Geoffrey Weber for the initial patch.
1234 #) Fixed compilation under Oracle 9i.
1235 #) Fixed a few error messages.
1236
1237
1238 Version 5.0.3 (February 2010)
1239 -----------------------------
1240
1241 #) Added support for 64-bit Windows.
1242 #) Added support for Python 3.1 and dropped support for Python 3.0.
1243 #) Added support for keyword parameters in cursor.callproc() and
1244 cursor.callfunc().
1245 #) Added documentation for the UNICODE and FIXED_UNICODE variable types.
1246 #) Added extra link arguments required for Mac OS X as suggested by Jason
1247 Woodward.
1248 #) Added additional error codes to the list of error codes that raise
1249 OperationalError rather than DatabaseError.
1250 #) Fixed calculation of display size for strings with national database
1251 character sets that are not the default AL16UTF16.
1252 #) Moved the resetting of the setinputsizes flag before the binding takes
1253 place so that if an error takes place and a new statement is prepared
1254 subsequently, spurious errors will not occur.
1255 #) Fixed compilation with Oracle 10g Release 1.
1256 #) Tweaked documentation based on feedback from a number of people.
1257 #) Added support for running the test suite using "python setup.py test"
1258 #) Added support for setting the CLIENT_IDENTIFIER value in the v$session
1259 table for connections.
1260 #) Added exception when attempting to call executemany() with arrays which is
1261 not supported by the OCI.
1262 #) Fixed bug when converting from decimal would result in OCI-22062 because
1263 the locale decimal point was not a period. Thanks to Amaury Forgeot d'Arc
1264 for the solution to this problem.
1265
1266
1267 Version 5.0.2 (May 2009)
1268 ------------------------
1269
1270 #) Fix creation of temporary NCLOB values and the writing of NCLOB values in
1271 non Unicode mode.
1272 #) Re-enabled parsing of non select statements as requested by Roy Terrill.
1273 #) Implemented a parse error offset as requested by Catherine Devlin.
1274 #) Removed lib subdirectory when forcing RPATH now that the library directory
1275 is being calculated exactly in setup.py.
1276 #) Added an additional cast in order to support compiling by Microsoft
1277 Visual C++ 2008 as requested by Marco de Paoli.
1278 #) Added additional include directory to setup.py in order to support
1279 compiling by Microsoft Visual Studio was requested by Jason Coombs.
1280 #) Fixed a few documentation issues.
1281
1282
1283 Version 5.0.1 (February 2009)
1284 -----------------------------
1285
1286 #) Added support for database change notification available in Oracle 10g
1287 Release 2 and higher.
1288 #) Fix bug where NCLOB data would be corrupted upon retrieval (non Unicode
1289 mode) or would generate exception ORA-24806 (LOB form mismatch). Oracle
1290 insists upon differentiating between CLOB and NCLOB no matter which
1291 character set is being used for retrieval.
1292 #) Add new attributes size, bufferSize and numElements to variable objects,
1293 deprecating allocelems (replaced by numElements) and maxlength (replaced
1294 by bufferSize)
1295 #) Avoid increasing memory allocation for strings when using variable width
1296 character sets and increasing the number of elements in a variable during
1297 executemany().
1298 #) Tweaked code in order to ensure that cx_Oracle can compile with Python
1299 3.0.1.
1300
1301
1302 Version 5.0 (December 2008)
1303 ---------------------------
1304
1305 #) Added support for Python 3.0 with much help from Amaury Forgeot d'Arc.
1306 #) Removed support for Python 2.3 and Oracle 8i.
1307 #) Added support for full unicode mode in Python 2.x where all strings are
1308 passed in and returned as unicode (module must be built in this mode)
1309 rather than encoded strings
1310 #) nchar and nvarchar columns now return unicode instead of encoded strings
1311 #) Added support for an output type handler and/or an input type handler to be
1312 specified at the connection and cursor levels.
1313 #) Added support for specifying both input and output converters for variables
1314 #) Added support for specifying the array size of variables that are created
1315 using the cursor.var() method
1316 #) Added support for events mode and database resident connection pooling
1317 (DRCP) in Oracle 11g.
1318 #) Added support for changing the password during construction of a new
1319 connection object as well as after the connection object has been created
1320 #) Added support for the interval day to second data type in Oracle,
1321 represented as datetime.timedelta objects in Python.
1322 #) Added support for getting and setting the current_schema attribute for a
1323 session
1324 #) Added support for proxy authentication in session pools as requested by
1325 Michael Wegrzynek (and thanks for the initial patch as well).
1326 #) Modified connection.prepare() to return a boolean indicating if a
1327 transaction was actually prepared in order to avoid the error ORA-24756
1328 (transaction does not exist).
1329 #) Raise a cx_Oracle.Error instance rather than a string for column
1330 truncation errors as requested by Helge Tesdal.
1331 #) Fixed handling of environment handles in session pools in order to allow
1332 session pools to fetch objects without exceptions taking place.
1333
1334
1335 Version 4.4.1 (October 2008)
1336 ----------------------------
1337
1338 #) Make the bind variables and fetch variables accessible although they need
1339 to be treated carefully since they are used internally; support added for
1340 forward compatibility with version 5.x.
1341 #) Include the "cannot insert null value" in the list of errors that are
1342 treated as integrity errors as requested by Matt Boersma.
1343 #) Use a cx_Oracle.Error instance rather than a string to hold the error when
1344 truncation (ORA-1406) takes place as requested by Helge Tesdal.
1345 #) Added support for fixed char, old style varchar and timestamp attribute
1346 values in objects.
1347 #) Tweaked setup.py to check for the Oracle version up front rather than
1348 during the build in order to produce more meaningful errors and simplify
1349 the code.
1350 #) In setup.py added proper detection for the instant client on Mac OS X as
1351 recommended by Martijn Pieters.
1352 #) In setup.py, avoided resetting the extraLinkArgs on Mac OS X as doing so
1353 prevents simple modification where desired as expressed by Christian
1354 Zagrodnick.
1355 #) Added documentation on exception handling as requested by Andreas Mock, who
1356 also graciously provided an initial patch.
1357 #) Modified documentation indicating that the password attribute on connection
1358 objects can be written.
1359 #) Added documentation warning that parameters not passed in during subsequent
1360 executions of a statement will retain their original values as requested by
1361 Harald Armin Massa.
1362 #) Added comments indicating that an Oracle client is required since so many
1363 people find this surprising.
1364 #) Removed all references to Oracle 8i from the documentation and version 5.x
1365 will eliminate all vestiges of support for this version of the Oracle
1366 client.
1367 #) Added additional link arguments for Cygwin as requested by Rob Gillen.
1368
1369
1370 Version 4.4 (June 2008)
1371 -----------------------
1372
1373 #) Fix setup.py to handle the Oracle instant client and Oracle XE on both
1374 Linux and Windows as pointed out by many. Thanks also to the many people
1375 who also provided patches.
1376 #) Set the default array size to 50 instead of 1 as the DB API suggests
1377 because the performance difference is so drastic and many people have
1378 recommended that the default be changed.
1379 #) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS around each blocking
1380 call for LOBs as requested by Jason Conroy who also provided an initial
1381 patch and performed a number of tests that demonstrate the new code is much
1382 more responsive.
1383 #) Add support for acquiring cursor.description after a parse.
1384 #) Defer type assignment when performing executemany() until the last possible
1385 moment if the value being bound in is null as suggested by Dragos Dociu.
1386 #) When dropping a connection from the pool, ignore any errors that occur
1387 during the rollback; unfortunately, Oracle decides to commit data even when
1388 dropping a connection from the pool instead of rolling it back so the
1389 attempt still has to be made.
1390 #) Added support for setting CLIENT_DRIVER in V$SESSION_CONNECT_INFO in Oracle
1391 11g and higher.
1392 #) Use cx_Oracle.InterfaceError rather than the builtin RuntimeError when
1393 unable to create the Oracle environment object as requested by Luke Mewburn
1394 since the error is specific to Oracle and someone attempting to catch any
1395 exception cannot simply use cx_Oracle.Error.
1396 #) Translated some error codes to OperationalError as requested by Matthew
1397 Harriger; translated if/elseif/else logic to switch statement to make it
1398 more readable and to allow for additional translation if desired.
1399 #) Transformed documentation to new format using restructured text. Thanks to
1400 Waldemar Osuch for contributing the initial draft of the new documentation.
1401 #) Allow the password to be overwritten by a new value as requested by Alex
1402 VanderWoude; this value is retained as a convenience to the user and not
1403 used by anything in the module; if changed externally it may be convenient
1404 to keep this copy up to date.
1405 #) Cygwin is on Windows so should be treated in the same way as noted by
1406 Matthew Cahn.
1407 #) Add support for using setuptools if so desired as requested by Shreya
1408 Bhatt.
1409 #) Specify that the version of Oracle 10 that is now primarily used is 10.2,
1410 not 10.1.
1411
1412
1413 Version 4.3.3 (October 2007)
1414 ----------------------------
1415
1416 #) Added method ping() on connections which can be used to test whether or not
1417 a connection is still active (available in Oracle 10g R2).
1418 #) Added method cx_Oracle.clientversion() which returns a 5-tuple giving the
1419 version of the client that is in use (available in Oracle 10g R2).
1420 #) Added methods startup() and shutdown() on connections which can be used to
1421 startup and shutdown databases (available in Oracle 10g R2).
1422 #) Added support for Oracle 11g.
1423 #) Added samples directory which contains a handful of scripts containing
1424 sample code for more advanced techniques. More will follow in future
1425 releases.
1426 #) Prevent error "ORA-24333: zero iteration count" when calling executemany()
1427 with zero rows as requested by Andreas Mock.
1428 #) Added methods __enter__() and __exit__() on connections to support using
1429 connections as context managers in Python 2.5 and higher. The context
1430 managed is the transaction state. Upon exit the transaction is either
1431 rolled back or committed depending on whether an exception took place or
1432 not.
1433 #) Make the search for the lib32 and lib64 directories automatic for all
1434 platforms.
1435 #) Tweak the setup configuration script to include all of the metadata and
1436 allow for building the module within another setup configuration script
1437 #) Include the Oracle version in addition to the Python version in the build
1438 directories that are created and in the names of the binary packages that
1439 are created.
1440 #) Remove unnecessary dependency on win32api to build module on Windows.
1441
1442
1443 Version 4.3.2 (August 2007)
1444 ---------------------------
1445
1446 #) Added methods open(), close(), isopen() and getchunksize() in order to
1447 improve performance of reading/writing LOB values in chunks.
1448 #) Fixed support for native doubles and floats in Oracle 10g; added new type
1449 NATIVE_FLOAT to allow specification of a variable of that specific type
1450 where desired. Thanks to D.R. Boxhoorn for pointing out the fact that this
1451 was not working properly when the arraysize was anything other than 1.
1452 #) When calling connection.begin(), only create a new transaction handle if
1453 one is not already associated with the connection. Thanks to Andreas Mock
1454 for discovering this and for Amaury Forgeot d'Arc for diagnosing the
1455 problem and pointing the way to a solution.
1456 #) Added attribute cursor.rowfactory which allows a method to be called for
1457 each row that is returned; this is about 20% faster than calling the method
1458 in Python using the idiom [method(\*r) for r in cursor].
1459 #) Attempt to locate an Oracle installation by looking at the PATH if the
1460 environment variable ORACLE_HOME is not set; this is of primary use on
1461 Windows where this variable should not normally be set.
1462 #) Added support for autocommit mode as requested by Ian Kelly.
1463 #) Added support for connection.stmtcachesize which allows for both reading
1464 and writing the size of the statement cache size. This parameter can make a
1465 huge difference with the length of time taken to prepare statements. Added
1466 support for setting the statement tag when preparing a statement. Both of
1467 these were requested by Bjorn Sandberg who also provided an initial patch.
1468 #) When copying the value of a variable, copy the return code as well.
1469
1470
1471 Version 4.3.1 (April 2007)
1472 --------------------------
1473
1474 #) Ensure that if the client buffer size exceeds 4000 bytes that the server
1475 buffer size does not as strings may only contain 4000 bytes; this allows
1476 handling of multibyte character sets on the server as well as the client.
1477 #) Added support for using buffer objects to populate binary data and made the
1478 Binary() constructor the buffer type as requested by Ken Mason.
1479 #) Fix potential crash when using full optimization with some compilers.
1480 Thanks to Aris Motas for noticing this and providing the initial patch and
1481 to Amaury Forgeot d'Arc for providing an even simpler solution.
1482 #) Pass the correct charset form in to the write call in order to support
1483 writing to national character set LOB values properly. Thanks to Ian Kelly
1484 for noticing this discrepancy.
1485
1486
1487 Version 4.3 (March 2007)
1488 ------------------------
1489
1490 #) Added preliminary support for fetching Oracle objects (SQL types) as
1491 requested by Kristof Beyls (who kindly provided an initial patch).
1492 Additional work needs to be done to support binding and updating objects
1493 but the basic structure is now in place.
1494 #) Added connection.maxBytesPerCharacter which indicates the maximum number of
1495 bytes each character can use; use this value to also determine the size of
1496 local buffers in order to handle discrepancies between the client character
1497 set and the server character set. Thanks to Andreas Mock for providing the
1498 initial patch and working with me to resolve this issue.
1499 #) Added support for querying native floats in Oracle 10g as requested by
1500 Danny Boxhoorn.
1501 #) Add support for temporary LOB variables created via PL/SQL instead of only
1502 directly by cx_Oracle; thanks to Henning von Bargen for discovering this
1503 problem.
1504 #) Added support for specifying variable types using the builtin types int,
1505 float, str and datetime.date which allows for finer control of what type of
1506 Python object is returned from cursor.callfunc() for example.
1507 #) Added support for passing booleans to callproc() and callfunc() as
1508 requested by Anana Aiyer.
1509 #) Fixed support for 64-bit environments in Python 2.5.
1510 #) Thanks to Filip Ballegeer and a number of his co-workers, an intermittent
1511 crash was tracked down; specifically, if a connection is closed, then the
1512 call to OCIStmtRelease() will free memory twice. Preventing the call when
1513 the connection is closed solves the problem.
1514
1515
1516 Version 4.2.1 (September 2006)
1517 ------------------------------
1518
1519 #) Added additional type (NCLOB) to handle CLOBs that use the national
1520 character set as requested by Chris Dunscombe.
1521 #) Added support for returning cursors from functions as requested by Daniel
1522 Steinmann.
1523 #) Added support for getting/setting the "get" mode on session pools as
1524 requested by Anand Aiyer.
1525 #) Added support for binding subclassed cursors.
1526 #) Fixed binding of decimal objects with absolute values less than 0.1.
1527
1528
1529 Version 4.2 (July 2006)
1530 -----------------------
1531
1532 #) Added support for parsing an Oracle statement as requested by Patrick
1533 Blackwill.
1534 #) Added support for BFILEs at the request of Matthew Cahn.
1535 #) Added support for binding decimal.Decimal objects to cursors.
1536 #) Added support for reading from NCLOBs as requested by Chris Dunscombe.
1537 #) Added connection attributes encoding and nencoding which return the IANA
1538 character set name for the character set and national character set in use
1539 by the client.
1540 #) Rework module initialization to use the techniques recommended by the
1541 Python documentation as one user was experiencing random segfaults due
1542 to the use of the module dictionary after the initialization was complete.
1543 #) Removed support for the OPT_Threading attribute. Use the threaded keyword
1544 when creating connections and session pools instead.
1545 #) Removed support for the OPT_NumbersAsStrings attribute. Use the
1546 numbersAsStrings attribute on cursors instead.
1547 #) Use type long rather than type int in order to support long integers on
1548 64-bit machines as reported by Uwe Hoffmann.
1549 #) Add cursor attribute "bindarraysize" which is defaulted to 1 and is used
1550 to determine the size of the arrays created for bind variables.
1551 #) Added repr() methods to provide something a little more useful than the
1552 standard type name and memory address.
1553 #) Added keyword parameter support to the functions that imply such in the
1554 documentation as requested by Harald Armin Massa.
1555 #) Treat an empty dictionary passed through to cursor.execute() as keyword
1556 parameters the same as if no keyword parameters were specified at all, as
1557 requested by Fabien Grumelard.
1558 #) Fixed memory leak when a LOB read would fail.
1559 #) Set the LDFLAGS value in the environment rather than directly in the
1560 setup.py file in order to satisfy those who wish to enable the use of
1561 debugging symbols.
1562 #) Use __DATE__ and __TIME__ to determine the date and time of the build
1563 rather than passing it through directly.
1564 #) Use Oracle types and add casts to reduce warnings as requested by Amaury
1565 Forgeot d'Arc.
1566 #) Fixed typo in error message.
1567
1568
1569 Version 4.1.2 (December 2005)
1570 -----------------------------
1571
1572 #) Restore support of Oracle 9i features when using the Oracle 10g client.
1573
1574
1575 Version 4.1.1 (December 2005)
1576 -----------------------------
1577
1578 #) Add support for dropping a connection from a session pool.
1579 #) Add support for write only attributes "module", "action" and "clientinfo"
1580 which work only in Oracle 10g as requested by Egor Starostin.
1581 #) Add support for pickling database errors.
1582 #) Use the previously created bind variable as a template if available when
1583 creating a new variable of a larger size. Thanks to Ted Skolnick for the
1584 initial patch.
1585 #) Fixed tests to work properly in the Python 2.4 environment where dates and
1586 timestamps are different Python types. Thanks to Henning von Bargen for
1587 pointing this out.
1588 #) Added additional directories to search for include files and libraries in
1589 order to better support the Oracle 10g instant client.
1590 #) Set the internal fetch number to 0 in order to satisfy very picky source
1591 analysis tools as requested by Amaury Fogeot d'Arc.
1592 #) Improve the documentation for building and installing the module from
1593 source as some people are unaware of the standard methods for building
1594 Python modules using distutils.
1595 #) Added note in the documentation indicating that the arraysize attribute
1596 can drastically affect performance of queries since this seems to be a
1597 common misunderstanding of first time users of cx_Oracle.
1598 #) Add a comment indicating that on HP-UX Itanium with Oracle 10g the library
1599 ttsh10 must also be linked against. Thanks to Bernard Delmee for the
1600 information.
1601
1602
1603 Version 4.1 (January 2005)
1604 --------------------------
1605
1606 #) Fixed bug where subclasses of Cursor do not pass the connection in the
1607 constructor causing a segfault.
1608 #) DDL statements must be reparsed before execution as noted by Mihai
1609 Ibanescu.
1610 #) Add support for setting input sizes by position.
1611 #) Fixed problem with catching an exception during execute and then still
1612 attempting to perform a fetch afterwards as noted by Leith Parkin.
1613 #) Rename the types so that they can be pickled and unpickled. Thanks to Harri
1614 Pasanen for pointing out the problem.
1615 #) Handle invalid NLS_LANG setting properly (Oracle seems to like to provide a
1616 handle back even though it is invalid) and determine the number of bytes
1617 per character in order to allow for proper support in the future of
1618 multibyte and variable width character sets.
1619 #) Remove date checking from the native case since Python already checks that
1620 dates are valid; enhance error message when invalid dates are encountered
1621 so that additional processing can be done.
1622 #) Fix bug executing SQL using numeric parameter names with predefined
1623 variables (such as what takes place when calling stored procedures with out
1624 parameters).
1625 #) Add support for reading CLOB values using multibyte or variable length
1626 character sets.
1627
1628
1629 Version 4.1 beta 1 (September 2004)
1630 -----------------------------------
1631
1632 #) Added support for Python 2.4. In Python 2.4, the datetime module is used
1633 for both binding and fetching of date and timestamp data. In Python 2.3,
1634 objects from the datetime module can be bound but the internal datetime
1635 objects will be returned from queries.
1636 #) Added pickling support for LOB and datetime data.
1637 #) Fully qualified the table name that was missing in an alter table
1638 statement in the setup test script as noted by Marc Gehling.
1639 #) Added a section allowing for the setting of the RPATH linker directive in
1640 setup.py as requested by Iustin Pop.
1641 #) Added code to raise a programming error exception when an attempt is made
1642 to access a LOB locator variable in a subsequent fetch.
1643 #) The username, password and dsn (tnsentry) are stored on the connection
1644 object when specified, regardless of whether or not a standard connection
1645 takes place.
1646 #) Added additional module level constant called "LOB" as requested by Joseph
1647 Canedo.
1648 #) Changed exception type to IntegrityError for constraint violations as
1649 requested by Joseph Canedo.
1650 #) If scale and precision are not specified, an attempt is made to return a
1651 long integer as requested by Joseph Canedo.
1652 #) Added workaround for Oracle bug which returns an invalid handle when the
1653 prepare call fails. Thanks to [email protected] for providing the code that
1654 demonstrated the problem.
1655 #) The cursor method arrayvar() will now accept the actual list so that it is
1656 not necessary to call cursor.arrayvar() followed immediately by
1657 var.setvalue().
1658 #) Fixed bug where attempts to execute the statement "None" with bind
1659 variables would cause a segmentation fault.
1660 #) Added support for binding by position (paramstyle = "numeric").
1661 #) Removed memory leak created by calls to OCIParamGet() which were not
1662 mirrored by calls to OCIDescriptorFree(). Thanks to Mihai Ibanescu for
1663 pointing this out and providing a patch.
1664 #) Added support for calling cursor.executemany() with statement None
1665 implying that the previously prepared statement ought to be executed.
1666 Thanks to Mihai Ibanescu for providing a patch.
1667 #) Added support for rebinding variables when a subsequent call to
1668 cursor.executemany() uses a different number of rows. Thanks to Mihai
1669 Ibanescu for supplying a patch.
1670 #) The microseconds are now displayed in datetime variables when nonzero
1671 similar to method used in the datetime module.
1672 #) Added support for binary_float and binary_double columns in Oracle 10g.
1673
1674
1675 Version 4.0.1 (February 2004)
1676 -----------------------------
1677
1678 #) Fixed bugs on 64-bit platforms that caused segmentation faults and bus
1679 errors in session pooling and determining the bind variables associated
1680 with a statement.
1681 #) Modified test suite so that 64-bit platforms are tested properly.
1682 #) Added missing commit statements in the test setup scripts. Thanks to Keith
1683 Lyon for pointing this out.
1684 #) Fix setup.py for Cygwin environments. Thanks to Doug Henderson for
1685 providing the necessary fix.
1686 #) Added support for compiling cx_Oracle without thread support. Thanks to
1687 Andre Reitz for pointing this out.
1688 #) Added support for a new keyword parameter called threaded on connections
1689 and session pools. This parameter defaults to False and indicates whether
1690 threaded mode ought to be used. It replaces the module level attribute
1691 OPT_Threading although examining the attribute will be retained until the
1692 next release at least.
1693 #) Added support for a new keyword parameter called twophase on connections.
1694 This parameter defaults to False and indicates whether support for two
1695 phase (distributed or global) transactions ought to be present. Note that
1696 support for distributed transactions is buggy when crossing major version
1697 boundaries (Oracle 8i to Oracle 9i for example).
1698 #) Ensure that the rowcount attribute is set properly when an exception is
1699 raised during execution. Thanks to Gary Aviv for pointing out this problem
1700 and its solution.
1701
1702
1703 Version 4.0 (December 2003)
1704 ---------------------------
1705
1706 #) Added support for subclassing connections, cursors and session pools. The
1707 changes involved made it necessary to drop support for Python 2.1 and
1708 earlier although a branch exists in CVS to allow for support of Python 2.1
1709 and earlier if needed.
1710 #) Connections and session pools can now be created with keyword parameters,
1711 not just sequential parameters.
1712 #) Queries now return integers whenever possible and long integers if the
1713 number will overflow a simple integer. Floats are only returned when it is
1714 known that the number is a floating point number or the integer conversion
1715 fails.
1716 #) Added initial support for user callbacks on OCI functions. See the
1717 documentation for more details.
1718 #) Add support for retrieving the bind variable names associated with a
1719 cursor with a new method bindnames().
1720 #) Add support for temporary LOB variables. This means that setinputsizes()
1721 can be used with the values CLOB and BLOB to create these temporary LOB
1722 variables and allow for the equivalent of empty_clob() and empty_blob()
1723 since otherwise Oracle will treat empty strings as NULL values.
1724 #) Automatically switch to long strings when the data size exceeds the
1725 maximum string size that Oracle allows (4000 characters) and raise an
1726 error if an attempt is made to set a string variable to a size that it
1727 does not support. This avoids truncation errors as reported by Jon Franz.
1728 #) Add support for global (distributed) transactions and two phase commit.
1729 #) Force the NLS settings for the session so that test tables are populated
1730 correctly in all circumstances; problems were noted by Ralf Braun and
1731 Allan Poulsen.
1732 #) Display error messages using the environment handle when the error handle
1733 has not yet been created; this provides better error messages during this
1734 rather rare situation.
1735 #) Removed memory leak in callproc() that was reported by Todd Whiteman.
1736 #) Make consistent the calls to manipulate memory; otherwise segfaults can
1737 occur when the pymalloc option is used, as reported by Matt Hoskins.
1738 #) Force a rollback when a session is released back to the session pool.
1739 Apparently the connections are not as stateless as Oracle's documentation
1740 suggests and this makes the logic consistent with normal connections.
1741 #) Removed module method attach(). This can be replaced with a call to
1742 Connection(handle=) if needed.
1743
1744
1745 Version 3.1 (August 2003)
1746 -------------------------
1747
1748 #) Added support for connecting with SYSDBA and SYSOPER access which is
1749 needed for connecting as sys in Oracle 9i.
1750 #) Only check the dictionary size if the variable is not NULL; otherwise, an
1751 error takes place which is not caught or cleared; this eliminates a
1752 spurious "Objects/dictobject.c:1258: bad argument to internal function" in
1753 Python 2.3.
1754 #) Add support for session pooling. This is only support for Oracle 9i but
1755 is amazingly fast -- about 100 times faster than connecting.
1756 #) Add support for statement caching when pooling sessions, this reduces the
1757 parse time considerably. Unfortunately, the Oracle OCI does not allow this
1758 to be easily turned on for normal sessions.
1759 #) Add method trim() on CLOB and BLOB variables for trimming the size.
1760 #) Add support for externally identified users; to use this feature leave the
1761 username and password fields empty when connecting.
1762 #) Add method cancel() on connection objects to cancel long running queries.
1763 Note that this only works on non-Windows platforms.
1764 #) Add method callfunc() on cursor objects to allow calling a function
1765 without using an anonymous PL/SQL block.
1766 #) Added documentation on objects that were not documented. At this point all
1767 objects, methods and constants in cx_Oracle have been documented.
1768 #) Added support for timestamp columns in Oracle 9i.
1769 #) Added module level method makedsn() which creates a data source name given
1770 the host, port and SID.
1771 #) Added constant "buildtime" which is the time when the module was built as
1772 an additional means of identifying the build that is in use.
1773 #) Binding a value that is incompatible to the previous value that was bound
1774 (data types do not match or array size is larger) will now result in a
1775 new bind taking place. This is more consistent with the DB API although
1776 it does imply a performance penalty when used.
1777
1778
1779 Version 3.0a (June 2003)
1780 ------------------------
1781
1782 #) Fixed bug where zero length PL/SQL arrays were being mishandled
1783 #) Fixed support for the data type "float" in Oracle; added one to the
1784 display size to allow for the sign of the number, if necessary; changed
1785 the display size of unconstrained numbers to 127, which is the largest
1786 number that Oracle can handle
1787 #) Added support for retrieving the description of a bound cursor before
1788 fetching it
1789 #) Fixed a couple of build issues on Mac OS X, AIX and Solaris (64-bit)
1790 #) Modified documentation slightly based on comments from several people
1791 #) Included files in MANIFEST that are needed to generate the binaries
1792 #) Modified test suite to work within the test environment at Computronix
1793 as well as within the packages that are distributed
1794
1795
1796 Version 3.0 (March 2003)
1797 ------------------------
1798
1799 #) Removed support for connection to Oracle7 databases; it is entirely
1800 possible that it will still work but I no longer have any way of testing
1801 and Oracle has dropped any meaningful support for Oracle7 anyway
1802 #) Fetching of strings is now done with predefined memory areas rather than
1803 dynamic memory areas; dynamic fetching of strings was causing problems
1804 with Oracle 9i in some instances and databases using a different character
1805 set other than US ASCII
1806 #) Fixed bug where segfault would occur if the '/' character preceded the '@'
1807 character in a connect string
1808 #) Added two new cursor methods var() and arrayvar() in order to eliminate
1809 the need for setinputsizes() when defining PL/SQL arrays and as a generic
1810 method of acquiring bind variables directly when needed
1811 #) Fixed support for binding cursors and added support for fetching cursors
1812 (these are known as ref cursors in PL/SQL).
1813 #) Eliminated discrepancy between the array size used internally and the
1814 array size specified by the interface user; this was done earlier to avoid
1815 bus errors on 64-bit platforms but another way has been found to get
1816 around that issue and a number of people were getting confused because of
1817 the discrepancy
1818 #) Added support for the attribute "connection" on cursors, an optional
1819 DB API extension
1820 #) Added support for passing a dictionary as the second parameter for the
1821 cursor.execute() method in order to comply with the DB API more closely;
1822 the method of passing parameters with keyword parameters is still supported
1823 and is in fact preferred
1824 #) Added support for the attribute "statement" on cursors which is a
1825 reference to the last SQL statement prepared or executed
1826 #) Added support for passing any sequence to callproc() rather than just
1827 lists as before
1828 #) Fixed bug where segfault would occur if the array size was changed after
1829 the cursor was executed but before it was fetched
1830 #) Ignore array size when performing executemany() and use the length of the
1831 list of parameters instead
1832 #) Rollback when connection is closed or destroyed to follow DB API rather
1833 than use the Oracle default (which is commit)
1834 #) Added check for array size too large causing an integer overflow
1835 #) Added support for iterators for Python 2.2 and above
1836 #) Added test suite based on PyUnitTest
1837 #) Added documentation in HTML format similar to the documentation for the
1838 core Python library
1839
1840
1841 Version 2.5a (August 2002)
1842 --------------------------
1843
1844 #) Fix problem with Oracle 9i and retrieving strings; it seems that Oracle 9i
1845 uses the correct method for dynamic callback but Oracle 8i will not work
1846 with that method so an #ifdef was added to check for the existence of an
1847 Oracle 9i feature; thanks to Paul Denize for discovering this problem
1848
1849
1850 Version 2.5 (July 2002)
1851 -----------------------
1852
1853 #) Added flag OPT_NoOracle7 which, if set, assumes that connections are being
1854 made to Oracle8 or higher databases; this allows for eliminating the
1855 overhead in performing this check at connect time
1856 #) Added flag OPT_NumbersAsStrings which, if set, returns all numbers as
1857 strings rather than integers or floats; this flag is used when defined
1858 variables are created (during select statements only)
1859 #) Added flag OPT_Threading which, if set, uses OCI threading mode; there is a
1860 significant performance degradation in this mode (about 15-20%) but it does
1861 allow threads to share connections (threadsafety level 2 according to the
1862 Python Database API 2.0); note that in order to support this, Oracle 8i or
1863 higher is now required
1864 #) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS pairs where
1865 applicable to support threading during blocking OCI calls
1866 #) Added global method attach() to cx_Oracle to support attaching to an
1867 existing database handle (as provided by PowerBuilder, for example)
1868 #) Eliminated the cursor method fetchbinds() which was used for returning the
1869 list of bind variables after execution to get the values of out variables;
1870 the cursor method setinputsizes() was modified to return the list of bind
1871 variables and the cursor method execute() was modified to return the list
1872 of defined variables in the case of a select statement being executed;
1873 these variables have three methods available to them: getvalue([<pos>]) to
1874 get the value of a variable, setvalue(<pos>, <value>) to set its value and
1875 copy(<var>, <src_pos>, <targ_pos>) to copy the value from a variable in a
1876 more efficient manner than setvalue(getvalue())
1877 #) Implemented cursor method executemany() which expects a list of
1878 dictionaries for the parameters
1879 #) Implemented cursor method callproc()
1880 #) Added cursor method prepare() which parses (prepares) the statement for
1881 execution; subsequent execute() or executemany() calls can pass None as the
1882 statement which will imply use of the previously prepared statement; used
1883 for high performance only
1884 #) Added cursor method fetchraw() which will perform a raw fetch of the cursor
1885 returning the number of rows thus fetched; this is used to avoid the
1886 overhead of generating result sets; used for high performance only
1887 #) Added cursor method executemanyprepared() which is identical to the method
1888 executemany() except that it takes a single parameter which is the number
1889 of times to execute a previously prepared statement and it assumes that the
1890 bind variables already have their values set; used for high performance
1891 only
1892 #) Added support for rowid being returned in a select statement
1893 #) Added support for comparing dates returned by cx_Oracle
1894 #) Integrated patch from Andre Reitz to set the null ok flag in the
1895 description attribute of the cursor
1896 #) Integrated patch from Andre Reitz to setup.py to support compilation with
1897 Python 1.5
1898 #) Integrated patch from Benjamin Kearns to setup.py to support compilation
1899 on Cygwin
1900
1901
1902 Version 2.4 (January 2002)
1903 --------------------------
1904
1905 #) String variables can now be made any length (previously restricted to the
1906 64K limit imposed by Oracle for default binding); use the type
1907 cx_Oracle.LONG_STRING as the parameter to setinputsizes() for binding in
1908 string values larger than 4000 bytes.
1909 #) Raw and long raw columns are now supported; use the types cx_Oracle.BINARY
1910 and cx_Oracle.LONG_BINARY as the parameter to setinputsizes() for binding
1911 in values of these types.
1912 #) Functions DateFromTicks(), TimeFromTicks() and TimestampFromTicks()
1913 are now implemented.
1914 #) Function cursor.setoutputsize() implemented
1915 #) Added the ability to bind arrays as out parameters to procedures; use the
1916 format [cx_Oracle.<DataType>, <NumElems>] as the input to the function
1917 setinputsizes() for binding arrays
1918 #) Discovered from the Oracle 8.1.6 version of the documentation of the OCI
1919 libraries, that the size of the memory location required for the precision
1920 variable is larger than the printed documentation says; this was causing a
1921 problem with the code on the Sun platform.
1922 #) Now support building RPMs for Linux.
1923
1924
1925 Version 2.3 (October 2001)
1926 --------------------------
1927
1928 #) Incremental performance enhancements (dealing with reusing cursors and
1929 bind handles)
1930 #) Ensured that arrays of integers with a single float in them are all
1931 treated as floats, as suggested by Martin Koch.
1932 #) Fixed code dealing with scale and precision for both defining a numeric
1933 variable and for providing the cursor description; this eliminates the
1934 problem of an underflow error (OCI-22054) when retrieving data with
1935 non-zero scale.
1936
1937
1938 Version 2.2 (July 2001)
1939 -----------------------
1940
1941 #) Upgraded thread safety to level 1 (according to the Python DB API 2.0) as
1942 an internal project required the ability to share the module between
1943 threads.
1944 #) Added ability to bind ref cursors to PL/SQL blocks as requested by
1945 Brad Powell.
1946 #) Added function write(Value, [Offset]) to LOB variables as requested by
1947 Matthias Kirst.
1948 #) Procedure execute() on Cursor objects now permits a value None for the
1949 statement which means that the previously prepared statement will be
1950 executed and any input sizes set earlier will be retained. This was done to
1951 improve the performance of scripts that execute one statement many times.
1952 #) Modified module global constants BINARY and DATETIME to point to the
1953 external representations of those types so that the expression
1954 type(var) == cx_Oracle.DATETIME will work as expected.
1955 #) Added global constant version to provide means of determining the current
1956 version of the module.
1957 #) Modified error checking routine to distinguish between an Oracle error and
1958 invalid handles.
1959 #) Added error checking to avoid setting the value of a bind variable to a
1960 value that it cannot support and raised an exception to indicate this fact.
1961 #) Added extra compile arguments for the AIX platform as suggested by Jehwan
1962 Ryu.
1963 #) Added section to the README to indicate the method for a binary
1964 installation as suggested by Steve Holden.
1965 #) Added simple usage example as requested by many people.
1966 #) Added HISTORY file to the distribution.
+0
-1658
doc/src/releasenotes.rst less more
0 .. _releasenotes:
1
2 cx_Oracle Release Notes
3 =======================
4
5 7.x releases
6 ############
7
8 .. _releasenotes70:
9
10 Version 7.1 (February 2019)
11 ---------------------------
12
13 #) Updated to `ODPI-C 3.1
14 <https://oracle.github.io/odpi/doc/releasenotes.html#
15 version-3-1-january-21-2019>`__.
16 #) Improved support for session tagging in session pools by allowing a
17 session callback to be specified when creating a pool via
18 :meth:`cx_Oracle.SessionPool()`. Callbacks can be written in Python or in
19 PL/SQL and can be used to improve performance by decreasing round trips to
20 the database needed to set session state. Callbacks written in Python will
21 be invoked for brand new connections (that have never been acquired from
22 the pool before) or when the tag assigned to the connection doesn't match
23 the one that was requested. Callbacks written in PL/SQL will only be
24 invoked when the tag assigned to the connection doesn't match the one that
25 was requested.
26 #) Added attribute :attr:`Connection.tag` to provide access to the actual tag
27 assigned to the connection. Setting this attribute will cause the
28 connection to be retagged when it is released back to the pool.
29 #) Added support for fetching SYS.XMLTYPE values as strings, as requested
30 (`issue 14 <https://github.com/oracle/python-cx_Oracle/issues/14>`__).
31 Note that this support is limited to the size of VARCHAR2 columns in the
32 database (either 4000 or 32767 bytes).
33 #) Added support for allowing the typename parameter in method
34 :meth:`Cursor.var()` to be None or a valid object type created by the
35 method :meth:`Connection.gettype()`, as requested
36 (`issue 231 <https://github.com/oracle/python-cx_Oracle/issues/231>`__).
37 #) Added support for getting and setting attributes of type RAW on Oracle
38 objects, as requested
39 (`ODPI-C issue 72 <https://github.com/oracle/odpi/issues/72>`__).
40 #) Added support for performing external authentication with proxy for
41 standalone connections.
42 #) Added support for mixing integers, floating point and decimal values in
43 data passed to :meth:`Cursor.executemany()`
44 (`issue 241 <https://github.com/oracle/python-cx_Oracle/issues/241>`__).
45 The error message raised when a value cannot be converted to an Oracle
46 number was also improved.
47 #) Adjusted fetching of numeric values so that no precision is lost. If an
48 Oracle number cannot be represented by a Python floating point number a
49 decimal value is automatically returned instead.
50 #) Corrected handling of multiple calls to method
51 :meth:`Cursor.executemany()` where all of the values in one of the columns
52 passed to the first call are all None and a subsequent call has a value
53 other than None in the same column
54 (`issue 236 <https://github.com/oracle/python-cx_Oracle/issues/236>`__).
55 #) Added additional check for calling :meth:`Cursor.setinputsizes()` with an
56 empty dictionary in order to avoid the error "cx_Oracle.ProgrammingError:
57 positional and named binds cannot be intermixed"
58 (`issue 199 <https://github.com/oracle/python-cx_Oracle/issues/199>`__).
59 #) Corrected handling of values that exceed the maximum value of a plain
60 integer object on Python 2 on Windows
61 (`issue 257 <https://github.com/oracle/python-cx_Oracle/issues/257>`__).
62 #) Added error message when attempting external authentication with proxy
63 without placing the user name in [] (proxy authentication was previously
64 silently ignored).
65 #) Exempted additional error messages from forcing a statement to be dropped
66 from the cache
67 (`ODPI-C issue 76 <https://github.com/oracle/odpi/issues/76>`__).
68 #) Improved dead session detection when using session pools for Oracle Client
69 12.2 and higher.
70 #) Ensured that the connection returned from a pool after a failed ping (such
71 as due to a killed session) is not itself marked as needing to be dropped
72 from the pool.
73 #) Eliminated memory leak under certain circumstances when pooled connections
74 are released back to the pool.
75 #) Eliminated memory leak when connections are dropped from the pool.
76 #) Eliminated memory leak when calling :meth:`Connection.close()` after
77 fetching collections from the database.
78 #) Adjusted order in which memory is freed when the last references to SODA
79 collections, documents, document cursors and collection cursors are
80 released, in order to prevent a segfault under certain circumstances.
81 #) Improved code preventing a statement from binding itself, in order to avoid
82 a potential segfault under certain circumstances.
83 #) Worked around OCI bug when attempting to free objects that are PL/SQL
84 records, in order to avoid a potential segfault.
85 #) Improved test suite and samples. Note that default passwords are no longer
86 supplied. New environment variables can be set to specify passwords if
87 desired, or the tests and samples will prompt for the passwords when
88 needed. In addition, a Python script is now available to create and drop
89 the schemas used for the tests and samples.
90 #) Improved documentation.
91
92
93 Version 7.0 (September 2018)
94 ----------------------------
95
96 #) Update to `ODPI-C 3.0
97 <https://oracle.github.io/odpi/doc/releasenotes.html#
98 version-3-0-0-september-13-2018>`__.
99 #) Added support for Oracle Client 18 libraries.
100 #) Added support for SODA (as preview). See :ref:`SODA Database <sodadb>`,
101 :ref:`SODA Collection <sodacoll>` and :ref:`SODA Document <sodadoc>` for
102 more information.
103 #) Added support for call timeouts available in Oracle Client 18.1 and
104 higher. See :attr:`Connection.callTimeout`.
105 #) Added support for getting the contents of a SQL collection object as a
106 dictionary, where the keys are the indices of the collection and the values
107 are the elements of the collection. See function :meth:`Object.asdict()`.
108 #) Added support for closing a session pool via the function
109 :meth:`SessionPool.close()`. Once closed, further attempts to use any
110 connection that was acquired from the pool will result in the error
111 "DPI-1010: not connected".
112 #) Added support for setting a LOB attribute of an object with a string or
113 bytes (instead of requiring a temporary LOB to be created).
114 #) Added support for the packed decimal type used by object attributes with
115 historical types DECIMAL and NUMERIC
116 (`issue 212 <https://github.com/oracle/python-cx_Oracle/issues/212>`__).
117 #) On Windows, first attempt to load oci.dll from the same directory as
118 the cx_Oracle module.
119 #) SQL objects that are created or fetched from the database are now tracked
120 and marked unusable when a connection is closed. This was done in order
121 to avoid a segfault under certain circumstances.
122 #) Re-enabled dead session detection functionality when using pools for Oracle
123 Client 12.2 and higher in order to handle classes of connection errors such
124 as resource profile limits.
125 #) Improved error messages when the Oracle Client or Oracle Database need to
126 be at a minimum version in order to support a particular feature.
127 #) When a connection is used as a context manager, the connection is now
128 closed when the block ends. Attempts to set
129 ``cx_Oracle.__future__.ctx_mgr_close`` are now ignored.
130 #) When a DML returning statement is executed, variables bound to it will
131 return an array when calling :meth:`Variable.getvalue()`. Attempts to set
132 ``cx_Oracle.__future__.dml_ret_array_val`` are now ignored.
133 #) Support for Python 3.4 has been dropped.
134 #) Added additional test cases.
135 #) Improved documentation.
136
137
138
139 6.x releases
140 ############
141
142 .. _releasenotes60:
143
144 Version 6.4.1 (July 2018)
145 -------------------------
146
147 #) Update to `ODPI-C 2.4.2
148 <https://oracle.github.io/odpi/doc/releasenotes.html#
149 version-2-4-2-july-9-2018>`__.
150
151 - Avoid buffer overrun due to improper calculation of length byte when
152 converting some negative 39 digit numbers from string to the internal
153 Oracle number format
154 (`ODPI-C issue 67 <https://github.com/oracle/odpi/issues/67>`__).
155
156 #) Prevent error "cx_Oracle.ProgrammingError: positional and named binds
157 cannot be intermixed" when calling cursor.setinputsizes() without any
158 parameters and then calling cursor.execute() with named bind parameters
159 (`issue 199 <https://github.com/oracle/python-cx_Oracle/issues/199>`__).
160
161
162 Version 6.4 (July 2018)
163 -----------------------
164
165 #) Update to `ODPI-C 2.4.1
166 <https://oracle.github.io/odpi/doc/releasenotes.html#
167 version-2-4-1-july-2-2018>`__.
168
169 - Added support for grouping subscriptions. See parameters groupingClass,
170 groupingValue and groupingType to function
171 :meth:`Connection.subscribe()`.
172 - Added support for specifying the IP address a subscription should use
173 instead of having the Oracle Client library determine the IP address on
174 its own. See parameter ipAddress to function
175 :meth:`Connection.subscribe()`.
176 - Added support for subscribing to notifications when messages are
177 available to dequeue in an AQ queue. The new constant
178 :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ` should be passed to the namespace
179 parameter of function :meth:`Connection.subscribe()` in order to get this
180 functionality. Attributes :attr:`Message.queueName` and
181 :attr:`Message.consumerName` will be populated in notification messages
182 that are received when this namespace is used.
183 - Added attribute :attr:`Message.registered` to let the notification
184 callback know when the subscription that generated the notification is no
185 longer registered with the database.
186 - Added support for timed waits when acquiring a session from a session
187 pool. Use the new constant :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT` in
188 the parameter getmode to function :meth:`cx_Oracle.SessionPool` along
189 with the new parameter waitTimeout.
190 - Added support for specifying the timeout and maximum lifetime session for
191 session pools when they are created using function
192 :meth:`cx_Oracle.SessionPool`. Previously the pool had to be created
193 before these values could be changed.
194 - Avoid memory leak when dequeuing from an empty queue.
195 - Ensure that the row count for queries is reset to zero when the statement
196 is executed
197 (`issue 193 <https://github.com/oracle/python-cx_Oracle/issues/193>`__).
198 - If the statement should be deleted from the statement cache, first check
199 to see that there is a statement cache currently being used; otherwise,
200 the error "ORA-24300: bad value for mode" will be raised under certain
201 conditions.
202
203 #) Added support for using the cursor as a context manager
204 (`issue 190 <https://github.com/oracle/python-cx_Oracle/issues/190>`__).
205 #) Added support for specifying the "errors" parameter to the decode() that
206 takes place internally when fetching strings from the database
207 (`issue 162 <https://github.com/oracle/python-cx_Oracle/issues/162>`__).
208 #) Added support for specifying an integer for the parameters argument to
209 :meth:`Cursor.executemany()`. This allows for batch execution when no
210 parameters are required or when parameters have previously been bound. This
211 replaces Cursor.executemanyprepared() (which is now deprecated and will be
212 removed in cx_Oracle 7).
213 #) Adjusted the binding of booleans so that outside of PL/SQL they are bound
214 as integers
215 (`issue 181 <https://github.com/oracle/python-cx_Oracle/issues/181>`__).
216 #) Added support for binding decimal.Decimal values to cx_Oracle.NATIVE_FLOAT
217 as requested
218 (`issue 184 <https://github.com/oracle/python-cx_Oracle/issues/184>`__).
219 #) Added checks on passing invalid type parameters to methods
220 :meth:`Cursor.arrayvar()`, :meth:`Cursor.callfunc()` and
221 :meth:`Cursor.setinputsizes()`.
222 #) Corrected handling of cursors and rowids in DML Returning statements.
223 #) Added sample from David Lapp demonstrating the use of GeoPandas with
224 SDO_GEOMETRY and a sample for demonstrating the use of REF cursors.
225 #) Adjusted samples and documentation for clarity.
226 #) Added additional test cases.
227
228
229 Version 6.3.1 (May 2018)
230 ------------------------
231
232 #) Update to `ODPI-C 2.3.2
233 <https://oracle.github.io/odpi/doc/releasenotes.html#
234 version-2-3-2-may-7-2018>`__.
235
236 - Ensure that a call to unregister a subscription only occurs if the
237 subscription is still registered.
238 - Ensure that before a statement is executed any buffers used for DML
239 returning statments are reset.
240
241 #) Ensure that behavior with cx_Oracle.__future__.dml_ret_array_val not
242 set or False is the same as the behavior in cx_Oracle 6.2
243 (`issue 176 <https://github.com/oracle/python-cx_Oracle/issues/176>`__).
244
245
246 Version 6.3 (April 2018)
247 ------------------------
248
249 #) Update to `ODPI-C 2.3.1
250 <https://oracle.github.io/odpi/doc/releasenotes.html#
251 version-2-3-1-april-25-2018>`__.
252
253 - Fixed binding of LONG data (values exceeding 32KB) when using the
254 function :meth:`Cursor.executemany()`.
255 - Added code to verify that a CQN subscription is open before permitting it
256 to be used. Error "DPI-1060: subscription was already closed" will now be
257 raised if an attempt is made to use a subscription that was closed
258 earlier.
259 - Stopped attempting to unregister a CQN subscription before it was
260 completely registered. This prevents errors encountered during
261 registration from being masked by an error stating that the subscription
262 has not been registered!
263 - Added error "DPI-1061: edition is not supported when a new password is
264 specified" to clarify the fact that specifying an edition and a new
265 password at the same time is not supported when creating a connection.
266 Previously the edition value was simply ignored.
267 - Improved error message when older OCI client libraries are being used
268 that don't have the method OCIClientVersion().
269 - Fixed the handling of ANSI types REAL and DOUBLE PRECISION as
270 implemented by Oracle. These types are just subtypes of NUMBER and are
271 different from BINARY_FLOAT and BINARY_DOUBLE
272 (`issue 163 <https://github.com/oracle/python-cx_Oracle/issues/163>`__).
273 - Fixed support for true heterogeneous session pools that use different
274 user/password combinations for each session acquired from the pool.
275 - Added error message indicating that setting either of the parameters
276 arraydmlrowcounts and batcherrors to True in :meth:`Cursor.executemany()`
277 is only supported with insert, update, delete and merge statements.
278
279 #) Fixed support for getting the OUT values of bind variables bound to a DML
280 Returning statement when calling the function :meth:`Cursor.executemany()`.
281 Note that the attribute dml_ret_array_val in :attr:`cx_Oracle.__future__`
282 must be set to True first.
283 #) Added support for binding integers and floats as cx_Oracle.NATIVE_FLOAT.
284 #) A :attr:`cx_Oracle._Error` object is now the value of all cx_Oracle
285 exceptions raised by cx_Oracle.
286 (`issue 51 <https://github.com/oracle/python-cx_Oracle/issues/51>`__).
287 #) Added support for building cx_Oracle with a pre-compiled version of ODPI-C,
288 as requested
289 (`issue 103 <https://github.com/oracle/python-cx_Oracle/issues/103>`__).
290 #) Default values are now provided for all parameters to
291 :meth:`cx_Oracle.SessionPool`.
292 #) Improved error message when an unsupported Oracle type is encountered.
293 #) The Python GIL is now prevented from being held while performing a round
294 trip for the call to get the attribute :attr:`Connection.version`
295 (`issue 158 <https://github.com/oracle/python-cx_Oracle/issues/158>`__).
296 #) Added check for the validity of the year for Python 2.x since it doesn't do
297 that itself like Python 3.x does
298 (`issue 166 <https://github.com/oracle/python-cx_Oracle/issues/166>`__).
299 #) Adjusted documentation to provide additional information on the use of
300 :meth:`Cursor.executemany()` as requested
301 (`issue 153 <https://github.com/oracle/python-cx_Oracle/issues/153>`__).
302 #) Adjusted documentation to state that batch errors and array DML row counts
303 can only be used with insert, update, delete and merge statements
304 (`issue 31 <https://github.com/oracle/python-cx_Oracle/issues/31>`__).
305 #) Updated tutorial to import common connection information from files in
306 order to make setup a bit more generic.
307
308
309 Version 6.2.1 (March 2018)
310 --------------------------
311
312 #) Make sure cxoModule.h is included in the source archive
313 (`issue 155 <https://github.com/oracle/python-cx_Oracle/issues/155>`__).
314
315
316 Version 6.2 (March 2018)
317 ------------------------
318
319 #) Update to `ODPI-C 2.2.1
320 <https://oracle.github.io/odpi/doc/releasenotes.html#
321 version-2-2-1-march-5-2018>`__.
322
323 - eliminate error "DPI-1054: connection cannot be closed when open
324 statements or LOBs exist" (`issue 138
325 <https://github.com/oracle/python-cx_Oracle/issues/138>`__).
326 - avoid a round trip to the database when a connection is released back to
327 the pool by preventing a rollback from being called when no transaction
328 is in progress.
329 - improve error message when the use of bind variables is attempted with
330 DLL statements, which is not supported by Oracle.
331 - if an Oracle object is retrieved from an attribute of another Oracle
332 object or a collection, prevent the "owner" from being destroyed until
333 the object that was retrieved has itself been destroyed.
334 - correct handling of boundary numbers 1e126 and -1e126
335 - eliminate memory leak when calling :meth:`Connection.enq()` and
336 :meth:`Connection.deq()`
337 - eliminate memory leak when setting NCHAR and NVARCHAR attributes of
338 objects.
339 - eliminate memory leak when fetching collection objects from the database.
340
341 #) Added support for creating a temporary CLOB, BLOB or NCLOB via the method
342 :meth:`Connection.createlob()`.
343 #) Added support for binding a LOB value directly to a cursor.
344 #) Added support for closing the connection when reaching the end of a
345 ``with`` code block controlled by the connection as a context manager, but
346 in a backwards compatible way
347 (`issue 113 <https://github.com/oracle/python-cx_Oracle/issues/113>`__).
348 See :data:`cx_Oracle.__future__` for more information.
349 #) Reorganized code to simplify continued maintenance and consolidate
350 transformations to/from Python objects.
351 #) Ensure that the number of elements in the array is not lost when the
352 buffer size is increased to accommodate larger strings.
353 #) Corrected support in Python 3.x for cursor.parse() by permitting a string
354 to be passed, instead of incorrectly requiring a bytes object.
355 #) Eliminate reference leak with LOBs acquired from attributes of objects or
356 elements of collections.
357 #) Eliminate reference leak when extending an Oracle collection.
358 #) Documentation improvements.
359 #) Added test cases to the test suite.
360
361
362 Version 6.1 (December 2017)
363 ---------------------------
364
365 #) Update to `ODPI-C 2.1
366 <https://oracle.github.io/odpi/doc/releasenotes.html#
367 version-2-1-december-12-2017>`__.
368
369 - Support was added for accessing sharded databases via sharding keys (new
370 in Oracle 12.2). NOTE: the underlying OCI library has a bug when using
371 standalone connections. There is a small memory leak proportional to the
372 number of connections created/dropped. There is no memory leak when using
373 session pools, which is recommended.
374 - Added options for authentication with SYSBACKUP, SYSDG, SYSKM and SYSRAC,
375 as requested (`issue 101
376 <https://github.com/oracle/python-cx_Oracle/issues/101>`__).
377 - Attempts to release statements or free LOBs after the connection has been
378 closed (by, for example, killing the session) are now prevented.
379 - An error message was added when specifying an edition and a connection
380 class since this combination is not supported.
381 - Attempts to close the session for connections created with an external
382 handle are now prevented.
383 - Attempting to ping a database earlier than 10g results in ORA-1010:
384 invalid OCI operation, but that implies a response from the database and
385 therefore a successful ping, so treat it that way!
386 (see `<https://github.com/rana/ora/issues/224>`__ for more information).
387 - Support was added for converting numeric values in an object type
388 attribute to integer and text, as requested (`ODPI-C issue 35
389 <https://github.com/oracle/odpi/issues/35>`__).
390 - Setting attributes :attr:`DeqOptions.msgId` and
391 :attr:`MessageProperties.msgId` now works as expected.
392 - The overflow check when using double values (Python floats) as input
393 to float attributes of objects or elements of collections was removed as
394 it didn't work anyway and is a well-known issue that cannot be prevented
395 without removing desired functionality. The developer should ensure that
396 the source value falls within the limits of floats, understand the
397 consequent precision loss or use a different data type.
398 - Variables of string/raw types are restricted to 2 bytes less than 1 GB
399 (1,073,741,822 bytes), since OCI cannot handle more than that currently.
400 - Support was added for identifying the id of the transaction which spawned
401 a CQN subscription message, as requested
402 (`ODPI-C issue 32 <https://github.com/oracle/odpi/issues/32>`__).
403 - Corrected use of subscription port number (`issue 115
404 <https://github.com/oracle/python-cx_Oracle/issues/115>`__).
405 - Problems reported with the usage of FormatMessage() on Windows were
406 addressed (`ODPI-C issue 47
407 <https://github.com/oracle/odpi/issues/47>`__).
408 - On Windows, if oci.dll cannot be loaded because it is the wrong
409 architecture (32-bit vs 64-bit), attempt to find the offending DLL and
410 include the full path of the DLL in the message, as suggested.
411 (`ODPI-C issue 49 <https://github.com/oracle/odpi/issues/49>`__).
412 - Force OCI prefetch to always use the value 2; the OCI default is 1 but
413 setting the ODPI-C default to 2 ensures that single row fetches don't
414 require an extra round trip to determine if there are more rows to fetch;
415 this change also reduces the potential memory consumption when
416 fetchArraySize was set to a large value and also avoids performance
417 issues discovered with larger values of prefetch.
418
419 #) Fix build with PyPy 5.9.0-alpha0 in libpython mode
420 (`PR 54 <https://github.com/oracle/python-cx_Oracle/pull/54>`__).
421 #) Ensure that the edition is passed through to the database when a session
422 pool is created.
423 #) Corrected handling of Python object references when an invalid keyword
424 parameter is passed to :meth:`cx_Oracle.SessionPool`.
425 #) Corrected handling of :attr:`Connection.handle` and the handle parameter
426 to :meth:`cx_Oracle.connect` on Windows.
427 #) Documentation improvements.
428 #) Added test cases to the test suite.
429
430
431 Version 6.0.3 (November 2017)
432 -----------------------------
433
434 #) Update to `ODPI-C 2.0.3
435 <https://oracle.github.io/odpi/doc/releasenotes.html#
436 version-2-0-3-november-6-2017>`__.
437
438 - Prevent use of unitialized data in certain cases (`issue 77
439 <https://github.com/oracle/python-cx_Oracle/issues/77>`__).
440 - Attempting to ping a database earlier than 10g results in error
441 "ORA-1010: invalid OCI operation", but that implies a response from the
442 database and therefore a successful ping, so treat it that way!
443 - Correct handling of conversion of some numbers to NATIVE_FLOAT.
444 - Prevent use of NaN with Oracle numbers since it produces corrupt data
445 (`issue 91 <https://github.com/oracle/python-cx_Oracle/issues/91>`__).
446 - Verify that Oracle objects bound to cursors, fetched from cursors, set in
447 object attributes or appended to collection objects are of the correct
448 type.
449 - Correct handling of NVARCHAR2 when used as attributes of Oracle objects
450 or as elements of collections.
451
452 #) Ensure that a call to setinputsizes() with an invalid type prior to a call
453 to executemany() does not result in a type error, but instead gracefully
454 ignores the call to setinputsizes() as required by the DB API
455 (`issue 75 <https://github.com/oracle/python-cx_Oracle/issues/75>`__).
456 #) Check variable array size when setting variable values and raise
457 IndexError, as is already done for getting variable values.
458
459
460 Version 6.0.2 (August 2017)
461 ---------------------------
462
463 #) Update to `ODPI-C 2.0.2
464 <https://oracle.github.io/odpi/doc/releasenotes.html
465 #version-2-0-2-august-30-2017>`__.
466
467 - Don't prevent connection from being explicitly closed when a fatal error
468 has taken place (`issue 67
469 <https://github.com/oracle/python-cx_Oracle/issues/67>`__).
470 - Correct handling of objects when dynamic binding is performed.
471 - Process deregistration events without an error.
472 - Eliminate memory leak when creating objects.
473
474 #) Added missing type check to prevent coercion of decimal to float
475 (`issue 68 <https://github.com/oracle/python-cx_Oracle/issues/68>`__).
476 #) On Windows, sizeof(long) = 4, not 8, which meant that integers between 10
477 and 18 digits were not converted to Python correctly
478 (`issue 70 <https://github.com/oracle/python-cx_Oracle/issues/70>`__).
479 #) Eliminate memory leak when repeatedly executing the same query.
480 #) Eliminate segfault when attempting to reuse a REF cursor that has been
481 closed.
482 #) Updated documentation.
483
484
485 Version 6.0.1 (August 2017)
486 ---------------------------
487
488 #) Update to `ODPI-C 2.0.1
489 <https://oracle.github.io/odpi/doc/releasenotes.html
490 #version-2-0-1-august-18-2017>`__.
491
492 - Ensure that queries registered via :meth:`Subscription.registerquery()`
493 do not prevent the associated connection from being closed
494 (`ODPI-C issue 27 <https://github.com/oracle/odpi/issues/27>`__).
495 - Deprecated attribute :attr:`Subscription.id` as it was never intended to
496 be exposed (`ODPI-C issue 28
497 <https://github.com/oracle/odpi/issues/28>`__). It will be dropped in
498 version 6.1.
499 - Add support for DML Returning statements that require dynamically
500 allocated variable data (such as CLOBs being returned as strings).
501
502 #) Correct packaging of Python 2.7 UCS4 wheels on Linux
503 (`issue 64 <https://github.com/oracle/python-cx_Oracle/issues/64>`__).
504 #) Updated documentation.
505
506
507 Version 6.0 (August 2017)
508 -------------------------
509
510 See :ref:`What's New <whatsnew60>` for a summary of the changes between
511 cx_Oracle 5.3 and cx_Oracle 6.0.
512
513 #) Update to `ODPI-C 2.0 <https://oracle.github.io/odpi/doc/releasenotes.html
514 #version-2-0-august-14-2017>`__.
515
516 - Prevent closing the connection when there are any open statements or
517 LOBs and add new error "DPI-1054: connection cannot be closed when open
518 statements or LOBs exist" when this situation is detected; this is
519 needed to prevent crashes under certain conditions when statements or
520 LOBs are being acted upon while at the same time (in another thread) a
521 connection is being closed; it also prevents leaks of statements and
522 LOBs when a connection is returned to a session pool.
523 - On platforms other than Windows, if the regular method for loading the
524 Oracle Client libraries fails, try using $ORACLE_HOME/lib/libclntsh.so
525 (`ODPI-C issue 20 <https://github.com/oracle/odpi/issues/20>`__).
526 - Use the environment variable DPI_DEBUG_LEVEL at runtime, not compile
527 time.
528 - Added support for DPI_DEBUG_LEVEL_ERRORS (reports errors and has the
529 value 8) and DPI_DEBUG_LEVEL_SQL (reports prepared SQL statement text
530 and has the value 16) in order to further improve the ability to debug
531 issues.
532 - Correct processing of :meth:`Cursor.scroll()` in some circumstances.
533
534 #) Delay initialization of the ODPI-C library until the first standalone
535 connection or session pool is created so that manipulation of the
536 environment variable NLS_LANG can be performed after the module has been
537 imported; this also has the added benefit of reducing the number of errors
538 that can take place when the module is imported.
539 #) Prevent binding of null values from generating the exception "ORA-24816:
540 Expanded non LONG bind data supplied after actual LONG or LOB column" in
541 certain circumstances
542 (`issue 50 <https://github.com/oracle/python-cx_Oracle/issues/50>`__).
543 #) Added information on how to run the test suite
544 (`issue 33 <https://github.com/oracle/python-cx_Oracle/issues/33>`__).
545 #) Documentation improvements.
546
547
548 Version 6.0 rc 2 (July 2017)
549 ----------------------------
550
551 #) Update to `ODPI-C rc 2 <https://oracle.github.io/odpi/doc/releasenotes.html
552 #version-2-0-0-rc-2-july-20-2017>`__.
553
554 - Provide improved error message when OCI environment cannot be created,
555 such as when the oraaccess.xml file cannot be processed properly.
556 - On Windows, convert system message to Unicode first, then to UTF-8;
557 otherwise, the error message returned could be in a mix of encodings
558 (`issue 40 <https://github.com/oracle/python-cx_Oracle/issues/40>`__).
559 - Corrected support for binding decimal values in object attribute values
560 and collection element values.
561 - Corrected support for binding PL/SQL boolean values to PL/SQL
562 procedures with Oracle client 11.2.
563
564 #) Define exception classes on the connection object in addition to at module
565 scope in order to simplify error handling in multi-connection environments,
566 as specified in the Python DB API.
567 #) Ensure the correct encoding is used for setting variable values.
568 #) Corrected handling of CLOB/NCLOB when using different encodings.
569 #) Corrected handling of TIMESTAMP WITH TIME ZONE attributes on objects.
570 #) Ensure that the array position passed to var.getvalue() does not exceed the
571 number of elements allocated in the array.
572 #) Reworked test suite and samples so that they are independent of each other
573 and so that the SQL scripts used to create/drop schemas are easily adjusted
574 to use different schema names, if desired.
575 #) Updated DB API test suite stub to support Python 3.
576 #) Added additional test cases and samples.
577 #) Documentation improvements.
578
579
580 Version 6.0 rc 1 (June 2017)
581 ----------------------------
582
583 #) Update to `ODPI-C rc 1 <https://oracle.github.io/odpi/doc/releasenotes.html
584 #version-2-0-0-rc-1-june-16-2017>`__.
585 #) The method :meth:`Cursor.setoutputsize` no longer needs to do anything,
586 since ODPI-C automatically manages buffer sizes of LONG and LONG RAW
587 columns.
588 #) Handle case when both precision and scale are zero, as occurs when
589 retrieving numeric expressions (`issue 34
590 <https://github.com/oracle/python-cx_Oracle/issues/34>`__).
591 #) OCI requires that both encoding and nencoding have values or that both
592 encoding and encoding do not have values. These parameters are used in
593 functions :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool`. The
594 missing value is set to its default value if one of the values is set and
595 the other is not (`issue 36
596 <https://github.com/oracle/python-cx_Oracle/issues/36>`__).
597 #) Permit use of both string and unicode for Python 2.7 for creating session
598 pools and for changing passwords (`issue 23
599 <https://github.com/oracle/python-cx_Oracle/issues/23>`__).
600 #) Corrected handling of BFILE LOBs.
601 #) Add script for dropping test schemas.
602 #) Documentation improvements.
603
604
605 Version 6.0 beta 2 (May 2017)
606 -----------------------------
607
608 #) Added support for getting/setting attributes of objects or element values
609 in collections that contain LOBs, BINARY_FLOAT values, BINARY_DOUBLE values
610 and NCHAR and NVARCHAR2 values. The error message for any types that are
611 not supported has been improved as well.
612 #) Enable temporary LOB caching in order to avoid disk I/O as
613 `suggested <https://github.com/oracle/odpi/issues/10>`__.
614 #) Added support for setting the debug level in ODPI-C, if desirable, by
615 setting environment variable DPI_DEBUG_LEVEL prior to building cx_Oracle.
616 #) Correct processing of strings in :meth:`Cursor.executemany` when a
617 larger string is found after a shorter string in the list of data bound to
618 the statement.
619 #) Correct handling of long Python integers that cannot fit inside a 64-bit C
620 integer (`issue 18
621 <https://github.com/oracle/python-cx_Oracle/issues/18>`__).
622 #) Correct creation of pool using external authentication.
623 #) Handle edge case when an odd number of zeroes trail the decimal point in a
624 value that is effectively zero (`issue 22
625 <https://github.com/oracle/python-cx_Oracle/issues/22>`__).
626 #) Prevent segfault under load when the attempt to create an error fails.
627 #) Eliminate resource leak when a standalone connection or pool is freed.
628 #) Correct `typo <https://github.com/oracle/python-cx_Oracle/issues/24>`__.
629 #) Correct handling of REF cursors when the array size is manipulated.
630 #) Prevent attempts from binding the cursor being executed to itself.
631 #) Correct reference count handling of parameters when creating a cursor.
632 #) Correct determination of the names of the bind variables in prepared SQL
633 statements (which behaves a little differently from PL/SQL statements).
634
635
636 Version 6.0 beta 1 (April 2017)
637 -------------------------------
638
639 #) Simplify building cx_Oracle considerably by use of
640 `ODPI-C <https://oracle.github.io/odpi>`__. This means that cx_Oracle can
641 now be built without Oracle Client header files or libraries and that at
642 runtime cx_Oracle can adapt to Oracle Client 11.2, 12.1 or 12.2 libraries
643 without needing to be rebuilt. This also means that wheels can now be
644 produced and installed via pip.
645 #) Added attribute :attr:`SessionPool.stmtcachesize` to support getting and
646 setting the default statement cache size for connections in the pool.
647 #) Added attribute :attr:`Connection.dbop` to support setting the database
648 operation that is to be monitored.
649 #) Added attribute :attr:`Connection.handle` to facilitate testing the
650 creation of a connection using a OCI service context handle.
651 #) Added parameters tag and matchanytag to the :meth:`cx_Oracle.connect`
652 and :meth:`SessionPool.acquire` methods and added parameters tag and retag
653 to the :meth:`SessionPool.release` method in order to support session
654 tagging.
655 #) Added parameter edition to the :meth:`cx_Oracle.SessionPool` method.
656 #) Added support for
657 `universal rowids <https://github.com/oracle/python-cx_Oracle/blob/master/
658 samples/UniversalRowids.py>`__.
659 #) Added support for `DML Returning of multiple rows
660 <https://github.com/oracle/python-cx_Oracle/blob/master/samples/
661 DMLReturningMultipleRows.py>`__.
662 #) Added attributes :attr:`Variable.actualElements` and
663 :attr:`Variable.values` to variables.
664 #) Added parameters region, sharding_key and super_sharding_key to the
665 :meth:`cx_Oracle.makedsn()` method to support connecting to a sharded
666 database (new in Oracle Database 12.2).
667 #) Added support for smallint and float data types in Oracle objects, as
668 `requested <https://github.com/oracle/python-cx_Oracle/issues/4>`__.
669 #) An exception is no longer raised when a collection is empty for methods
670 :meth:`Object.first()` and :meth:`Object.last()`. Instead, the value None
671 is returned to be consistent with the methods :meth:`Object.next()` and
672 :meth:`Object.prev()`.
673 #) If the environment variables NLS_LANG and NLS_NCHAR are being used, they
674 must be set before the module is imported. Using the encoding and nencoding
675 parameters to the :meth:`cx_Oracle.connect` and
676 :meth:`cx_Oracle.SessionPool` methods is a simpler alternative to setting
677 these environment variables.
678 #) Removed restriction on fetching LOBs across round trips to the database
679 (eliminates error "LOB variable no longer valid after subsequent fetch").
680 #) Removed requirement for specifying a maximum size when fetching LONG or
681 LONG raw columns. This also allows CLOB, NCLOB, BLOB and BFILE columns to
682 be fetched as strings or bytes without needing to specify a maximum size.
683 #) Dropped deprecated parameter twophase from the :meth:`cx_Oracle.connect`
684 method. Applications should set the :attr:`Connection.internal_name` and
685 :attr:`Connection.external_name` attributes instead to a value appropriate
686 to the application.
687 #) Dropped deprecated parameters action, module and clientinfo from the
688 :meth:`cx_Oracle.connect` method. The appcontext parameter should be used
689 instead as shown in this `sample <https://github.com/oracle/
690 python-cx_Oracle/blob/master/samples/AppContext.py>`__.
691 #) Dropped deprecated attribute numbersAsString from
692 :ref:`cursor objects <cursorobj>`. Use an output type handler instead as
693 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
694 master/samples/ReturnNumbersAsDecimals.py>`__.
695 #) Dropped deprecated attributes cqqos and rowids from
696 :ref:`subscription objects <subscrobj>`. Use the qos attribute instead as
697 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
698 master/samples/CQN.py>`__.
699 #) Dropped deprecated parameters cqqos and rowids from the
700 :meth:`Connection.subscribe()` method. Use the qos parameter instead as
701 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
702 master/samples/CQN.py>`__.
703
704
705 5.x releases
706 ############
707
708
709 Version 5.3 (March 2017)
710 ------------------------
711
712 #) Added support for Python 3.6.
713 #) Dropped support for Python versions earlier than 2.6.
714 #) Dropped support for Oracle clients earlier than 11.2.
715 #) Added support for
716 :meth:`fetching implicit results<Cursor.getimplicitresults()>`
717 (available in Oracle 12.1)
718 #) Added support for :attr:`Transaction Guard <Connection.ltxid>` (available
719 in Oracle 12.1).
720 #) Added support for setting the
721 :attr:`maximum lifetime <SessionPool.max_lifetime_session>` of pool
722 connections (available in Oracle 12.1).
723 #) Added support for large row counts (larger than 2 ** 32, available in
724 Oracle 12.1)
725 #) Added support for :meth:`advanced queuing <Connection.deq()>`.
726 #) Added support for :meth:`scrollable cursors <Cursor.scroll()>`.
727 #) Added support for :attr:`edition based redefinition <Connection.edition>`.
728 #) Added support for :meth:`creating <ObjectType.newobject()>`, modifying and
729 binding user defined types and collections.
730 #) Added support for creating, modifying and binding PL/SQL records and
731 collections (available in Oracle 12.1).
732 #) Added support for binding :data:`native integers <cx_Oracle.NATIVE_INT>`.
733 #) Enabled statement caching.
734 #) Removed deprecated variable attributes maxlength and allocelems.
735 #) Corrected support for setting the encoding and nencoding parameters when
736 :meth:`creating a connection <cx_Oracle.Connection>` and added support for
737 setting these when creating a session pool. These can now be used instead
738 of setting the environment variables NLS_LANG and NLS_NCHAR.
739 #) Use None instead of 0 for items in the :attr:`Cursor.description` attribute
740 that do not have any validity.
741 #) Changed driver name to match informal driver name standard used by Oracle
742 for other drivers.
743 #) Add check for maximum of 10,000 parameters when calling a stored procedure
744 or function in order to prevent a possible improper memory access from
745 taking place.
746 #) Removed -mno-cygwin compile flag since it is no longer used in newer
747 versions of the gcc compiler for Cygwin.
748 #) Simplified test suite by combining Python 2 and 3 scripts into one script
749 and separated out 12.1 features into a single script.
750 #) Updated samples to use code that works on both Python 2 and 3
751 #) Added support for pickling/unpickling error objects
752 (`Issue #23 <https://bitbucket.org/anthony_tuininga/cx_oracle/issues/23>`__)
753 #) Dropped support for callbacks on OCI functions.
754 #) Removed deprecated types UNICODE, FIXED_UNICODE and LONG_UNICODE (use
755 NCHAR, FIXED_NCHAR and LONG_NCHAR instead).
756 #) Increased default array size to 100 (from 50) to match other drivers.
757 #) Added support for setting the :attr:`~Connection.internal_name` and
758 :attr:`~Connection.external_name` on the connection directly. The use of
759 the twophase parameter is now deprecated. Applications should set the
760 internal_name and external_name attributes directly to a value appropriate
761 to the application.
762 #) Added support for using application context when
763 :meth:`creating a connection <cx_Oracle.Connection>`. This should be used
764 in preference to the module, action and clientinfo parameters which are now
765 deprecated.
766 #) Reworked database change notification and continuous query notification to
767 more closely align with the PL/SQL implementation and prepare for sending
768 notifications for AQ messages. The following changes were made:
769
770 - added constant :data:`~cx_Oracle.SUBSCR_QOS_BEST_EFFORT` to replace
771 deprecated constant SUBSCR_CQ_QOS_BEST_EFFORT
772 - added constant :data:`~cx_Oracle.SUBSCR_QOS_QUERY` to replace
773 deprecated constant SUBSCR_CQ_QOS_QUERY
774 - added constant :data:`~cx_Oracle.SUBSCR_QOS_DEREG_NFY` to replace
775 deprecated constant SUBSCR_QOS_PURGE_ON_NTFN
776 - added constant :data:`~cx_Oracle.SUBSCR_QOS_ROWIDS` to replace parameter
777 rowids for method :meth:`Connection.subscribe()`
778 - deprecated parameter cqqos for method :meth:`Connection.subscribe()`. The
779 qos parameter should be used instead.
780 - dropped constants SUBSCR_CQ_QOS_CLQRYCACHE, SUBSCR_QOS_HAREG,
781 SUBSCR_QOS_MULTICBK, SUBSCR_QOS_PAYLOAD, SUBSCR_QOS_REPLICATE, and
782 SUBSCR_QOS_SECURE since they were never actually used
783 #) Deprecated use of the numbersAsStrings attribute on cursors. An output type
784 handler should be used instead.
785
786
787 Version 5.2.1 (January 2016)
788 ----------------------------
789
790 #) Added support for Python 3.5.
791 #) Removed password attribute from connection and session pool objects in
792 order to promote best security practices (if stored in RAM in cleartext it
793 can be read in process dumps, for example). For those who would like to
794 retain this feature, a subclass of Connection could be used to store the
795 password.
796 #) Added optional parameter externalauth to SessionPool() which enables wallet
797 based or other external authentication mechanisms to be used.
798 #) Use the national character set encoding when required (when char set form
799 is SQLCS_NCHAR); otherwise, the wrong encoding would be used if the
800 environment variable NLS_NCHAR is set.
801 #) Added support for binding boolean values to PL/SQL blocks and stored
802 procedures (available in Oracle 12.1).
803
804
805 Version 5.2 (June 2015)
806 -----------------------
807
808 #) Added support for strings up to 32k characters (new in Oracle 12c).
809 #) Added support for getting array DML row counts (new in Oracle 12c).
810 #) Added support for fetching batch errors.
811 #) Added support for LOB values larger than 4 GB.
812 #) Added support for connections as SYSASM.
813 #) Added support for building without any configuration changes to the machine
814 when using instant client RPMs on Linux.
815 #) Added types NCHAR, FIXED_NCHAR and LONG_NCHAR to replace the types UNICODE,
816 FIXED_UNICODE and LONG_UNICODE (which are now deprecated). These types are
817 available in Python 3 as well so they can be used to specify the use of
818 NCHAR type fields when binding or using setinputsizes().
819 #) Fixed binding of booleans in Python 3.x.
820 #) Test suite now sets NLS_LANG if not already set.
821 #) Enhanced documentation for connection.action attribute and added note
822 on cursor.parse() method to make clear that DDL statements are executed
823 when parsed.
824 #) Removed remaining remnants of support Oracle 9i.
825 #) Added __version__ attribute to conform with PEP 396.
826 #) Ensure that sessions are released to the pool when calling
827 connection.close()
828 (`Issue #2 <https://bitbucket.org/anthony_tuininga/cx_oracle/issue/2>`__)
829 #) Fixed handling of datetime intervals
830 (`Issue #7 <https://bitbucket.org/anthony_tuininga/cx_oracle/issue/7>`__)
831
832
833 Version 5.1.3 (May 2014)
834 ------------------------
835
836 #) Added support for Oracle 12c.
837 #) Added support for Python 3.4.
838 #) Added support for query result set change notification. Thanks to Glen
839 Walker for the patch.
840 #) Ensure that in Python 3.x that NCHAR and NVARCHAR2 and NCLOB columns are
841 retrieved properly without conversion issues. Thanks to Joakim Andersson
842 for pointing out the issue and the possible solution.
843 #) Fix bug when an exception is caught and then another exception is raised
844 while handling that exception in Python 3.x. Thanks to Boris Dzuba for
845 pointing out the issue and providing a test case.
846 #) Enhance performance returning integers between 10 and 18 digits on 64-bit
847 platforms that support it. Thanks for Shai Berger for the initial patch.
848 #) Fixed two memory leaks.
849 #) Fix to stop current_schema from throwing a MemoryError on 64-bit platforms
850 on occasion. Thanks to Andrew Horton for the fix.
851 #) Class name of cursors changed to real name cx_Oracle.Cursor.
852
853
854 Version 5.1.2 (July 2012)
855 -------------------------
856
857 #) Added support for LONG_UNICODE which is a type used to handle long unicode
858 strings. These are not explicitly supported in Oracle but can be used to
859 bind to NCLOB, for example, without getting the error "unimplemented or
860 unreasonable conversion requested".
861 #) Set the row number in a cursor when executing PL/SQL blocks as requested
862 by Robert Ritchie.
863 #) Added support for setting the module, action and client_info attributes
864 during connection so that logon triggers will see the supplied values, as
865 requested by Rodney Barnett.
866
867
868 Version 5.1.1 (October 2011)
869 ----------------------------
870
871 #) Simplify management of threads for callbacks performed by database change
872 notification and eliminate a crash that occurred under high load in
873 certain situations. Thanks to Calvin S. for noting the issue and suggesting
874 a solution and testing the patch.
875 #) Force server detach on close so that the connection is completely closed
876 and not just the session as before.
877 #) Force use of OCI_UTF16ID for NCLOBs as using the default character set
878 would result in ORA-03127 with Oracle 11.2.0.2 and UTF8 character set.
879 #) Avoid attempting to clear temporary LOBs a second time when destroying the
880 variable as in certain situations this results in spurious errors.
881 #) Added additional parameter service_name to makedsn() which can be used to
882 use the service_name rather than the SID in the DSN string that is
883 generated.
884 #) Fix cursor description in test suite to take into account the number of
885 bytes per character.
886 #) Added tests for NCLOBS to the test suite.
887 #) Removed redundant code in setup.py for calculating the library path.
888
889
890 Version 5.1 (March 2011)
891 ------------------------
892
893 #) Remove support for UNICODE mode and permit Unicode to be passed through in
894 everywhere a string may be passed in. This means that strings will be
895 passed through to Oracle using the value of the NLS_LANG environment
896 variable in Python 3.x as well. Doing this eliminated a bunch of problems
897 that were discovered by using UNICODE mode and also removed an unnecessary
898 restriction in Python 2.x that Unicode could not be used in connect strings
899 or SQL statements, for example.
900 #) Added support for creating an empty object variable via a named type, the
901 first step to adding full object support.
902 #) Added support for Python 3.2.
903 #) Account for lib64 used on x86_64 systems. Thanks to Alex Wood for supplying
904 the patch.
905 #) Clear up potential problems when calling cursor.close() ahead of the
906 cursor being freed by going out of scope.
907 #) Avoid compilation difficulties on AIX5 as OCIPing does not appear to be
908 available on that platform under Oracle 10g Release 2. Thanks to
909 Pierre-Yves Fontaniere for the patch.
910 #) Free temporary LOBs prior to each fetch in order to avoid leaking them.
911 Thanks to Uwe Hoffmann for the initial patch.
912
913
914 Version 5.0.4 (July 2010)
915 -------------------------
916
917 #) Added support for Python 2.7.
918 #) Added support for new parameter (port) for subscription() call which allows
919 the client to specify the listening port for callback notifications from
920 the database server. Thanks to Geoffrey Weber for the initial patch.
921 #) Fixed compilation under Oracle 9i.
922 #) Fixed a few error messages.
923
924
925 Version 5.0.3 (February 2010)
926 -----------------------------
927
928 #) Added support for 64-bit Windows.
929 #) Added support for Python 3.1 and dropped support for Python 3.0.
930 #) Added support for keyword parameters in cursor.callproc() and
931 cursor.callfunc().
932 #) Added documentation for the UNICODE and FIXED_UNICODE variable types.
933 #) Added extra link arguments required for Mac OS X as suggested by Jason
934 Woodward.
935 #) Added additional error codes to the list of error codes that raise
936 OperationalError rather than DatabaseError.
937 #) Fixed calculation of display size for strings with national database
938 character sets that are not the default AL16UTF16.
939 #) Moved the resetting of the setinputsizes flag before the binding takes
940 place so that if an error takes place and a new statement is prepared
941 subsequently, spurious errors will not occur.
942 #) Fixed compilation with Oracle 10g Release 1.
943 #) Tweaked documentation based on feedback from a number of people.
944 #) Added support for running the test suite using "python setup.py test"
945 #) Added support for setting the CLIENT_IDENTIFIER value in the v$session
946 table for connections.
947 #) Added exception when attempting to call executemany() with arrays which is
948 not supported by the OCI.
949 #) Fixed bug when converting from decimal would result in OCI-22062 because
950 the locale decimal point was not a period. Thanks to Amaury Forgeot d'Arc
951 for the solution to this problem.
952
953
954 Version 5.0.2 (May 2009)
955 ------------------------
956
957 #) Fix creation of temporary NCLOB values and the writing of NCLOB values in
958 non Unicode mode.
959 #) Re-enabled parsing of non select statements as requested by Roy Terrill.
960 #) Implemented a parse error offset as requested by Catherine Devlin.
961 #) Removed lib subdirectory when forcing RPATH now that the library directory
962 is being calculated exactly in setup.py.
963 #) Added an additional cast in order to support compiling by Microsoft
964 Visual C++ 2008 as requested by Marco de Paoli.
965 #) Added additional include directory to setup.py in order to support
966 compiling by Microsoft Visual Studio was requested by Jason Coombs.
967 #) Fixed a few documentation issues.
968
969
970 Version 5.0.1 (February 2009)
971 -----------------------------
972
973 #) Added support for database change notification available in Oracle 10g
974 Release 2 and higher.
975 #) Fix bug where NCLOB data would be corrupted upon retrieval (non Unicode
976 mode) or would generate exception ORA-24806 (LOB form mismatch). Oracle
977 insists upon differentiating between CLOB and NCLOB no matter which
978 character set is being used for retrieval.
979 #) Add new attributes size, bufferSize and numElements to variable objects,
980 deprecating allocelems (replaced by numElements) and maxlength (replaced
981 by bufferSize)
982 #) Avoid increasing memory allocation for strings when using variable width
983 character sets and increasing the number of elements in a variable during
984 executemany().
985 #) Tweaked code in order to ensure that cx_Oracle can compile with Python
986 3.0.1.
987
988
989 Version 5.0 (December 2008)
990 ---------------------------
991
992 #) Added support for Python 3.0 with much help from Amaury Forgeot d'Arc.
993 #) Removed support for Python 2.3 and Oracle 8i.
994 #) Added support for full unicode mode in Python 2.x where all strings are
995 passed in and returned as unicode (module must be built in this mode)
996 rather than encoded strings
997 #) nchar and nvarchar columns now return unicode instead of encoded strings
998 #) Added support for an output type handler and/or an input type handler to be
999 specified at the connection and cursor levels.
1000 #) Added support for specifying both input and output converters for variables
1001 #) Added support for specifying the array size of variables that are created
1002 using the cursor.var() method
1003 #) Added support for events mode and database resident connection pooling
1004 (DRCP) in Oracle 11g.
1005 #) Added support for changing the password during construction of a new
1006 connection object as well as after the connection object has been created
1007 #) Added support for the interval day to second data type in Oracle,
1008 represented as datetime.timedelta objects in Python.
1009 #) Added support for getting and setting the current_schema attribute for a
1010 session
1011 #) Added support for proxy authentication in session pools as requested by
1012 Michael Wegrzynek (and thanks for the initial patch as well).
1013 #) Modified connection.prepare() to return a boolean indicating if a
1014 transaction was actually prepared in order to avoid the error ORA-24756
1015 (transaction does not exist).
1016 #) Raise a cx_Oracle.Error instance rather than a string for column
1017 truncation errors as requested by Helge Tesdal.
1018 #) Fixed handling of environment handles in session pools in order to allow
1019 session pools to fetch objects without exceptions taking place.
1020
1021
1022 Older releases
1023 ##############
1024
1025 Version 4.4.1 (October 2008)
1026 ----------------------------
1027
1028 #) Make the bind variables and fetch variables accessible although they need
1029 to be treated carefully since they are used internally; support added for
1030 forward compatibility with version 5.x.
1031 #) Include the "cannot insert null value" in the list of errors that are
1032 treated as integrity errors as requested by Matt Boersma.
1033 #) Use a cx_Oracle.Error instance rather than a string to hold the error when
1034 truncation (ORA-1406) takes place as requested by Helge Tesdal.
1035 #) Added support for fixed char, old style varchar and timestamp attribute
1036 values in objects.
1037 #) Tweaked setup.py to check for the Oracle version up front rather than
1038 during the build in order to produce more meaningful errors and simplify
1039 the code.
1040 #) In setup.py added proper detection for the instant client on Mac OS X as
1041 recommended by Martijn Pieters.
1042 #) In setup.py, avoided resetting the extraLinkArgs on Mac OS X as doing so
1043 prevents simple modification where desired as expressed by Christian
1044 Zagrodnick.
1045 #) Added documentation on exception handling as requested by Andreas Mock, who
1046 also graciously provided an initial patch.
1047 #) Modified documentation indicating that the password attribute on connection
1048 objects can be written.
1049 #) Added documentation warning that parameters not passed in during subsequent
1050 executions of a statement will retain their original values as requested by
1051 Harald Armin Massa.
1052 #) Added comments indicating that an Oracle client is required since so many
1053 people find this surprising.
1054 #) Removed all references to Oracle 8i from the documentation and version 5.x
1055 will eliminate all vestiges of support for this version of the Oracle
1056 client.
1057 #) Added additional link arguments for Cygwin as requested by Rob Gillen.
1058
1059
1060 Version 4.4 (June 2008)
1061 -----------------------
1062
1063 #) Fix setup.py to handle the Oracle instant client and Oracle XE on both
1064 Linux and Windows as pointed out by many. Thanks also to the many people
1065 who also provided patches.
1066 #) Set the default array size to 50 instead of 1 as the DB API suggests
1067 because the performance difference is so drastic and many people have
1068 recommended that the default be changed.
1069 #) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS around each blocking
1070 call for LOBs as requested by Jason Conroy who also provided an initial
1071 patch and performed a number of tests that demonstrate the new code is much
1072 more responsive.
1073 #) Add support for acquiring cursor.description after a parse.
1074 #) Defer type assignment when performing executemany() until the last possible
1075 moment if the value being bound in is null as suggested by Dragos Dociu.
1076 #) When dropping a connection from the pool, ignore any errors that occur
1077 during the rollback; unfortunately, Oracle decides to commit data even when
1078 dropping a connection from the pool instead of rolling it back so the
1079 attempt still has to be made.
1080 #) Added support for setting CLIENT_DRIVER in V$SESSION_CONNECT_INFO in Oracle
1081 11g and higher.
1082 #) Use cx_Oracle.InterfaceError rather than the builtin RuntimeError when
1083 unable to create the Oracle environment object as requested by Luke Mewburn
1084 since the error is specific to Oracle and someone attempting to catch any
1085 exception cannot simply use cx_Oracle.Error.
1086 #) Translated some error codes to OperationalError as requested by Matthew
1087 Harriger; translated if/elseif/else logic to switch statement to make it
1088 more readable and to allow for additional translation if desired.
1089 #) Transformed documentation to new format using restructured text. Thanks to
1090 Waldemar Osuch for contributing the initial draft of the new documentation.
1091 #) Allow the password to be overwritten by a new value as requested by Alex
1092 VanderWoude; this value is retained as a convenience to the user and not
1093 used by anything in the module; if changed externally it may be convenient
1094 to keep this copy up to date.
1095 #) Cygwin is on Windows so should be treated in the same way as noted by
1096 Matthew Cahn.
1097 #) Add support for using setuptools if so desired as requested by Shreya
1098 Bhatt.
1099 #) Specify that the version of Oracle 10 that is now primarily used is 10.2,
1100 not 10.1.
1101
1102
1103 Version 4.3.3 (October 2007)
1104 ----------------------------
1105
1106 #) Added method ping() on connections which can be used to test whether or not
1107 a connection is still active (available in Oracle 10g R2).
1108 #) Added method cx_Oracle.clientversion() which returns a 5-tuple giving the
1109 version of the client that is in use (available in Oracle 10g R2).
1110 #) Added methods startup() and shutdown() on connections which can be used to
1111 startup and shutdown databases (available in Oracle 10g R2).
1112 #) Added support for Oracle 11g.
1113 #) Added samples directory which contains a handful of scripts containing
1114 sample code for more advanced techniques. More will follow in future
1115 releases.
1116 #) Prevent error "ORA-24333: zero iteration count" when calling executemany()
1117 with zero rows as requested by Andreas Mock.
1118 #) Added methods __enter__() and __exit__() on connections to support using
1119 connections as context managers in Python 2.5 and higher. The context
1120 managed is the transaction state. Upon exit the transaction is either
1121 rolled back or committed depending on whether an exception took place or
1122 not.
1123 #) Make the search for the lib32 and lib64 directories automatic for all
1124 platforms.
1125 #) Tweak the setup configuration script to include all of the metadata and
1126 allow for building the module within another setup configuration script
1127 #) Include the Oracle version in addition to the Python version in the build
1128 directories that are created and in the names of the binary packages that
1129 are created.
1130 #) Remove unnecessary dependency on win32api to build module on Windows.
1131
1132
1133 Version 4.3.2 (August 2007)
1134 ---------------------------
1135
1136 #) Added methods open(), close(), isopen() and getchunksize() in order to
1137 improve performance of reading/writing LOB values in chunks.
1138 #) Fixed support for native doubles and floats in Oracle 10g; added new type
1139 NATIVE_FLOAT to allow specification of a variable of that specific type
1140 where desired. Thanks to D.R. Boxhoorn for pointing out the fact that this
1141 was not working properly when the arraysize was anything other than 1.
1142 #) When calling connection.begin(), only create a new tranasction handle if
1143 one is not already associated with the connection. Thanks to Andreas Mock
1144 for discovering this and for Amaury Forgeot d'Arc for diagnosing the
1145 problem and pointing the way to a solution.
1146 #) Added attribute cursor.rowfactory which allows a method to be called for
1147 each row that is returned; this is about 20% faster than calling the method
1148 in Python using the idiom [method(\*r) for r in cursor].
1149 #) Attempt to locate an Oracle installation by looking at the PATH if the
1150 environment variable ORACLE_HOME is not set; this is of primary use on
1151 Windows where this variable should not normally be set.
1152 #) Added support for autocommit mode as requested by Ian Kelly.
1153 #) Added support for connection.stmtcachesize which allows for both reading
1154 and writing the size of the statement cache size. This parameter can make a
1155 huge difference with the length of time taken to prepare statements. Added
1156 support for setting the statement tag when preparing a statement. Both of
1157 these were requested by Bjorn Sandberg who also provided an initial patch.
1158 #) When copying the value of a variable, copy the return code as well.
1159
1160
1161 Version 4.3.1 (April 2007)
1162 --------------------------
1163
1164 #) Ensure that if the client buffer size exceeds 4000 bytes that the server
1165 buffer size does not as strings may only contain 4000 bytes; this allows
1166 handling of multibyte character sets on the server as well as the client.
1167 #) Added support for using buffer objects to populate binary data and made the
1168 Binary() constructor the buffer type as requested by Ken Mason.
1169 #) Fix potential crash when using full optimization with some compilers.
1170 Thanks to Aris Motas for noticing this and providing the initial patch and
1171 to Amaury Forgeot d'Arc for providing an even simpler solution.
1172 #) Pass the correct charset form in to the write call in order to support
1173 writing to national character set LOB values properly. Thanks to Ian Kelly
1174 for noticing this discrepancy.
1175
1176
1177 Version 4.3 (March 2007)
1178 ------------------------
1179
1180 #) Added preliminary support for fetching Oracle objects (SQL types) as
1181 requested by Kristof Beyls (who kindly provided an initial patch).
1182 Additional work needs to be done to support binding and updating objects
1183 but the basic structure is now in place.
1184 #) Added connection.maxBytesPerCharacter which indicates the maximum number of
1185 bytes each character can use; use this value to also determine the size of
1186 local buffers in order to handle discrepancies between the client character
1187 set and the server character set. Thanks to Andreas Mock for providing the
1188 initial patch and working with me to resolve this issue.
1189 #) Added support for querying native floats in Oracle 10g as requested by
1190 Danny Boxhoorn.
1191 #) Add support for temporary LOB variables created via PL/SQL instead of only
1192 directly by cx_Oracle; thanks to Henning von Bargen for discovering this
1193 problem.
1194 #) Added support for specifying variable types using the builtin types int,
1195 float, str and datetime.date which allows for finer control of what type of
1196 Python object is returned from cursor.callfunc() for example.
1197 #) Added support for passing booleans to callproc() and callfunc() as
1198 requested by Anana Aiyer.
1199 #) Fixed support for 64-bit environments in Python 2.5.
1200 #) Thanks to Filip Ballegeer and a number of his co-workers, an intermittent
1201 crash was tracked down; specifically, if a connection is closed, then the
1202 call to OCIStmtRelease() will free memory twice. Preventing the call when
1203 the connection is closed solves the problem.
1204
1205
1206 Version 4.2.1 (September 2006)
1207 ------------------------------
1208
1209 #) Added additional type (NCLOB) to handle CLOBs that use the national
1210 character set as requested by Chris Dunscombe.
1211 #) Added support for returning cursors from functions as requested by Daniel
1212 Steinmann.
1213 #) Added support for getting/setting the "get" mode on session pools as
1214 requested by Anand Aiyer.
1215 #) Added support for binding subclassed cursors.
1216 #) Fixed binding of decimal objects with absolute values less than 0.1.
1217
1218
1219 Version 4.2 (July 2006)
1220 -----------------------
1221
1222 #) Added support for parsing an Oracle statement as requested by Patrick
1223 Blackwill.
1224 #) Added support for BFILEs at the request of Matthew Cahn.
1225 #) Added support for binding decimal.Decimal objects to cursors.
1226 #) Added support for reading from NCLOBs as requested by Chris Dunscombe.
1227 #) Added connection attributes encoding and nencoding which return the IANA
1228 character set name for the character set and national character set in use
1229 by the client.
1230 #) Rework module initialization to use the techniques recommended by the
1231 Python documentation as one user was experiencing random segfaults due
1232 to the use of the module dictionary after the initialization was complete.
1233 #) Removed support for the OPT_Threading attribute. Use the threaded keyword
1234 when creating connections and session pools instead.
1235 #) Removed support for the OPT_NumbersAsStrings attribute. Use the
1236 numbersAsStrings attribute on cursors instead.
1237 #) Use type long rather than type int in order to support long integers on
1238 64-bit machines as reported by Uwe Hoffmann.
1239 #) Add cursor attribute "bindarraysize" which is defaulted to 1 and is used
1240 to determine the size of the arrays created for bind variables.
1241 #) Added repr() methods to provide something a little more useful than the
1242 standard type name and memory address.
1243 #) Added keyword parameter support to the functions that imply such in the
1244 documentation as requested by Harald Armin Massa.
1245 #) Treat an empty dictionary passed through to cursor.execute() as keyword
1246 parameters the same as if no keyword parameters were specified at all, as
1247 requested by Fabien Grumelard.
1248 #) Fixed memory leak when a LOB read would fail.
1249 #) Set the LDFLAGS value in the environment rather than directly in the
1250 setup.py file in order to satisfy those who wish to enable the use of
1251 debugging symbols.
1252 #) Use __DATE__ and __TIME__ to determine the date and time of the build
1253 rather than passing it through directly.
1254 #) Use Oracle types and add casts to reduce warnings as requested by Amaury
1255 Forgeot d'Arc.
1256 #) Fixed typo in error message.
1257
1258
1259 Version 4.1.2 (December 2005)
1260 -----------------------------
1261
1262 #) Restore support of Oracle 9i features when using the Oracle 10g client.
1263
1264
1265 Version 4.1.1 (December 2005)
1266 -----------------------------
1267
1268 #) Add support for dropping a connection from a session pool.
1269 #) Add support for write only attributes "module", "action" and "clientinfo"
1270 which work only in Oracle 10g as requested by Egor Starostin.
1271 #) Add support for pickling database errors.
1272 #) Use the previously created bind variable as a template if available when
1273 creating a new variable of a larger size. Thanks to Ted Skolnick for the
1274 initial patch.
1275 #) Fixed tests to work properly in the Python 2.4 environment where dates and
1276 timestamps are different Python types. Thanks to Henning von Bargen for
1277 pointing this out.
1278 #) Added additional directories to search for include files and libraries in
1279 order to better support the Oracle 10g instant client.
1280 #) Set the internal fetch number to 0 in order to satisfy very picky source
1281 analysis tools as requested by Amaury Fogeot d'Arc.
1282 #) Improve the documentation for building and installing the module from
1283 source as some people are unaware of the standard methods for building
1284 Python modules using distutils.
1285 #) Added note in the documentation indicating that the arraysize attribute
1286 can drastically affect performance of queries since this seems to be a
1287 common misunderstanding of first time users of cx_Oracle.
1288 #) Add a comment indicating that on HP-UX Itanium with Oracle 10g the library
1289 ttsh10 must alos be linked against. Thanks to Bernard Delmee for the
1290 information.
1291
1292
1293 Version 4.1 (January 2005)
1294 --------------------------
1295
1296 #) Fixed bug where subclasses of Cursor do not pass the connection in the
1297 constructor causing a segfault.
1298 #) DDL statements must be reparsed before execution as noted by Mihai
1299 Ibanescu.
1300 #) Add support for setting input sizes by position.
1301 #) Fixed problem with catching an exception during execute and then still
1302 attempting to perform a fetch afterwards as noted by Leith Parkin.
1303 #) Rename the types so that they can be pickled and unpickled. Thanks to Harri
1304 Pasanen for pointing out the problem.
1305 #) Handle invalid NLS_LANG setting properly (Oracle seems to like to provide a
1306 handle back even though it is invalid) and determine the number of bytes
1307 per character in order to allow for proper support in the future of
1308 multibyte and variable width character sets.
1309 #) Remove date checking from the native case since Python already checks that
1310 dates are valid; enhance error message when invalid dates are encountered
1311 so that additional processing can be done.
1312 #) Fix bug executing SQL using numeric parameter names with predefined
1313 variables (such as what takes place when calling stored procedures with out
1314 parameters).
1315 #) Add support for reading CLOB values using multibyte or variable length
1316 character sets.
1317
1318
1319 Version 4.1 beta 1 (September 2004)
1320 -----------------------------------
1321
1322 #) Added support for Python 2.4. In Python 2.4, the datetime module is used
1323 for both binding and fetching of date and timestamp data. In Python 2.3,
1324 objects from the datetime module can be bound but the internal datetime
1325 objects will be returned from queries.
1326 #) Added pickling support for LOB and datetime data.
1327 #) Fully qualified the table name that was missing in an alter table
1328 statement in the setup test script as noted by Marc Gehling.
1329 #) Added a section allowing for the setting of the RPATH linker directive in
1330 setup.py as requested by Iustin Pop.
1331 #) Added code to raise a programming error exception when an attempt is made
1332 to access a LOB locator variable in a subsequent fetch.
1333 #) The username, password and dsn (tnsentry) are stored on the connection
1334 object when specified, regardless of whether or not a standard connection
1335 takes place.
1336 #) Added additional module level constant called "LOB" as requested by Joseph
1337 Canedo.
1338 #) Changed exception type to IntegrityError for constraint violations as
1339 requested by Joseph Canedo.
1340 #) If scale and precision are not specified, an attempt is made to return a
1341 long integer as requested by Joseph Canedo.
1342 #) Added workaround for Oracle bug which returns an invalid handle when the
1343 prepare call fails. Thanks to [email protected] for providing the code that
1344 demonstrated the problem.
1345 #) The cursor method arrayvar() will now accept the actual list so that it is
1346 not necessary to call cursor.arrayvar() followed immediately by
1347 var.setvalue().
1348 #) Fixed bug where attempts to execute the statement "None" with bind
1349 variables would cause a segmentation fault.
1350 #) Added support for binding by position (paramstyle = "numeric").
1351 #) Removed memory leak created by calls to OCIParamGet() which were not
1352 mirrored by calls to OCIDescriptorFree(). Thanks to Mihai Ibanescu for
1353 pointing this out and providing a patch.
1354 #) Added support for calling cursor.executemany() with statement None
1355 implying that the previously prepared statement ought to be executed.
1356 Thanks to Mihai Ibanescu for providing a patch.
1357 #) Added support for rebinding variables when a subsequent call to
1358 cursor.executemany() uses a different number of rows. Thanks to Mihai
1359 Ibanescu for supplying a patch.
1360 #) The microseconds are now displayed in datetime variables when nonzero
1361 similar to method used in the datetime module.
1362 #) Added support for binary_float and binary_double columns in Oracle 10g.
1363
1364
1365 Version 4.0.1 (February 2004)
1366 -----------------------------
1367
1368 #) Fixed bugs on 64-bit platforms that caused segmentation faults and bus
1369 errors in session pooling and determining the bind variables associated
1370 with a statement.
1371 #) Modified test suite so that 64-bit platforms are tested properly.
1372 #) Added missing commit statements in the test setup scripts. Thanks to Keith
1373 Lyon for pointing this out.
1374 #) Fix setup.py for Cygwin environments. Thanks to Doug Henderson for
1375 providing the necessary fix.
1376 #) Added support for compiling cx_Oracle without thread support. Thanks to
1377 Andre Reitz for pointing this out.
1378 #) Added support for a new keyword parameter called threaded on connections
1379 and session pools. This parameter defaults to False and indicates whether
1380 threaded mode ought to be used. It replaces the module level attribute
1381 OPT_Threading although examining the attribute will be retained until the
1382 next release at least.
1383 #) Added support for a new keyword parameter called twophase on connections.
1384 This parameter defaults to False and indicates whether support for two
1385 phase (distributed or global) transactions ought to be present. Note that
1386 support for distributed transactions is buggy when crossing major version
1387 boundaries (Oracle 8i to Oracle 9i for example).
1388 #) Ensure that the rowcount attribute is set properly when an exception is
1389 raised during execution. Thanks to Gary Aviv for pointing out this problem
1390 and its solution.
1391
1392
1393 Version 4.0 (December 2003)
1394 ---------------------------
1395
1396 #) Added support for subclassing connections, cursors and session pools. The
1397 changes involved made it necessary to drop support for Python 2.1 and
1398 earlier although a branch exists in CVS to allow for support of Python 2.1
1399 and earlier if needed.
1400 #) Connections and session pools can now be created with keyword parameters,
1401 not just sequential parameters.
1402 #) Queries now return integers whenever possible and long integers if the
1403 number will overflow a simple integer. Floats are only returned when it is
1404 known that the number is a floating point number or the integer conversion
1405 fails.
1406 #) Added initial support for user callbacks on OCI functions. See the
1407 documentation for more details.
1408 #) Add support for retrieving the bind variable names associated with a
1409 cursor with a new method bindnames().
1410 #) Add support for temporary LOB variables. This means that setinputsizes()
1411 can be used with the values CLOB and BLOB to create these temporary LOB
1412 variables and allow for the equivalent of empty_clob() and empty_blob()
1413 since otherwise Oracle will treat empty strings as NULL values.
1414 #) Automatically switch to long strings when the data size exceeds the
1415 maximum string size that Oracle allows (4000 characters) and raise an
1416 error if an attempt is made to set a string variable to a size that it
1417 does not support. This avoids truncation errors as reported by Jon Franz.
1418 #) Add support for global (distributed) transactions and two phase commit.
1419 #) Force the NLS settings for the session so that test tables are populated
1420 correctly in all circumstances; problems were noted by Ralf Braun and
1421 Allan Poulsen.
1422 #) Display error messages using the environment handle when the error handle
1423 has not yet been created; this provides better error messages during this
1424 rather rare situation.
1425 #) Removed memory leak in callproc() that was reported by Todd Whiteman.
1426 #) Make consistent the calls to manipulate memory; otherwise segfaults can
1427 occur when the pymalloc option is used, as reported by Matt Hoskins.
1428 #) Force a rollback when a session is released back to the session pool.
1429 Apparently the connections are not as stateless as Oracle's documentation
1430 suggests and this makes the logic consistent with normal connections.
1431 #) Removed module method attach(). This can be replaced with a call to
1432 Connection(handle=) if needed.
1433
1434
1435 Version 3.1 (August 2003)
1436 -------------------------
1437
1438 #) Added support for connecting with SYSDBA and SYSOPER access which is
1439 needed for connecting as sys in Oracle 9i.
1440 #) Only check the dictionary size if the variable is not NULL; otherwise, an
1441 error takes place which is not caught or cleared; this eliminates a
1442 spurious "Objects/dictobject.c:1258: bad argument to internal function" in
1443 Python 2.3.
1444 #) Add support for session pooling. This is only support for Oracle 9i but
1445 is amazingly fast -- about 100 times faster than connecting.
1446 #) Add support for statement caching when pooling sessions, this reduces the
1447 parse time considerably. Unfortunately, the Oracle OCI does not allow this
1448 to be easily turned on for normal sessions.
1449 #) Add method trim() on CLOB and BLOB variables for trimming the size.
1450 #) Add support for externally identified users; to use this feature leave the
1451 username and password fields empty when connecting.
1452 #) Add method cancel() on connection objects to cancel long running queries.
1453 Note that this only works on non-Windows platforms.
1454 #) Add method callfunc() on cursor objects to allow calling a function
1455 without using an anonymous PL/SQL block.
1456 #) Added documentation on objects that were not documented. At this point all
1457 objects, methods and constants in cx_Oracle have been documented.
1458 #) Added support for timestamp columns in Oracle 9i.
1459 #) Added module level method makedsn() which creates a data source name given
1460 the host, port and SID.
1461 #) Added constant "buildtime" which is the time when the module was built as
1462 an additional means of identifying the build that is in use.
1463 #) Binding a value that is incompatible to the previous value that was bound
1464 (data types do not match or array size is larger) will now result in a
1465 new bind taking place. This is more consistent with the DB API although
1466 it does imply a performance penalty when used.
1467
1468
1469 Version 3.0a (June 2003)
1470 ------------------------
1471
1472 #) Fixed bug where zero length PL/SQL arrays were being mishandled
1473 #) Fixed support for the data type "float" in Oracle; added one to the
1474 display size to allow for the sign of the number, if necessary; changed
1475 the display size of unconstrained numbers to 127, which is the largest
1476 number that Oracle can handle
1477 #) Added support for retrieving the description of a bound cursor before
1478 fetching it
1479 #) Fixed a couple of build issues on Mac OS X, AIX and Solaris (64-bit)
1480 #) Modified documentation slightly based on comments from several people
1481 #) Included files in MANIFEST that are needed to generate the binaries
1482 #) Modified test suite to work within the test environment at Computronix
1483 as well as within the packages that are distributed
1484
1485
1486 Version 3.0 (March 2003)
1487 ------------------------
1488
1489 #) Removed support for connection to Oracle7 databases; it is entirely
1490 possible that it will still work but I no longer have any way of testing
1491 and Oracle has dropped any meaningful support for Oracle7 anyway
1492 #) Fetching of strings is now done with predefined memory areas rather than
1493 dynamic memory areas; dynamic fetching of strings was causing problems
1494 with Oracle 9i in some instances and databases using a different character
1495 set other than US ASCII
1496 #) Fixed bug where segfault would occur if the '/' character preceded the '@'
1497 character in a connect string
1498 #) Added two new cursor methods var() and arrayvar() in order to eliminate
1499 the need for setinputsizes() when defining PL/SQL arrays and as a generic
1500 method of acquiring bind variables directly when needed
1501 #) Fixed support for binding cursors and added support for fetching cursors
1502 (these are known as ref cursors in PL/SQL).
1503 #) Eliminated discrepancy between the array size used internally and the
1504 array size specified by the interface user; this was done earlier to avoid
1505 bus errors on 64-bit platforms but another way has been found to get
1506 around that issue and a number of people were getting confused because of
1507 the discrepancy
1508 #) Added support for the attribute "connection" on cursors, an optional
1509 DB API extension
1510 #) Added support for passing a dictionary as the second parameter for the
1511 cursor.execute() method in order to comply with the DB API more closely;
1512 the method of passing parameters with keyword parameters is still supported
1513 and is in fact preferred
1514 #) Added support for the attribute "statement" on cursors which is a
1515 reference to the last SQL statement prepared or executed
1516 #) Added support for passing any sequence to callproc() rather than just
1517 lists as before
1518 #) Fixed bug where segfault would occur if the array size was changed after
1519 the cursor was executed but before it was fetched
1520 #) Ignore array size when performing executemany() and use the length of the
1521 list of parameters instead
1522 #) Rollback when connection is closed or destroyed to follow DB API rather
1523 than use the Oracle default (which is commit)
1524 #) Added check for array size too large causing an integer overflow
1525 #) Added support for iterators for Python 2.2 and above
1526 #) Added test suite based on PyUnitTest
1527 #) Added documentation in HTML format similar to the documentation for the
1528 core Python library
1529
1530
1531 Version 2.5a (August 2002)
1532 --------------------------
1533
1534 #) Fix problem with Oracle 9i and retrieving strings; it seems that Oracle 9i
1535 uses the correct method for dynamic callback but Oracle 8i will not work
1536 with that method so an #ifdef was added to check for the existence of an
1537 Oracle 9i feature; thanks to Paul Denize for discovering this problem
1538
1539
1540 Version 2.5 (July 2002)
1541 -----------------------
1542
1543 #) Added flag OPT_NoOracle7 which, if set, assumes that connections are being
1544 made to Oracle8 or higher databases; this allows for eliminating the
1545 overhead in performing this check at connect time
1546 #) Added flag OPT_NumbersAsStrings which, if set, returns all numbers as
1547 strings rather than integers or floats; this flag is used when defined
1548 variables are created (during select statements only)
1549 #) Added flag OPT_Threading which, if set, uses OCI threading mode; there is a
1550 significant performance degradation in this mode (about 15-20%) but it does
1551 allow threads to share connections (threadsafety level 2 according to the
1552 Python Database API 2.0); note that in order to support this, Oracle 8i or
1553 higher is now required
1554 #) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS pairs where
1555 applicable to support threading during blocking OCI calls
1556 #) Added global method attach() to cx_Oracle to support attaching to an
1557 existing database handle (as provided by PowerBuilder, for example)
1558 #) Eliminated the cursor method fetchbinds() which was used for returning the
1559 list of bind variables after execution to get the values of out variables;
1560 the cursor method setinputsizes() was modified to return the list of bind
1561 variables and the cursor method execute() was modified to return the list
1562 of defined variables in the case of a select statement being executed;
1563 these variables have three methods available to them: getvalue([<pos>]) to
1564 get the value of a variable, setvalue(<pos>, <value>) to set its value and
1565 copy(<var>, <src_pos>, <targ_pos>) to copy the value from a variable in a
1566 more efficient manner than setvalue(getvalue())
1567 #) Implemented cursor method executemany() which expects a list of
1568 dictionaries for the parameters
1569 #) Implemented cursor method callproc()
1570 #) Added cursor method prepare() which parses (prepares) the statement for
1571 execution; subsequent execute() or executemany() calls can pass None as the
1572 statement which will imply use of the previously prepared statement; used
1573 for high performance only
1574 #) Added cursor method fetchraw() which will perform a raw fetch of the cursor
1575 returning the number of rows thus fetched; this is used to avoid the
1576 overhead of generating result sets; used for high performance only
1577 #) Added cursor method executemanyprepared() which is identical to the method
1578 executemany() except that it takes a single parameter which is the number
1579 of times to execute a previously prepared statement and it assumes that the
1580 bind variables already have their values set; used for high performance
1581 only
1582 #) Added support for rowid being returned in a select statement
1583 #) Added support for comparing dates returned by cx_Oracle
1584 #) Integrated patch from Andre Reitz to set the null ok flag in the
1585 description attribute of the cursor
1586 #) Integrated patch from Andre Reitz to setup.py to support compilation with
1587 Python 1.5
1588 #) Integrated patch from Benjamin Kearns to setup.py to support compilation
1589 on Cygwin
1590
1591
1592 Version 2.4 (January 2002)
1593 --------------------------
1594
1595 #) String variables can now be made any length (previously restricted to the
1596 64K limit imposed by Oracle for default binding); use the type
1597 cx_Oracle.LONG_STRING as the parameter to setinputsizes() for binding in
1598 string values larger than 4000 bytes.
1599 #) Raw and long raw columns are now supported; use the types cx_Oracle.BINARY
1600 and cx_Oracle.LONG_BINARY as the parameter to setinputsizes() for binding
1601 in values of these types.
1602 #) Functions DateFromTicks(), TimeFromTicks() and TimestampFromTicks()
1603 are now implemented.
1604 #) Function cursor.setoutputsize() implemented
1605 #) Added the ability to bind arrays as out parameters to procedures; use the
1606 format [cx_Oracle.<DataType>, <NumElems>] as the input to the function
1607 setinputsizes() for binding arrays
1608 #) Discovered from the Oracle 8.1.6 version of the documentation of the OCI
1609 libraries, that the size of the memory location required for the precision
1610 variable is larger than the printed documentation says; this was causing a
1611 problem with the code on the Sun platform.
1612 #) Now support building RPMs for Linux.
1613
1614
1615 Version 2.3 (October 2001)
1616 --------------------------
1617
1618 #) Incremental performance enhancements (dealing with reusing cursors and
1619 bind handles)
1620 #) Ensured that arrays of integers with a single float in them are all
1621 treated as floats, as suggested by Martin Koch.
1622 #) Fixed code dealing with scale and precision for both defining a numeric
1623 variable and for providing the cursor description; this eliminates the
1624 problem of an underflow error (OCI-22054) when retrieving data with
1625 non-zero scale.
1626
1627
1628 Version 2.2 (July 2001)
1629 -----------------------
1630
1631 #) Upgraded thread safety to level 1 (according to the Python DB API 2.0) as
1632 an internal project required the ability to share the module between
1633 threads.
1634 #) Added ability to bind ref cursors to PL/SQL blocks as requested by
1635 Brad Powell.
1636 #) Added function write(Value, [Offset]) to LOB variables as requested by
1637 Matthias Kirst.
1638 #) Procedure execute() on Cursor objects now permits a value None for the
1639 statement which means that the previously prepared statement will be
1640 executed and any input sizes set earlier will be retained. This was done to
1641 improve the performance of scripts that execute one statement many times.
1642 #) Modified module global constants BINARY and DATETIME to point to the
1643 external representations of those types so that the expression
1644 type(var) == cx_Oracle.DATETIME will work as expected.
1645 #) Added global constant version to provide means of determining the current
1646 version of the module.
1647 #) Modified error checking routine to distinguish between an Oracle error and
1648 invalid handles.
1649 #) Added error checking to avoid setting the value of a bind variable to a
1650 value that it cannot support and raised an exception to indicate this fact.
1651 #) Added extra compile arguments for the AIX platform as suggested by Jehwan
1652 Ryu.
1653 #) Added section to the README to indicate the method for a binary
1654 installation as suggested by Steve Holden.
1655 #) Added simple usage example as requested by many people.
1656 #) Added HISTORY file to the distribution.
1657
+0
-186
doc/src/session_pool.rst less more
0 .. _sesspool:
1
2 ******************
3 SessionPool Object
4 ******************
5
6 .. note::
7
8 This object is an extension to the DB API.
9
10
11 .. method:: SessionPool.acquire(user=None, password=None, cclass=None, \
12 purity=cx_Oracle.ATTR_PURITY_DEFAULT, tag=None, matchanytag=False, \
13 shardingkey=[], supershardingkey=[])
14
15 Acquire a connection from the session pool and return a
16 :ref:`connection object <connobj>`.
17
18 If the pool is homogeneous, the user and password parameters cannot be
19 specified. If they are, an exception will be raised.
20
21 The cclass parameter, if specified, should be a string corresponding to the
22 connection class for database resident connection pooling (DRCP).
23
24 The purity parameter is expected to be one of
25 :data:`~cx_Oracle.ATTR_PURITY_NEW`, :data:`~cx_Oracle.ATTR_PURITY_SELF`, or
26 :data:`~cx_Oracle.ATTR_PURITY_DEFAULT`.
27
28 The tag parameter, if specified, is expected to be a string with name=value
29 pairs like "k1=v1;k2=v2" and will limit the sessions that can be returned
30 from a session pool unless the matchanytag parameter is set to True. In
31 that case sessions with the specified tag will be preferred over others,
32 but if no such sessions are available a session with a different tag may be
33 returned instead. In any case, untagged sessions will always be returned if
34 no sessions with the specified tag are available. Sessions are tagged when
35 they are :meth:`released <SessionPool.release>` back to the pool.
36
37 The shardingkey and supershardingkey parameters, if specified, are expected
38 to be a sequence of values which will be used to identify the database
39 shard to connect to. Currently only strings are supported for the key
40 values.
41
42 .. attribute:: SessionPool.busy
43
44 This read-only attribute returns the number of sessions currently acquired.
45
46
47 .. method:: SessionPool.close(force=False)
48
49 Close the session pool now, rather than when the last reference to it is
50 released, which makes it unsable for further work.
51
52 If any connections have been acquired and not released back to the pool
53 this method will fail unless the force parameter is set to True.
54
55
56 .. method:: SessionPool.drop(connection)
57
58 Drop the connection from the pool which is useful if the connection is no
59 longer usable (such as when the session is killed).
60
61
62 .. attribute:: SessionPool.dsn
63
64 This read-only attribute returns the TNS entry of the database to which a
65 connection has been established.
66
67
68 .. attribute:: SessionPool.homogeneous
69
70 This read-write boolean attribute indicates whether the pool is considered
71 homogeneous or not. If the pool is not homogeneous different authentication
72 can be used for each connection acquired from the pool.
73
74
75 .. attribute:: SessionPool.increment
76
77 This read-only attribute returns the number of sessions that will be
78 established when additional sessions need to be created.
79
80
81 .. attribute:: SessionPool.max
82
83 This read-only attribute returns the maximum number of sessions that the
84 session pool can control.
85
86
87 .. attribute:: SessionPool.max_lifetime_session
88
89 This read-write attribute returns the maximum length of time (in seconds)
90 that a pooled session may exist. Sessions that are in use will not be
91 closed. They become candidates for termination only when they are released
92 back to the pool and have existed for longer than max_lifetime_session
93 seconds. Note that termination only occurs when the pool is accessed. A
94 value of 0 means that there is no maximum length of time that a pooled
95 session may exist. This attribute is only available in Oracle Database
96 12.1.
97
98
99
100 .. versionadded:: 5.3
101
102
103 .. attribute:: SessionPool.min
104
105 This read-only attribute returns the number of sessions with which the
106 session pool was created and the minimum number of sessions that will be
107 controlled by the session pool.
108
109
110 .. attribute:: SessionPool.name
111
112 This read-only attribute returns the name assigned to the session pool by
113 Oracle.
114
115
116 .. attribute:: SessionPool.opened
117
118 This read-only attribute returns the number of sessions currently opened by
119 the session pool.
120
121
122 .. method:: SessionPool.release(connection, tag=None)
123
124 Release the connection back to the pool now, rather than whenever __del__
125 is called. The connection will be unusable from this point forward; an
126 Error exception will be raised if any operation is attempted with the
127 connection. Any cursors or LOBs created by the connection will also be
128 marked unusable and an Error exception will be raised if any operation is
129 attempted with them.
130
131 Internally, references to the connection are held by cursor objects,
132 LOB objects, etc. Once all of these references are released, the connection
133 itself will be released back to the pool automatically. Either control
134 references to these related objects carefully or explicitly release
135 connections back to the pool in order to ensure sufficient resources are
136 available.
137
138 If the tag is not None, it is expected to be a string with name=value pairs
139 like "k1=v1;k2=v2" and will override the value in the property
140 :attr:`Connection.tag`. If either :attr:`Connection.tag` or the tag
141 parameter are not None, the connection will be retagged when it is released
142 back to the pool.
143
144
145 .. attribute:: SessionPool.stmtcachesize
146
147 This read-write attribute specifies the size of the statement cache that
148 will be used as the starting point for any connections that are created by
149 the session pool. Once created, the connection's statement cache size can
150 only be changed by setting the stmtcachesize attribute on the connection
151 itself.
152
153 .. versionadded:: 6.0
154
155
156 .. attribute:: SessionPool.timeout
157
158 This read-write attribute specifies the time (in seconds) after which idle
159 sessions will be terminated in order to maintain an optimum number of open
160 sessions. Note that termination only occurs when the pool is accessed. A
161 value of 0 means that no idle sessions are terminated.
162
163
164 .. attribute:: SessionPool.tnsentry
165
166 This read-only attribute returns the TNS entry of the database to which a
167 connection has been established.
168
169
170 .. attribute:: SessionPool.username
171
172 This read-only attribute returns the name of the user which established the
173 connection to the database.
174
175
176 .. attribute:: SessionPool.wait_timeout
177
178 This read-write attribute specifies the time (in milliseconds) that the
179 caller should wait for a session to become available in the pool before
180 returning with an error. This value is only used if the getmode parameter
181 to :meth:`cx_Oracle.SessionPool()` was the value
182 :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`.
183
184 .. versionadded:: 6.4
185
+0
-496
doc/src/soda.rst less more
0 .. _soda:
1
2 ****
3 SODA
4 ****
5
6 An overview of Simple Oracle Document Access (SODA) can be found
7 `here
8 <https://docs.oracle.com/en/database/oracle/simple-oracle-document-access>`__.
9
10 SODA requires Oracle Client 18.3 or higher and Oracle Database 18.1 and higher.
11 The role SODA_APP must be granted to the user.
12
13 SODA support in cx_Oracle is in Preview status and should not be used in
14 production. It will be supported with a future version of Oracle Client
15 libraries.
16
17 .. _sodadb:
18
19 --------------------
20 SODA Database Object
21 --------------------
22
23 .. note::
24
25 This object is an extension the DB API. It is returned by the method
26 :meth:`Connection.getSodaDatabase()`.
27
28
29 .. method:: SodaDatabase.createCollection(name, metadata=None, mapMode=False)
30
31 Creates a SODA collection with the given name and returns a new
32 :ref:`SODA collection object <sodacoll>`. If you try to create a
33 collection, and a collection with the same name and metadata already
34 exists, then that existing collection is opened without error.
35
36 If metadata is specified, it is expected to be a string containing valid
37 JSON or a dictionary that will be transformed into a JSON string. This JSON
38 permits you to specify the configuration of the collection including
39 storage options; specifying the presence or absence of columns for creation
40 timestamp, last modified timestamp and version; whether the collection can
41 store only JSON documents; and methods of key and version generation. The
42 default metadata creates a collection that only supports JSON documents and
43 uses system generated keys. See this `collection metadata reference
44 <https://www.oracle.com/pls/topic/
45 lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
46 for more information.
47
48 If the mapMode parameter is set to True, the new collection is mapped to an
49 existing table instead of creating a table. If a collection is created in
50 this way, dropping the collection will not drop the existing table either.
51
52 .. versionadded:: 7.0
53
54
55 .. method:: SodaDatabase.createDocument(content, key=None, mediaType="application/json")
56
57 Creates a :ref:`SODA document <sodadoc>` usable for SODA write operations.
58 You only need to use this method if your collection requires
59 client-assigned keys or has non-JSON content; otherwise, you can pass your
60 content directly to SODA write operations. SodaDocument attributes
61 'createdOn', 'lastModified' and 'version' will be None.
62
63 The content parameter can be a dictionary or list which will be transformed
64 into a JSON string and then UTF-8 encoded. It can also be a string which
65 will be UTF-8 encoded or it can be a bytes object which will be stored
66 unchanged. If a bytes object is provided and the content is expected to be
67 JSON, note that SODA only supports UTF-8, UTF-16LE and UTF-16BE encodings.
68
69 The key parameter should only be supplied if the collection in which the
70 document is to be placed requires client-assigned keys.
71
72 The mediaType parameter should only be supplied if the collection in which
73 the document is to be placed supports non-JSON documents and the content
74 for this document is non-JSON. Using a standard MIME type for this value is
75 recommended but any string will be accepted.
76
77 .. versionadded:: 7.0
78
79
80 .. method:: SodaDatabase.getCollectionNames(startName=None, limit=0)
81
82 Returns a list of the names of collections in the database that match the
83 criteria, in alphabetical order.
84
85 If the startName parameter is specified, the list of names returned will
86 start with this value and also contain any names that fall after this value
87 in alphabetical order.
88
89 If the limit parameter is specified and is non-zero, the number of
90 collection names returned will be limited to this value.
91
92 .. versionadded:: 7.0
93
94
95 .. method:: SodaDatabase.openCollection(name)
96
97 Opens an existing collection with the given name and returns a new
98 :ref:`SODA collection object <sodacoll>`. If a collection with that name
99 does not exist, None is returned.
100
101 .. versionadded:: 7.0
102
103
104 .. _sodacoll:
105
106 ----------------------
107 SODA Collection Object
108 ----------------------
109
110 .. note::
111
112 This object is an extension the DB API. It is used to represent SODA
113 collections and is created by methods
114 :meth:`SodaDatabase.createCollection()` and
115 :meth:`SodaDatabase.openCollection()`.
116
117
118 .. method:: SodaCollection.createIndex(spec)
119
120 Creates an index on a SODA collection. The spec is expected to be a
121 dictionary or a JSON-encoded string. See this `overview
122 <https://www.oracle.com/pls/topic/
123 lookup?ctx=dblatest&id=GUID-4848E6A0-58A7-44FD-8D6D-A033D0CCF9CB>`__
124 for information on indexes in SODA.
125
126 Note that a commit should be performed before attempting to create an
127 index.
128
129 .. versionadded:: 7.0
130
131
132 .. method:: SodaCollection.drop()
133
134 Drops the collection from the database, if it exists. Note that if the
135 collection was created with mapMode set to True the underlying table will
136 not be dropped.
137
138 A boolean value is returned indicating if the collection was actually
139 dropped.
140
141 .. versionadded:: 7.0
142
143
144 .. method:: SodaCollection.dropIndex(name, force=False)
145
146 Drops the index with the specified name, if it exists.
147
148 The force parameter, if set to True, can be used to force the dropping of
149 an index that the underlying Oracle Database domain index doesn't normally
150 permit. This is only applicable to spatial and JSON search indexes.
151 See `here <https://www.oracle.com/pls/topic/
152 lookup?ctx=dblatest&id=GUID-F60F75DF-2866-4F93-BB7F-8FCE64BF67B6>`__
153 for more information.
154
155 A boolean value is returned indicating if the index was actually dropped.
156
157 .. versionadded:: 7.0
158
159
160 .. method:: SodaCollection.find()
161
162 This method is used to begin an operation that will act upon documents in
163 the collection. It creates and returns a
164 :ref:`SodaOperation object <sodaop>` which is used to specify the criteria
165 and the operation that will be performed on the documents that match that
166 criteria.
167
168 .. versionadded:: 7.0
169
170
171 .. method:: SodaCollection.getDataGuide()
172
173 Returns a :ref:`SODA document object <sodadoc>` containing property names,
174 data types and lengths inferred from the JSON documents in the collection.
175 It can be useful for exploring the schema of a collection. Note that this
176 method is only supported for JSON-only collections where a JSON search
177 index has been created with the 'dataguide' option enabled. If there are
178 no documents in the collection, None is returned.
179
180 .. versionadded:: 7.0
181
182
183 .. method:: SodaCollection.insertOne(doc)
184
185 Inserts a given document into the collection. The input document can be a
186 dictionary or list or an existing :ref:`SODA document object <sodadoc>`.
187
188 .. versionadded:: 7.0
189
190
191 .. method:: SodaCollection.insertOneAndGet(doc)
192
193 Similarly to :meth:`~SodaCollection.insertOne()` this method inserts a
194 given document into the collection. The only difference is that it
195 returns a :ref:`SODA Document object <sodadoc>`. Note that for performance
196 reasons the returned document does not contain the content.
197
198 .. versionadded:: 7.0
199
200
201 .. attribute:: SodaCollection.metadata
202
203 This read-only attribute returns a dicationary containing the metadata that
204 was used to create the collection. See this `collection metadata reference
205 <https://www.oracle.com/pls/topic/
206 lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
207 for more information.
208
209 .. versionadded:: 7.0
210
211
212 .. attribute:: SodaCollection.name
213
214 This read-only attribute returns the name of the collection.
215
216 .. versionadded:: 7.0
217
218
219 .. _sodadoc:
220
221 --------------------
222 SODA Document Object
223 --------------------
224
225 .. note::
226
227 This object is an extension the DB API. It is returned by the methods
228 :meth:`SodaDatabase.createDocument()`,
229 :meth:`SodaOperation.getDocuments()` and
230 :meth:`SodaOperation.getOne()` as well as by iterating over
231 :ref:`SODA document cursors <sodadoccur>`.
232
233
234 .. attribute:: SodaDoc.createdOn
235
236 This read-only attribute returns the creation time of the document in
237 `ISO 8601 <https://www.iso.org/iso-8601-date-and-time-format.html>`__
238 format. Documents created by :meth:`SodaDatabase.createDocument()` or
239 fetched from collections where this attribute is not stored will return
240 None.
241
242 .. versionadded:: 7.0
243
244
245 .. method:: SodaDoc.getContent()
246
247 Returns the content of the document as a dictionary or list. This method
248 assumes that the content is application/json and will raise an exception if
249 this is not the case. If there is no content, however, None will be
250 returned.
251
252 .. versionadded:: 7.0
253
254
255 .. method:: SodaDoc.getContentAsBytes()
256
257 Returns the content of the document as a bytes object. If there is no
258 content, however, None will be returned.
259
260 .. versionadded:: 7.0
261
262
263 .. method:: SodaDoc.getContentAsString()
264
265 Returns the content of the document as a string. If the document encoding
266 is not known, UTF-8 will be used. If there is no content, however, None
267 will be returned.
268
269 .. versionadded:: 7.0
270
271
272 .. attribute:: SodaDoc.key
273
274 This read-only attribute returns the unique key assigned to this document.
275 Documents created by :meth:`SodaDatabase.createDocument()` may not have a
276 value assigned to them and return None.
277
278 .. versionadded:: 7.0
279
280
281 .. attribute:: SodaDoc.lastModified
282
283 This read-only attribute returns the last modified time of the document in
284 `ISO 8601 <https://www.iso.org/iso-8601-date-and-time-format.html>`__
285 format. Documents created by :meth:`SodaDatabase.createDocument()` or
286 fetched from collections where this attribute is not stored will return
287 None.
288
289 .. versionadded:: 7.0
290
291
292 .. attribute:: SodaDoc.mediaType
293
294 This read-only attribute returns the media type assigned to the document.
295 By convention this is expected to be a MIME type but no checks are
296 performed on this value. If a value is not specified when calling
297 :meth:`SodaDatabase.createDocument()` or the document is fetched from a
298 collection where this component is not stored, the string
299 "application/json" is returned.
300
301 .. versionadded:: 7.0
302
303
304 .. attribute:: SodaDoc.version
305
306 This read-only attribute returns the version assigned to this document.
307 Documents created by :meth:`SodaDatabase.createDocument()` or fetched
308 from collections where this attribute is not stored will return None.
309
310 .. versionadded:: 7.0
311
312
313 .. _sodadoccur:
314
315 ---------------------------
316 SODA Document Cursor Object
317 ---------------------------
318
319 .. note::
320
321 This object is an extension the DB API. It is returned by the method
322 :meth:`SodaOperation.getCursor()` and implements the iterator protocol.
323 Each iteration will return a :ref:`SODA document object <sodadoc>`.
324
325
326 .. method:: SodaDocCursor.close()
327
328 Close the cursor now, rather than whenever __del__ is called. The cursor
329 will be unusable from this point forward; an Error exception will be raised
330 if any operation is attempted with the cursor.
331
332 .. versionadded:: 7.0
333
334
335 .. _sodaop:
336
337 ---------------------
338 SODA Operation Object
339 ---------------------
340
341 .. note::
342
343 This object is an extension to the DB API. It represents an operation that
344 will be performed on all or some of the documents in a SODA collection. It
345 is created by the method :meth:`SodaCollection.find()`.
346
347
348 .. method:: SodaOperation.count()
349
350 Returns a count of the number of documents in the collection that match
351 the criteria. If :meth:`~SodaOperation.skip()` or
352 :meth:`~SodaOperation.limit()` were called on this object, an exception is
353 raised.
354
355 .. versionadded:: 7.0
356
357
358 .. method:: SodaOperation.filter(value)
359
360 Sets a filter specification for complex document queries and ordering of
361 JSON documents. Filter specifications must be provided as a dictionary or
362 JSON-encoded string and can include comparisons, regular expressions,
363 logical and spatial operators, among others. See the
364 `overview of SODA filter specifications
365 <https://www.oracle.com/pls/topic/
366 lookup?ctx=dblatest&id=GUID-CB09C4E3-BBB1-40DC-88A8-8417821B0FBE>`__
367 for more information.
368
369 As a convenience, the SodaOperation object is returned so that further
370 criteria can be specified by chaining methods together.
371
372 .. versionadded:: 7.0
373
374
375 .. method:: SodaOperation.getCursor()
376
377 Returns a :ref:`SODA Document Cursor object <sodadoccur>` that can be used
378 to iterate over the documents that match the criteria.
379
380 .. versionadded:: 7.0
381
382
383 .. method:: SodaOperation.getDocuments()
384
385 Returns a list of :ref:`SODA Document objects <sodadoc>` that match the
386 criteria.
387
388 .. versionadded:: 7.0
389
390
391 .. method:: SodaOperation.getOne()
392
393 Returns a single :ref:`SODA Document object <sodadoc>` that matches the
394 criteria. Note that if multiple documents match the criteria only the first
395 one is returned.
396
397 .. versionadded:: 7.0
398
399
400 .. method:: SodaOperation.key(value)
401
402 Specifies that the document with the specified key should be returned.
403 This causes any previous calls made to this method and
404 :meth:`~SodaOperation.keys()` to be ignored.
405
406 As a convenience, the SodaOperation object is returned so that further
407 criteria can be specified by chaining methods together.
408
409 .. versionadded:: 7.0
410
411
412 .. method:: SodaOperation.keys(seq)
413
414 Specifies that documents that match the keys found in the supplied sequence
415 should be returned. This causes any previous calls made to this method and
416 :meth:`~SodaOperation.key()` to be ignored.
417
418 As a convenience, the SodaOperation object is returned so that further
419 criteria can be specified by chaining methods together.
420
421 .. versionadded:: 7.0
422
423
424 .. method:: SodaOperation.limit(value)
425
426 Specifies that only the specified number of documents should be returned.
427 This method is only usable for read operations such as
428 :meth:`~SodaOperation.getCursor()` and
429 :meth:`~SodaOperation.getDocuments()`. For write operations, any value set
430 using this method is ignored.
431
432 As a convenience, the SodaOperation object is returned so that further
433 criteria can be specified by chaining methods together.
434
435 .. versionadded:: 7.0
436
437
438 .. method:: SodaOperation.remove()
439
440 Removes all of the documents in the collection that match the criteria. The
441 number of documents that have been removed is returned.
442
443 .. versionadded:: 7.0
444
445
446 .. method:: SodaOperation.replaceOne(doc)
447
448 Replaces a single document in the collection with the specified document.
449 The input document can be a dictionary or list or an existing
450 :ref:`SODA document object <sodadoc>`. A boolean indicating if a document
451 was replaced or not is returned.
452
453 Currently the method :meth:`~SodaOperation.key()` must be called before
454 this method can be called.
455
456 .. versionadded:: 7.0
457
458
459 .. method:: SodaOperation.replaceOneAndGet(doc)
460
461 Similarly to :meth:`~SodaOperation.replaceOne()`, this method replaces a
462 single document in the collection with the specified document. The only
463 difference is that it returns a :ref:`SODA document object <sodadoc>`.
464 Note that for performance reasons the returned document does not contain
465 the content.
466
467 .. versionadded:: 7.0
468
469
470 .. method:: SodaOperation.skip(value)
471
472 Specifies the number of documents that match the other criteria that will
473 be skipped. This method is only usable for read operations such as
474 :meth:`~SodaOperation.getCursor()` and
475 :meth:`~SodaOperation.getDocuments()`. For write operations, any value set
476 using this method is ignored.
477
478 As a convenience, the SodaOperation object is returned so that further
479 criteria can be specified by chaining methods together.
480
481 .. versionadded:: 7.0
482
483
484 .. method:: SodaOperation.version(value)
485
486 Specifies that documents with the specified version should be returned.
487 Typically this is used with :meth:`~SodaOperation.key()` to implement
488 optimistic locking, so that the write operation called later does not
489 affect a document that someone else has modified.
490
491 As a convenience, the SodaOperation object is returned so that further
492 criteria can be specified by chaining methods together.
493
494 .. versionadded:: 7.0
495
+0
-259
doc/src/subscription.rst less more
0 .. _subscrobj:
1
2 *******************
3 Subscription Object
4 *******************
5
6 .. note::
7
8 This object is an extension the DB API.
9
10
11 .. attribute:: Subscription.callback
12
13 This read-only attribute returns the callback that was registered when the
14 subscription was created.
15
16
17 .. attribute:: Subscription.connection
18
19 This read-only attribute returns the connection that was used to register
20 the subscription when it was created.
21
22
23 .. attribute:: Subscription.id
24
25 This read-only attribute returns the value 0.
26
27 .. deprecated:: 6.0
28 This attribute was never intended to be exposed and will be removed
29 in cx_Oracle 7.
30
31
32 .. attribute:: Subscription.ipAddress
33
34 This read-only attribute returns the IP address used for callback
35 notifications from the database server. If not set during construction,
36 this value is None.
37
38 .. versionadded:: 6.4
39
40
41 .. attribute:: Subscription.name
42
43 This read-only attribute returns the name used to register the subscription
44 when it was created.
45
46 .. versionadded:: 6.4
47
48
49 .. attribute:: Subscription.namespace
50
51 This read-only attribute returns the namespace used to register the
52 subscription when it was created.
53
54
55 .. attribute:: Subscription.operations
56
57 This read-only attribute returns the operations that will send
58 notifications for each table or query that is registered using this
59 subscription.
60
61
62 .. attribute:: Subscription.port
63
64 This read-only attribute returns the port used for callback notifications
65 from the database server. If not set during construction, this value is
66 zero.
67
68
69 .. attribute:: Subscription.protocol
70
71 This read-only attribute returns the protocol used to register the
72 subscription when it was created.
73
74
75 .. attribute:: Subscription.qos
76
77 This read-only attribute returns the quality of service flags used to
78 register the subscription when it was created.
79
80
81 .. method:: Subscription.registerquery(statement, [args])
82
83 Register the query for subsequent notification when tables referenced by
84 the query are changed. This behaves similarly to cursor.execute() but only
85 queries are permitted and the args parameter must be a sequence or
86 dictionary. If the qos parameter included the flag
87 cx_Oracle.SUBSCR_QOS_QUERY when the subscription was created, then the ID
88 for the registered query is returned; otherwise, None is returned.
89
90
91 .. attribute:: Subscription.timeout
92
93 This read-only attribute returns the timeout (in seconds) that was
94 specified when the subscription was created. A value of 0 indicates that
95 there is no timeout.
96
97
98 .. _msgobjects:
99
100 Message Objects
101 ===============
102
103 .. note::
104
105 This object is created internally when notification is received and passed
106 to the callback procedure specified when a subscription is created.
107
108
109 .. attribute:: Message.consumerName
110
111 This read-only attribute returns the name of the consumer which generated
112 the notification. It will be populated if the subscription was created with
113 the namespace :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ` and the queue is a
114 multiple consumer queue.
115
116 .. versionadded:: 6.4
117
118
119 .. attribute:: Message.dbname
120
121 This read-only attribute returns the name of the database that generated
122 the notification.
123
124
125 .. attribute:: Message.queries
126
127 This read-only attribute returns a list of message query objects that give
128 information about query result sets changed for this notification. This
129 attribute will be None if the qos parameter did not include the flag
130 :data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created.
131
132
133 .. attribute:: Message.queueName
134
135 This read-only attribute returns the name of the queue which generated the
136 notification. It will only be populated if the subscription was created
137 with the namespace :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`.
138
139 .. versionadded:: 6.4
140
141
142 .. attribute:: Message.registered
143
144 This read-only attribute returns whether the subscription which generated
145 this notification is still registered with the database. The subscription
146 is automatically deregistered with the database when the subscription
147 timeout value is reached or when the first notification is sent (when the
148 quality of service flag :data:`cx_Oracle.SUBSCR_QOS_DEREG_NFY` is used).
149
150 .. versionadded:: 6.4
151
152
153 .. attribute:: Message.subscription
154
155 This read-only attribute returns the subscription object for which this
156 notification was generated.
157
158
159 .. attribute:: Message.tables
160
161 This read-only attribute returns a list of message table objects that give
162 information about the tables changed for this notification. This
163 attribute will be None if the qos parameter included the flag
164 :data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created.
165
166
167 .. attribute:: Message.txid
168
169 This read-only attribute returns the id of the transaction that generated
170 the notification.
171
172
173 .. attribute:: Message.type
174
175 This read-only attribute returns the type of message that has been sent.
176 See the constants section on event types for additional information.
177
178
179 Message Table Objects
180 =====================
181
182 .. note::
183
184 This object is created internally for each table changed when notification
185 is received and is found in the tables attribute of message objects, and
186 the tables attribute of message query objects.
187
188
189 .. attribute:: MessageTable.name
190
191 This read-only attribute returns the name of the table that was changed.
192
193
194 .. attribute:: MessageTable.operation
195
196 This read-only attribute returns the operation that took place on the table
197 that was changed.
198
199
200 .. attribute:: MessageTable.rows
201
202 This read-only attribute returns a list of message row objects that give
203 information about the rows changed on the table. This value is only filled
204 in if the qos parameter to the :meth:`Connection.subscribe()` method
205 included the flag :data:`~cx_Oracle.SUBSCR_QOS_ROWIDS`.
206
207
208 Message Row Objects
209 ===================
210
211 .. note::
212
213 This object is created internally for each row changed on a table when
214 notification is received and is found in the rows attribute of message
215 table objects.
216
217
218 .. attribute:: MessageRow.operation
219
220 This read-only attribute returns the operation that took place on the row
221 that was changed.
222
223
224 .. attribute:: MessageRow.rowid
225
226 This read-only attribute returns the rowid of the row that was changed.
227
228
229 Message Query Objects
230 =====================
231
232 .. note::
233
234 This object is created internally for each query result set changed when
235 notification is received and is found in the queries attribute of message
236 objects.
237
238
239 .. attribute:: MessageQuery.id
240
241 This read-only attribute returns the query id of the query for which the
242 result set changed. The value will match the value returned by
243 Subscription.registerquery when the related query was registered.
244
245
246 .. attribute:: MessageQuery.operation
247
248 This read-only attribute returns the operation that took place on the query
249 result set that was changed. Valid values for this attribute are
250 :data:`~cx_Oracle.EVENT_DEREG` and :data:`~cx_Oracle.EVENT_QUERYCHANGE`.
251
252
253 .. attribute:: MessageQuery.tables
254
255 This read-only attribute returns a list of message table objects that give
256 information about the table changes that caused the query result set to
257 change for this notification.
258
0 .. _aqusermanual:
1
2 ****************************
3 Oracle Advanced Queuing (AQ)
4 ****************************
5
6 `Oracle Advanced Queuing
7 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADQUE>`__ is a highly
8 configurable and scalable messaging feature of Oracle Database. It has
9 interfaces in various languages, letting you integrate multiple tools in your
10 architecture.
11
12 cx_Oracle 7.2 introduced an updated interface for Oracle Advanced
13 Queuing.
14
15 There are Advanced Queuing examples in the `GitHub examples
16 <https://github.com/oracle/python-cx_Oracle/tree/master/samples>`__ directory.
17
18
19 Creating a Queue
20 ================
21
22 Before being used, queues need to be created in the database, for example in
23 SQL*Plus:
24
25 .. code-block:: sql
26
27 begin
28 dbms_aqadm.create_queue_table('MY_QUEUE_TABLE', 'RAW');
29 dbms_aqadm.create_queue('DEMO_RAW_QUEUE', 'MY_QUEUE_TABLE');
30 dbms_aqadm.start_queue('DEMO_RAW_QUEUE');
31 end;
32 /
33
34 This examples creates a RAW queue suitable for sending string or raw bytes
35 messages.
36
37
38 Enqueuing Messages
39 ==================
40
41 To send messages in Python you connect and get a :ref:`queue <queue>`. The
42 queue can be used for enqueuing, dequeuing, or both as needed.
43
44 .. code-block:: python
45
46 queue = connection.queue("DEMO_RAW_QUEUE")
47
48 Now messages can be queued using :meth:`Queue.enqOne()`. To send three
49 messages:
50
51 .. code-block:: python
52
53 PAYLOAD_DATA = [
54 "The first message",
55 "The second message",
56 "The third message"
57 ]
58 for data in PAYLOAD_DATA:
59 queue.enqOne(connection.msgproperties(payload=data))
60 connection.commit()
61
62 Since the queue sending the messages is a RAW queue, the strings in this
63 example will be internally encoded to bytes using :attr:`Connection.encoding`
64 before being enqueued.
65
66
67 Dequeuing Messages
68 ==================
69
70 Dequeuing is performed similarly. To dequeue a message call the method
71 :meth:`Queue.deqOne()` as shown. Note that if the message is expected to be a
72 string, the bytes must be decoded using :attr:`Connection.encoding`.
73
74 .. code-block:: python
75
76 queue = connection.queue("DEMO_RAW_QUEUE")
77 msg = queue.deqOne()
78 connection.commit()
79 print(msg.payload.decode(connection.encoding))
80
81
82 Using Object Queues
83 ===================
84
85 Named Oracle objects can be enqueued and dequeued as well. Given an object
86 type called ``UDT_BOOK``:
87
88 .. code-block:: sql
89
90 CREATE OR REPLACE TYPE udt_book AS OBJECT (
91 Title VARCHAR2(100),
92 Authors VARCHAR2(100),
93 Price NUMBER(5,2)
94 );
95 /
96
97 And a queue that accepts this type:
98
99 .. code-block:: sql
100
101 begin
102 dbms_aqadm.create_queue_table('BOOK_QUEUE_TAB', 'UDT_BOOK');
103 dbms_aqadm.create_queue('DEMO_BOOK_QUEUE', 'BOOK_QUEUE_TAB');
104 dbms_aqadm.start_queue('DEMO_BOOK_QUEUE');
105 end;
106 /
107
108 You can queue messages:
109
110 .. code-block:: python
111
112 booksType = connection.gettype("UDT_BOOK")
113 queue = connection.queue("DEMO_BOOK_QUEUE", booksType)
114
115 book = booksType.newobject()
116 book.TITLE = "Quick Brown Fox"
117 book.AUTHORS = "The Dog"
118 book.PRICE = 123
119
120 queue.enqOne(connection.msgproperties(payload=book))
121 connection.commit()
122
123 Dequeuing is done like this:
124
125 .. code-block:: python
126
127 booksType = connection.gettype("UDT_BOOK")
128 queue = connection.queue("DEMO_BOOK_QUEUE", booksType)
129
130 msg = queue.deqOne()
131 connection.commit()
132 print(msg.payload.TITLE) # will print Quick Brown Fox
133
134
135 Changing Queue and Message Options
136 ==================================
137
138 Refer to the :ref:`cx_Oracle AQ API <aq>` and
139 `Oracle Advanced Queuing documentation
140 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADQUE>`__ for details
141 on all of the enqueue and dequeue options available.
142
143 Enqueue options can be set. For example, to make it so that an explicit
144 call to :meth:`~Connection.commit()` on the connection is not needed to commit
145 messages:
146
147 .. code-block:: python
148
149 queue = connection.queue("DEMO_RAW_QUEUE")
150 queue.enqOptions.visibility = cx_Oracle.ENQ_IMMEDIATE
151
152 Dequeue options can also be set. For example, to specify not to block on
153 dequeuing if no messages are available:
154
155 .. code-block:: python
156
157 queue = connection.queue("DEMO_RAW_QUEUE")
158 queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
159
160 Message properties can be set when enqueuing. For example, to set an
161 expiration of 60 seconds on a message:
162
163 .. code-block:: python
164
165 queue.enqOne(connection.msgproperties(payload="Message", expiration=60))
166
167 This means that if no dequeue operation occurs within 60 seconds that the
168 message will be dropped from the queue.
169
170
171 Bulk Enqueue and Dequeue
172 ========================
173
174 The :meth:`Queue.enqMany()` and :meth:`Queue.deqMany()` methods can be used for
175 efficient bulk message handling.
176
177 :meth:`Queue.enqMany()` is similar to :meth:`Queue.enqOne()` but accepts an
178 array of messages:
179
180 .. code-block:: python
181
182 messages = [
183 "The first message",
184 "The second message",
185 "The third message",
186 ]
187 queue = connection.queue("DEMO_RAW_QUEUE")
188 queue.enqMany(connection.msgproperties(payload=m) for m in messages)
189 connection.commit()
190
191 Warning: calling :meth:`Queue.enqMany()` in parallel on different connections
192 acquired from the same pool may fail due to Oracle bug 29928074. Ensure that
193 this function is not run in parallel, use standalone connections or connections
194 from different pools, or make multiple calls to :meth:`Queue.enqOne()` instead.
195 The function :meth:`Queue.deqMany()` call is not affected.
196
197 To dequeue multiple messages at one time, use :meth:`Queue.deqMany()`. This
198 takes an argument specifying the maximum number of messages to dequeue at one
199 time:
200
201 .. code-block:: python
202
203 for m in queue.deqMany(maxMessages=10):
204 print(m.payload.decode(connection.encoding))
205
206 Depending on the queue properties and the number of messages available to
207 dequeue, this code will print out from zero to ten messages.
0 .. _batchstmnt:
1
2 ******************************************
3 Batch Statement Execution and Bulk Loading
4 ******************************************
5
6 Inserting or updating multiple rows can be performed efficiently with
7 :meth:`Cursor.executemany()`, making it easy to work with large data sets with
8 cx_Oracle. This method can significantly outperform repeated calls to
9 :meth:`Cursor.execute()` by reducing network transfer costs and database overheads.
10 The :meth:`~Cursor.executemany()` method can also be used to execute PL/SQL
11 statements multiple times at once.
12
13 There are examples in the `GitHub examples
14 <https://github.com/oracle/python-cx_Oracle/tree/master/samples>`__
15 directory.
16
17 The following tables will be used in the samples that follow:
18
19 .. code-block:: sql
20
21 create table ParentTable (
22 ParentId number(9) not null,
23 Description varchar2(60) not null,
24 constraint ParentTable_pk primary key (ParentId)
25 );
26
27 create table ChildTable (
28 ChildId number(9) not null,
29 ParentId number(9) not null,
30 Description varchar2(60) not null,
31 constraint ChildTable_pk primary key (ChildId),
32 constraint ChildTable_fk foreign key (ParentId)
33 references ParentTable
34 );
35
36
37 Batch Execution of SQL
38 ======================
39
40 The following example inserts five rows into the table ``ParentTable``:
41
42 .. code-block:: python
43
44 dataToInsert = [
45 (10, 'Parent 10'),
46 (20, 'Parent 20'),
47 (30, 'Parent 30'),
48 (40, 'Parent 40'),
49 (50, 'Parent 50')
50 ]
51 cursor.executemany("insert into ParentTable values (:1, :2)", dataToInsert)
52
53 This code requires only one :ref:`round-trip <roundtrips>` from the client to
54 the database instead of the five round-trips that would be required for
55 repeated calls to :meth:`~Cursor.execute()`. For very large data sets there
56 may be an external buffer or network limits to how many rows can be processed,
57 so repeated calls to ``executemany()`` may be required. The limits are based
58 on both the number of rows being processed as well as the "size" of each row
59 that is being processed. Repeated calls to :meth:`~Cursor.executemany()` are
60 still better than repeated calls to :meth:`~Cursor.execute()`.
61
62
63 Batch Execution of PL/SQL
64 =========================
65
66 PL/SQL functions and procedures and anonymous PL/SQL blocks can also be called
67 using :meth:`~Cursor.executemany()` in order to improve performance. For
68 example:
69
70 .. code-block:: python
71
72 dataToInsert = [
73 (10, 'Parent 10'),
74 (20, 'Parent 20'),
75 (30, 'Parent 30'),
76 (40, 'Parent 40'),
77 (50, 'Parent 50')
78 ]
79 cursor.executemany("begin mypkg.create_parent(:1, :2); end;", dataToInsert)
80
81 Note that the ``batcherrors`` parameter (discussed below) cannot be used with
82 PL/SQL block execution.
83
84
85 Handling Data Errors
86 ====================
87
88 Large datasets may contain some invalid data. When using batch execution as
89 discussed above, the entire batch will be discarded if a single error is
90 detected, potentially eliminating the performance benefits of batch execution
91 and increasing the complexity of the code required to handle those errors. If
92 the parameter ``batchErrors`` is set to the value ``True`` when calling
93 :meth:`~Cursor.executemany()`, however, processing will continue even if there
94 are data errors in some rows, and the rows containing errors can be examined
95 afterwards to determine what course the application should take. Note that if
96 any errors are detected, a transaction will be started but not committed, even
97 if :attr:`Connection.autocommit` is set to ``True``. After examining the errors
98 and deciding what to do with them, the application needs to explicitly commit
99 or roll back the transaction with :meth:`Connection.commit()` or
100 :meth:`Connection.rollback()`, as needed.
101
102 This example shows how data errors can be identified:
103
104 .. code-block:: python
105
106 dataToInsert = [
107 (60, 'Parent 60'),
108 (70, 'Parent 70'),
109 (70, 'Parent 70 (duplicate)'),
110 (80, 'Parent 80'),
111 (80, 'Parent 80 (duplicate)'),
112 (90, 'Parent 90')
113 ]
114 cursor.executemany("insert into ParentTable values (:1, :2)", dataToInsert,
115 batcherrors=True)
116 for error in cursor.getbatcherrors():
117 print("Error", error.message, "at row offset", error.offset)
118
119 The output is::
120
121 Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 2
122 Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 4
123
124 The row offset is the index into the array of the data that could not be
125 inserted due to errors. The application could choose to commit or rollback the
126 other rows that were successfully inserted. Alternatively, it could correct
127 the data for the two invalid rows and attempt to insert them again before
128 committing.
129
130
131 Identifying Affected Rows
132 =========================
133
134 When executing a DML statement using :meth:`~Cursor.execute()`, the number of
135 rows affected can be examined by looking at the attribute
136 :attr:`~Cursor.rowcount`. When performing batch executing with
137 :meth:`Cursor.executemany()`, however, the row count will return the *total*
138 number of rows that were affected. If you want to know the total number of rows
139 affected by each row of data that is bound you must set the parameter
140 ``arraydmlrowcounts`` to ``True``, as shown:
141
142 .. code-block:: python
143
144 parentIdsToDelete = [20, 30, 50]
145 cursor.executemany("delete from ChildTable where ParentId = :1",
146 [(i,) for i in parentIdsToDelete],
147 arraydmlrowcounts=True)
148 rowCounts = cursor.getarraydmlrowcounts()
149 for parentId, count in zip(parentIdsToDelete, rowCounts):
150 print("Parent ID:", parentId, "deleted", count, "rows.")
151
152 Using the data found in the `GitHub samples
153 <https://github.com/oracle/python-cx_Oracle/tree/master/samples>`__ the output
154 is as follows::
155
156 Parent ID: 20 deleted 3 rows.
157 Parent ID: 30 deleted 2 rows.
158 Parent ID: 50 deleted 4 rows.
159
160
161 DML RETURNING
162 =============
163
164 DML statements like INSERT, UPDATE, DELETE and MERGE can return values by using
165 the DML RETURNING syntax. A bind variable can be created to accept this data.
166 See :ref:`bind` for more information.
167
168 If, instead of merely deleting the rows as shown in the previous example, you
169 also wanted to know some information about each of the rows that were deleted,
170 you could use the following code:
171
172 .. code-block:: python
173
174 parentIdsToDelete = [20, 30, 50]
175 childIdVar = cursor.var(int, arraysize=len(parentIdsToDelete))
176 cursor.setinputsizes(None, childIdVar)
177 cursor.executemany("""
178 delete from ChildTable
179 where ParentId = :1
180 returning ChildId into :2""",
181 [(i,) for i in parentIdsToDelete])
182 for ix, parentId in enumerate(parentIdsToDelete):
183 print("Child IDs deleted for parent ID", parentId, "are",
184 childIdVar.getvalue(ix))
185
186 The output would then be::
187
188 Child IDs deleted for parent ID 20 are [1002, 1003, 1004]
189 Child IDs deleted for parent ID 30 are [1005, 1006]
190 Child IDs deleted for parent ID 50 are [1012, 1013, 1014, 1015]
191
192 Note that the bind variable created to accept the returned data must have an
193 arraysize large enough to hold data for each row that is processed. Also,
194 the call to :meth:`Cursor.setinputsizes()` binds this variable immediately so
195 that it does not have to be passed in each row of data.
196
197
198 Predefining Memory Areas
199 ========================
200
201 When multiple rows of data are being processed there is the possibility that
202 the data is not uniform in type and size. In such cases, cx_Oracle makes some
203 effort to accommodate such differences. Type determination for each column is
204 deferred until a value that is not ``None`` is found in the column's data. If
205 all values in a particular column are ``None``, then cx_Oracle assumes the type
206 is a string and has a length of 1. cx_Oracle will also adjust the size of the
207 buffers used to store strings and bytes when a longer value is encountered in
208 the data. These sorts of operations incur overhead as memory has to be
209 reallocated and data copied. To eliminate this overhead, using
210 :meth:`~Cursor.setinputsizes()` tells cx_Oracle about the type and size of the
211 data that is going to be used.
212
213 Consider the following code:
214
215 .. code-block:: python
216
217 data = [
218 ( 110, "Parent 110"),
219 ( 2000, "Parent 2000"),
220 ( 30000, "Parent 30000"),
221 ( 400000, "Parent 400000"),
222 (5000000, "Parent 5000000")
223 ]
224 cursor.setinputsizes(None, 20)
225 cursor.executemany("""
226 insert into ParentTable (ParentId, Description)
227 values (:1, :2)""", data)
228
229 In this example, without the call to :meth:`~Cursor.setinputsizes()`, cx_Oracle
230 would perform five allocations of increasing size as it discovered each new,
231 longer string. However ``cursor.setinputsizes(None, 20)`` tells cx_Oracle that
232 the maximum size of the strings that will be processed is 20 characters. Since
233 cx_Oracle allocates memory for each row based on this value, it is best not to
234 oversize it. The first parameter of ``None`` tells cx_Oracle that its default
235 processing will be sufficient.
236
237 Loading CSV Files into Oracle Database
238 ======================================
239
240 The :meth:`Cursor.executemany()` method and `csv module
241 <https://docs.python.org/3/library/csv.html#module-csv>`__ can be used to
242 efficiently load CSV (Comma Separated Values) files. For example, consider the
243 file ``data.csv``::
244
245 101,Abel
246 154,Baker
247 132,Charlie
248 199,Delta
249 . . .
250
251 And the schema:
252
253 .. code-block:: sql
254
255 create table test (id number, name varchar2(25));
256
257 Instead of looping through each line of the CSV file and inserting it
258 individually, you can insert batches of records using
259 :meth:`Cursor.executemany()`:
260
261 .. code-block:: python
262
263 import cx_Oracle
264 import csv
265
266 . . .
267
268 # Predefine the memory areas to match the table definition
269 cursor.setinputsizes(None, 25)
270
271 # Adjust the batch size to meet your memory and performance requirements
272 batch_size = 10000
273
274 with open('testsp.csv', 'r') as csv_file:
275 csv_reader = csv.reader(csv_file, delimiter=',')
276 sql = "insert into test (id,name) values (:1, :2)"
277 data = []
278 for line in csv_reader:
279 data.append((line[0], line[1]))
280 if len(data) % batch_size == 0:
281 cursor.executemany(sql, data)
282 data = []
283 if data:
284 cursor.executemany(sql, data)
285 con.commit()
0 .. _bind:
1
2 ********************
3 Using Bind Variables
4 ********************
5
6 SQL and PL/SQL statements that pass data to and from Oracle Database should use
7 placeholders in SQL and PL/SQL statements that mark where data is supplied or
8 returned. These placeholders are referred to as bind variables or bind
9 parameters A bind variable is a colon-prefixed identifier or numeral. For
10 example, there are two bind variables (``dept_id`` and ``dept_name``) in this
11 SQL statement:
12
13 .. code-block:: python
14
15 sql = """insert into departments (department_id, department_name)
16 values (:dept_id, :dept_name)"""
17 cursor.execute(sql, [280, "Facility"])
18
19 Using bind variables is important for scalability and security. They help avoid
20 SQL Injection security problems because data is never treated as part of an
21 executable statement. Never concatenate or interpolate user data into SQL
22 statements:
23
24 .. code-block:: python
25
26 did = 280
27 dnm = "Facility"
28
29 # !! Never do this !!
30 sql = f"""insert into departments (department_id, department_name)
31 values ({did}, {dnm})"""
32 cursor.execute(sql)
33
34 Bind variables reduce parsing and execution costs when statements are executed
35 more than once with different data values. If you do not use bind variables,
36 Oracle must reparse and cache multiple statements. When using bind variables,
37 Oracle Database may be able to reuse the statement execution plan and context.
38
39 Bind variables can be used to substitute data, but cannot be used to substitute
40 the text of the statement. You cannot, for example, use a bind variable where
41 a column name or a table name is required. Bind variables also cannot be used
42 in Data Definition Language (DDL) statements, such as CREATE TABLE or ALTER
43 statements.
44
45 Binding By Name or Position
46 ===========================
47
48 Binding can be done by name or by position. A named bind is performed when the
49 bind variables in a statement are associated with a name. For example:
50
51 .. code-block:: python
52
53 cursor.execute("""
54 insert into departments (department_id, department_name)
55 values (:dept_id, :dept_name)""", dept_id=280,
56 dept_name="Facility")
57
58 # alternatively, the parameters can be passed as a dictionary instead of as
59 # keyword parameters
60 data = { "dept_id": 280, "dept_name": "Facility" }
61 cursor.execute("""
62 insert into departments (department_id, department_name)
63 values (:dept_id, :dept_name)""", data)
64
65 In the above example, the keyword parameter names or the keys of the dictionary
66 must match the bind variable names. The advantages of this approach are that
67 the location of the bind variables in the statement is not important, the
68 names can be meaningful and the names can be repeated while still only
69 supplying the value once.
70
71 A positional bind is performed when a list of bind values are passed to the
72 execute() call. For example:
73
74 .. code-block:: python
75
76 cursor.execute("""
77 insert into departments (department_id, department_name)
78 values (:dept_id, :dept_name)""", [280, "Facility"])
79
80 Note that for SQL statements, the order of the bind values must exactly match
81 the order of each bind variable and duplicated names must have their values
82 repeated. For PL/SQL statements, however, the order of the bind values must
83 exactly match the order of each **unique** bind variable found in the PL/SQL
84 block and values should not be repeated. In order to avoid this difference,
85 binding by name is recommended when bind variable names are repeated.
86
87
88 Bind Direction
89 ==============
90
91 The caller can supply data to the database (IN), the database can return
92 data to the caller (OUT) or the caller can supply initial data to the
93 database and the database can supply the modified data back to the caller
94 (IN/OUT). This is known as the bind direction.
95
96 The examples shown above have all supplied data to the database and are
97 therefore classified as IN bind variables. In order to have the database return
98 data to the caller, a variable must be created. This is done by calling the
99 method :func:`Cursor.var()`, which identifies the type of data that will be
100 found in that bind variable and its maximum size among other things.
101
102 Here is an example showing how to use OUT binds. It calculates the sum of the
103 integers 8 and 7 and stores the result in an OUT bind variable of type integer:
104
105 .. code-block:: python
106
107 outVal = cursor.var(int)
108 cursor.execute("""
109 begin
110 :outVal := :inBindVar1 + :inBindVar2;
111 end;""", outVal=outVal, inBindVar1=8, inBindVar2=7)
112 print(outVal.getvalue()) # will print 15
113
114 If instead of simply getting data back you wish to supply an initial value to
115 the database, you can set the variable's initial value. This example is the
116 same as the previous one but it sets the initial value first:
117
118 .. code-block:: python
119
120 inOutVal = cursor.var(int)
121 inOutVal.setvalue(0, 25)
122 cursor.execute("""
123 begin
124 :inOutBindVar := :inOutBindVar + :inBindVar1 + :inBindVar2;
125 end;""", inOutBindVar=inOutVal, inBindVar1=8, inBindVar2=7)
126 print(inOutVal.getvalue()) # will print 40
127
128 When binding data to parameters of PL/SQL procedures that are declared as OUT
129 parameters, it is worth noting that any value that is set in the bind variable
130 will be ignored. In addition, any parameters declared as IN/OUT that do not
131 have a value set will start out with a value of ``null``.
132
133
134 Binding Null Values
135 ===================
136
137 In cx_Oracle, null values are represented by the Python singleton ``None``.
138
139 For example:
140
141 .. code-block:: python
142
143 cursor.execute("""
144 insert into departments (department_id, department_name)
145 values (:dept_id, :dept_name)""", dept_id=280, dept_name=None)
146
147 In this specific case, because the ``DEPARTMENT_NAME`` column is defined as a
148 ``NOT NULL`` column, an error will occur::
149
150 cx_Oracle.IntegrityError: ORA-01400: cannot insert NULL into ("HR"."DEPARTMENTS"."DEPARTMENT_NAME")
151
152
153 If this value is bound directly, cx_Oracle assumes it to be a string
154 (equivalent to a VARCHAR2 column). If you need to use a different Oracle type
155 you will need to make a call to :func:`Cursor.setinputsizes()` or create a bind
156 variable with the correct type by calling :func:`Cursor.var()`.
157
158
159 Binding ROWID Values
160 ====================
161
162 The pseudo-column ``ROWID`` uniquely identifies a row within a table. In
163 cx_Oracle, ROWID values are represented as strings. The example below shows
164 fetching a row and then updating that row by binding its rowid:
165
166 .. code-block:: python
167
168 # fetch the row
169 cursor.execute("""
170 select rowid, manager_id
171 from departments
172 where department_id = :dept_id""", dept_id=280)
173 rowid, manager_id = cursor.fetchone()
174
175 # update the row by binding ROWID
176 cursor.execute("""
177 update departments set
178 manager_id = :manager_id
179 where rowid = :rid""", manager_id=205, rid=rowid)
180
181
182 DML RETURNING Bind Variables
183 ============================
184
185 When a RETURNING clause is used with a DML statement like UPDATE,
186 INSERT, or DELETE, the values are returned to the application through
187 the use of OUT bind variables. Consider the following example:
188
189 .. code-block:: python
190
191 # The RETURNING INTO bind variable is a string
192 dept_name = cursor.var(str)
193
194 cursor.execute("""
195 update departments set
196 location_id = :loc_id
197 where department_id = :dept_id
198 returning department_name into :dept_name""",
199 loc_id=1700, dept_id=50, dept_name=dept_name)
200 print(dept_name.getvalue()) # will print ['Shipping']
201
202 In the above example, since the WHERE clause matches only one row, the output
203 contains a single item in the list. If the WHERE clause matched multiple rows,
204 however, the output would contain as many items as there were rows that were
205 updated.
206
207 No duplicate binds are allowed in a DML statement with a RETURNING clause, and
208 no duplication is allowed between bind variables in the DML section and the
209 RETURNING section of the statement.
210
211
212 LOB Bind Variables
213 ==================
214
215 Database CLOBs, NCLOBS, BLOBs and BFILEs can be bound with types
216 :attr:`cx_Oracle.DB_TYPE_CLOB`, :attr:`cx_Oracle.DB_TYPE_NCLOB`,
217 :attr:`cx_Oracle.DB_TYPE_BLOB` and :attr:`cx_Oracle.DB_TYPE_BFILE`
218 respectively. LOBs fetched from the database or created with
219 :meth:`Connection.createlob()` can also be bound.
220
221 LOBs may represent Oracle Database persistent LOBs (those stored in tables) or
222 temporary LOBs (such as those created with :meth:`Connection.createlob()` or
223 returned by some SQL and PL/SQL operations).
224
225 LOBs can be used as IN, OUT or IN/OUT bind variables.
226
227 See :ref:`lobdata` for examples.
228
229 .. _refcur:
230
231 REF CURSOR Bind Variables
232 =========================
233
234 cx_Oracle provides the ability to bind and define PL/SQL REF cursors. As an
235 example, consider the PL/SQL procedure:
236
237 .. code-block:: sql
238
239 CREATE OR REPLACE PROCEDURE find_employees (
240 p_query IN VARCHAR2,
241 p_results OUT SYS_REFCURSOR
242 ) AS
243 BEGIN
244 OPEN p_results FOR
245 SELECT employee_id, first_name, last_name
246 FROM employees
247 WHERE UPPER(first_name || ' ' || last_name || ' ' || email)
248 LIKE '%' || UPPER(p_query) || '%';
249 END;
250 /
251
252 A newly opened cursor can be bound to the REF CURSOR parameter, as shown in the
253 following Python code. After the PL/SQL procedure has been called with
254 :meth:`Cursor.callproc()`, the cursor can then be fetched just like any other
255 cursor which had executed a SQL query:
256
257 .. code-block:: python
258
259 refCursor = connection.cursor()
260 cursor.callproc("find_employees", ['Smith', refCursor])
261 for row in refCursor:
262 print(row)
263
264 With Oracle's `sample HR schema
265 <https://github.com/oracle/db-sample-schemas>`__ there are two
266 employees with the last name 'Smith' so the result is::
267
268 (159, 'Lindsey', 'Smith')
269 (171, 'William', 'Smith')
270
271 To return a REF CURSOR from a PL/SQL function, use ``cx_Oracle.DB_TYPE_CURSOR`` for the
272 return type of :meth:`Cursor.callfunc()`:
273
274 .. code-block:: python
275
276 refCursor = cursor.callfunc('example_package.f_get_cursor', cx_Oracle.DB_TYPE_CURSOR)
277 for row in refCursor:
278 print(row)
279
280 See :ref:`tuning` for information on how to tune REF CURSORS.
281
282 Binding PL/SQL Collections
283 ==========================
284
285 PL/SQL Collections like Associative Arrays can be bound as IN, OUT, and IN/OUT
286 variables. When binding IN values, an array can be passed directly as shown in
287 this example, which sums up the lengths of all of the strings in the provided
288 array. First the PL/SQL package definition:
289
290 .. code-block:: sql
291
292 create or replace package mypkg as
293
294 type udt_StringList is table of varchar2(100) index by binary_integer;
295
296 function DemoCollectionIn (
297 a_Values udt_StringList
298 ) return number;
299
300 end;
301 /
302
303 create or replace package body mypkg as
304
305 function DemoCollectionIn (
306 a_Values udt_StringList
307 ) return number is
308 t_ReturnValue number := 0;
309 begin
310 for i in 1..a_Values.count loop
311 t_ReturnValue := t_ReturnValue + length(a_Values(i));
312 end loop;
313 return t_ReturnValue;
314 end;
315
316 end;
317 /
318
319 Then the Python code:
320
321 .. code-block:: python
322
323 values = ["String One", "String Two", "String Three"]
324 returnVal = cursor.callfunc("mypkg.DemoCollectionIn", int, [values])
325 print(returnVal) # will print 32
326
327 In order get values back from the database, a bind variable must be created
328 using :meth:`Cursor.arrayvar()`. The first parameter to this method is a Python
329 type that cx_Oracle knows how to handle or one of the cx_Oracle :ref:`types`.
330 The second parameter is the maximum number of elements that the array can hold
331 or an array providing the value (and indirectly the maximum length). The final
332 parameter is optional and only used for strings and bytes. It identifies the
333 maximum length of the strings and bytes that can be stored in the array. If not
334 specified, the length defaults to 4000 bytes.
335
336 Consider the following PL/SQL package:
337
338 .. code-block:: sql
339
340 create or replace package mypkg as
341
342 type udt_StringList is table of varchar2(100) index by binary_integer;
343
344 procedure DemoCollectionOut (
345 a_NumElements number,
346 a_Values out nocopy udt_StringList
347 );
348
349 procedure DemoCollectionInOut (
350 a_Values in out nocopy udt_StringList
351 );
352
353 end;
354 /
355
356 create or replace package body mypkg as
357
358 procedure DemoCollectionOut (
359 a_NumElements number,
360 a_Values out nocopy udt_StringList
361 ) is
362 begin
363 for i in 1..a_NumElements loop
364 a_Values(i) := 'Demo out element #' || to_char(i);
365 end loop;
366 end;
367
368 procedure DemoCollectionInOut (
369 a_Values in out nocopy udt_StringList
370 ) is
371 begin
372 for i in 1..a_Values.count loop
373 a_Values(i) := 'Converted element #' || to_char(i) ||
374 ' originally had length ' || length(a_Values(i));
375 end loop;
376 end;
377
378 end;
379 /
380
381 The Python code to process an OUT collection would look as follows. Note the
382 call to :meth:`Cursor.arrayvar()` which creates space for an array of strings.
383 Each string would permit up to 100 bytes and only 10 strings would be
384 permitted. If the PL/SQL block exceeds the maximum number of strings allowed
385 the error ``ORA-06513: PL/SQL: index for PL/SQL table out of range for host
386 language array`` would be raised.
387
388 .. code-block:: python
389
390 outArrayVar = cursor.arrayvar(str, 10, 100)
391 cursor.callproc("mypkg.DemoCollectionOut", [5, outArrayVar])
392 for val in outArrayVar.getvalue():
393 print(val)
394
395 This would produce the following output::
396
397 Demo out element #1
398 Demo out element #2
399 Demo out element #3
400 Demo out element #4
401 Demo out element #5
402
403 The Python code to process an IN/OUT collections is similar. Note the different
404 call to :meth:`Cursor.arrayvar()` which creates space for an array of strings,
405 but uses an array to determine both the maximum length of the array and its
406 initial value.
407
408 .. code-block:: python
409
410 inValues = ["String One", "String Two", "String Three", "String Four"]
411 inOutArrayVar = cursor.arrayvar(str, inValues)
412 cursor.callproc("mypkg.DemoCollectionInOut", [inOutArrayVar])
413 for val in inOutArrayVar.getvalue():
414 print(val)
415
416 This would produce the following output::
417
418 Converted element #1 originally had length 10
419 Converted element #2 originally had length 10
420 Converted element #3 originally had length 12
421 Converted element #4 originally had length 11
422
423 If an array variable needs to have an initial value but also needs to allow
424 for more elements than the initial value contains, the following code can be
425 used instead:
426
427 .. code-block:: python
428
429 inOutArrayVar = cursor.arrayvar(str, 10, 100)
430 inOutArrayVar.setvalue(0, ["String One", "String Two"])
431
432 All of the collections that have been bound in preceding examples have used
433 contiguous array elements. If an associative array with sparse array elements
434 is needed, a different approach is required. Consider the following PL/SQL
435 code:
436
437 .. code-block:: sql
438
439 create or replace package mypkg as
440
441 type udt_StringList is table of varchar2(100) index by binary_integer;
442
443 procedure DemoCollectionOut (
444 a_Value out nocopy udt_StringList
445 );
446
447 end;
448 /
449
450 create or replace package body mypkg as
451
452 procedure DemoCollectionOut (
453 a_Value out nocopy udt_StringList
454 ) is
455 begin
456 a_Value(-1048576) := 'First element';
457 a_Value(-576) := 'Second element';
458 a_Value(284) := 'Third element';
459 a_Value(8388608) := 'Fourth element';
460 end;
461
462 end;
463 /
464
465 Note that the collection element indices are separated by large values. The
466 technique used above would fail with the exception ``ORA-06513: PL/SQL: index
467 for PL/SQL table out of range for host language array``. The code required to
468 process this collection looks like this instead:
469
470 .. code-block:: python
471
472 collectionType = connection.gettype("MYPKG.UDT_STRINGLIST")
473 collection = collectionType.newobject()
474 cursor.callproc("mypkg.DemoCollectionOut", [collection])
475 print(collection.aslist())
476
477 This produces the output::
478
479 ['First element', 'Second element', 'Third element', 'Fourth element']
480
481 Note the use of :meth:`Object.aslist()` which returns the collection element
482 values in index order as a simple Python list. The indices themselves are lost
483 in this approach. Starting from cx_Oracle 7.0, the associative array can be
484 turned into a Python dictionary using :meth:`Object.asdict()`. If that value
485 was printed in the previous example instead, the output would be::
486
487 {-1048576: 'First element', -576: 'Second element', 284: 'Third element', 8388608: 'Fourth element'}
488
489 If the elements need to be traversed in index order, the methods
490 :meth:`Object.first()` and :meth:`Object.next()` can be used. The method
491 :meth:`Object.getelement()` can be used to acquire the element at a particular
492 index. This is shown in the following code:
493
494 .. code-block:: python
495
496 ix = collection.first()
497 while ix is not None:
498 print(ix, "->", collection.getelement(ix))
499 ix = collection.next(ix)
500
501 This produces the output::
502
503 -1048576 -> First element
504 -576 -> Second element
505 284 -> Third element
506 8388608 -> Fourth element
507
508 Similarly, the elements can be traversed in reverse index order using the
509 methods :meth:`Object.last()` and :meth:`Object.prev()` as shown in the
510 following code:
511
512 .. code-block:: python
513
514 ix = collection.last()
515 while ix is not None:
516 print(ix, "->", collection.getelement(ix))
517 ix = collection.prev(ix)
518
519 This produces the output::
520
521 8388608 -> Fourth element
522 284 -> Third element
523 -576 -> Second element
524 -1048576 -> First element
525
526
527 Binding PL/SQL Records
528 ======================
529
530 PL/SQL record type objects can also be bound for IN, OUT and IN/OUT
531 bind variables. For example:
532
533 .. code-block:: sql
534
535 create or replace package mypkg as
536
537 type udt_DemoRecord is record (
538 NumberValue number,
539 StringValue varchar2(30),
540 DateValue date,
541 BooleanValue boolean
542 );
543
544 procedure DemoRecordsInOut (
545 a_Value in out nocopy udt_DemoRecord
546 );
547
548 end;
549 /
550
551 create or replace package body mypkg as
552
553 procedure DemoRecordsInOut (
554 a_Value in out nocopy udt_DemoRecord
555 ) is
556 begin
557 a_Value.NumberValue := a_Value.NumberValue * 2;
558 a_Value.StringValue := a_Value.StringValue || ' (Modified)';
559 a_Value.DateValue := a_Value.DateValue + 5;
560 a_Value.BooleanValue := not a_Value.BooleanValue;
561 end;
562
563 end;
564 /
565
566 Then this Python code can be used to call the stored procedure which will
567 update the record:
568
569 .. code-block:: python
570
571 # create and populate a record
572 recordType = connection.gettype("MYPKG.UDT_DEMORECORD")
573 record = recordType.newobject()
574 record.NUMBERVALUE = 6
575 record.STRINGVALUE = "Test String"
576 record.DATEVALUE = datetime.datetime(2016, 5, 28)
577 record.BOOLEANVALUE = False
578
579 # show the original values
580 print("NUMBERVALUE ->", record.NUMBERVALUE)
581 print("STRINGVALUE ->", record.STRINGVALUE)
582 print("DATEVALUE ->", record.DATEVALUE)
583 print("BOOLEANVALUE ->", record.BOOLEANVALUE)
584 print()
585
586 # call the stored procedure which will modify the record
587 cursor.callproc("mypkg.DemoRecordsInOut", [record])
588
589 # show the modified values
590 print("NUMBERVALUE ->", record.NUMBERVALUE)
591 print("STRINGVALUE ->", record.STRINGVALUE)
592 print("DATEVALUE ->", record.DATEVALUE)
593 print("BOOLEANVALUE ->", record.BOOLEANVALUE)
594
595 This will produce the following output::
596
597 NUMBERVALUE -> 6
598 STRINGVALUE -> Test String
599 DATEVALUE -> 2016-05-28 00:00:00
600 BOOLEANVALUE -> False
601
602 NUMBERVALUE -> 12
603 STRINGVALUE -> Test String (Modified)
604 DATEVALUE -> 2016-06-02 00:00:00
605 BOOLEANVALUE -> True
606
607 Note that when manipulating records, all of the attributes must be set by the
608 Python program in order to avoid an Oracle Client bug which will result in
609 unexpected values or the Python application segfaulting.
610
611 .. _spatial:
612
613 Binding Spatial Datatypes
614 =========================
615
616 Oracle Spatial datatypes objects can be represented by Python objects and their
617 attribute values can be read and updated. The objects can further be bound and
618 committed to database. This is similar to the examples above.
619
620 An example of fetching SDO_GEOMETRY is in :ref:`Oracle Database Objects and
621 Collections <fetchobjects>`.
622
623
624 .. _inputtypehandlers:
625
626 Changing Bind Data Types using an Input Type Handler
627 ====================================================
628
629 Input Type Handlers allow applications to change how data is bound to
630 statements, or even to enable new types to be bound directly.
631
632 An input type handler is enabled by setting the attribute
633 :attr:`Cursor.inputtypehandler` or :attr:`Connection.inputtypehandler`.
634
635 Input type handlers can be combined with variable converters to bind Python
636 objects seamlessly:
637
638 .. code-block:: python
639
640 # A standard Python object
641 class Building(object):
642 def __init__(self, buildingId, description, numFloors, dateBuilt):
643 self.buildingId = buildingId
644 self.description = description
645 self.numFloors = numFloors
646 self.dateBuilt = dateBuilt
647
648 building = Building(1, "Skyscraper 1", 5, datetime.date(2001, 5, 24))
649
650 # Get Python representation of the Oracle user defined type UDT_BUILDING
651 objType = con.gettype("UDT_BUILDING")
652
653 # convert a Python Building object to the Oracle user defined type UDT_BUILDING
654 def BuildingInConverter(value):
655 obj = objType.newobject()
656 obj.BUILDINGID = value.buildingId
657 obj.DESCRIPTION = value.description
658 obj.NUMFLOORS = value.numFloors
659 obj.DATEBUILT = value.dateBuilt
660 return obj
661
662 def InputTypeHandler(cursor, value, numElements):
663 if isinstance(value, Building):
664 return cursor.var(cx_Oracle.DB_TYPE_OBJECT, arraysize = numElements,
665 inconverter = BuildingInConverter, typename = objType.name)
666
667
668 # With the input type handler, the bound Python object is converted
669 # to the required Oracle object before being inserted
670 cur.inputtypehandler = InputTypeHandler
671 cur.execute("insert into myTable values (:1, :2)", (1, building))
672
673
674 Binding Multiple Values to a SQL WHERE IN Clause
675 ================================================
676
677 To use an IN clause with multiple values in a WHERE clause, you must define and
678 bind multiple values. You cannot bind an array of values. For example:
679
680 .. code-block:: python
681
682 cursor.execute("""
683 select employee_id, first_name, last_name
684 from employees
685 where last_name in (:name1, :name2)""",
686 name1="Smith", name2="Taylor")
687 for row in cursor:
688 print(row)
689
690 This will produce the following output::
691
692 (159, 'Lindsey', 'Smith')
693 (171, 'William', 'Smith')
694 (176, 'Jonathon', 'Taylor')
695 (180, 'Winston', 'Taylor')
696
697 If this sort of query is executed multiple times with differing numbers of
698 values, a bind variable should be included for each possible value up to the
699 maximum number of values that can be provided. Missing values can be bound with
700 the value ``None``. For example, if the query above is used for up to 5 values,
701 the code should be adjusted as follows:
702
703 .. code-block:: python
704
705 cursor.execute("""
706 select employee_id, first_name, last_name
707 from employees
708 where last_name in (:name1, :name2, :name3, :name4, :name5)""",
709 name1="Smith", name2="Taylor", name3=None, name4=None, name5=None)
710 for row in cursor:
711 print(row)
712
713 This will produce the same output as the original example.
714
715 If the number of values is only going to be known at runtime, then a SQL
716 statement can be built up as follows:
717
718 .. code-block:: python
719
720 bindValues = ["Gates", "Marvin", "Fay"]
721 bindNames = [":" + str(i + 1) for i in range(len(bindValues))]
722 sql = "select employee_id, first_name, last_name from employees " + \
723 "where last_name in (%s)" % (",".join(bindNames))
724 cursor.execute(sql, bindValues)
725 for row in cursor:
726 print(row)
727
728 Another solution for a larger number of values is to construct a SQL
729 statement like::
730
731 SELECT ... WHERE col IN ( <something that returns a list of rows> )
732
733 The easiest way to do the '<something that returns a list of rows>'
734 will depend on how the data is initially represented and the number of
735 items. You might look at using CONNECT BY or nested tables. Or,
736 for really large numbers of items, you might prefer to use a global
737 temporary table.
738
739 Binding Column and Table Names
740 ==============================
741
742 Column and table names cannot be bound in SQL queries. You can concatenate
743 text to build up a SQL statement, but make sure you use an Allow List or other
744 means to validate the data in order to avoid SQL Injection security issues:
745
746 .. code-block:: python
747
748 tableAllowList = ['employees', 'departments']
749 tableName = getTableName() # get the table name from user input
750 if tableName not in tableAllowList:
751 raise Exception('Invalid table name')
752 sql = 'select * from ' + tableName
753
754 Binding column names can be done either by using the above method or by using a
755 CASE statement. The example below demonstrates binding a column name in an
756 ORDER BY clause:
757
758 .. code-block:: python
759
760 sql = """
761 SELECT * FROM departments
762 ORDER BY
763 CASE :bindvar
764 WHEN 'department_id' THEN DEPARTMENT_ID
765 ELSE MANAGER_ID
766 END"""
767
768 columnName = getColumnName() # Obtain a column name from the user
769 cursor.execute(sql, [colname])
770
771 Depending on the name provided by the user, the query results will be
772 ordered either by the column ``DEPARTMENT_ID`` or the column ``MANAGER_ID``.
0 .. _connhandling:
1
2 *****************************
3 Connecting to Oracle Database
4 *****************************
5
6 Connections between cx_Oracle and Oracle Database are used for executing
7 :ref:`SQL <sqlexecution>`, :ref:`PL/SQL <plsqlexecution>`, and :ref:`SODA
8 <sodausermanual>`.
9
10 Establishing Database Connections
11 =================================
12
13 There are two ways to connect to Oracle Database using cx_Oracle:
14
15 * **Standalone connections**
16
17 These are useful when the application maintains a single user
18 session to a database. Connections are created by
19 :meth:`cx_Oracle.connect()` or its alias
20 :meth:`cx_Oracle.Connection()`.
21
22 * **Pooled connections**
23
24 :ref:`Connection pooling <connpool>` is important for performance when
25 applications frequently connect and disconnect from the database. Pools
26 support Oracle's :ref:`high availability <highavailability>` features and are
27 recommended for applications that must be reliable. Small pools can also be
28 useful for applications that want a few connections available for infrequent
29 use. Pools are created with :meth:`cx_Oracle.SessionPool()` at application
30 initialization time, and then :meth:`SessionPool.acquire()` can be called to
31 obtain a connection from a pool.
32
33 Many connection behaviors can be controlled by cx_Oracle options. Other
34 settings can be configured in :ref:`optnetfiles` or in :ref:`optclientfiles`.
35 These include limiting the amount of time that opening a connection can take, or
36 enabling :ref:`network encryption <netencrypt>`.
37
38 **Example: Standalone Connection to Oracle Database**
39
40 .. code-block:: python
41
42 import cx_Oracle
43
44 userpwd = ". . ." # Obtain password string from a user prompt or environment variable
45
46 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1", encoding="UTF-8")
47
48 cx_Oracle also supports :ref:`external authentication <extauth>` so
49 passwords do not need to be in the application.
50
51
52 Closing Connections
53 ===================
54
55 Connections should be released when they are no longer needed by calling
56 :meth:`Connection.close()`. Alternatively, you may prefer to let connections
57 be automatically cleaned up when references to them go out of scope. This lets
58 cx_Oracle close dependent resources in the correct order. One other approach is
59 the use of a "with" block, for example:
60
61 .. code-block:: python
62
63 with cx_Oracle.connect(userName, password, "dbhost.example.com/orclpdb1",
64 encoding="UTF-8") as connection:
65 cursor = connection.cursor()
66 cursor.execute("insert into SomeTable values (:1, :2)",
67 (1, "Some string"))
68 connection.commit()
69
70 This code ensures that, once the block is completed, the connection is closed
71 and resources have been reclaimed by the database. In addition, any attempt to
72 use the variable ``connection`` outside of the block will simply fail.
73
74 Prompt closing of connections is important when using connection pools so
75 connections are available for reuse by other pool users.
76
77 .. _connstr:
78
79 Connection Strings
80 ==================
81
82 The data source name parameter ``dsn`` of :meth:`cx_Oracle.connect()` and
83 :meth:`cx_Oracle.SessionPool()` is the Oracle Database connection string
84 identifying which database service to connect to. The ``dsn`` string can be one
85 of:
86
87 * An Oracle Easy Connect string
88 * An Oracle Net Connect Descriptor string
89 * A Net Service Name mapping to a connect descriptor
90
91 For more information about naming methods, see `Oracle Net Service Reference <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-E5358DEA-D619-4B7B-A799-3D2F802500F1>`__.
92
93 .. _easyconnect:
94
95 Easy Connect Syntax for Connection Strings
96 ------------------------------------------
97
98 An Easy Connect string is often the simplest connection string to use for the
99 data source name parameter ``dsn`` of :meth:`cx_Oracle.connect()` and
100 :meth:`cx_Oracle.SessionPool()`. This method does not need configuration files
101 such as ``tnsnames.ora``.
102
103 For example, to connect to the Oracle Database service ``orclpdb1`` that is
104 running on the host ``dbhost.example.com`` with the default Oracle
105 Database port 1521, use::
106
107 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
108 encoding="UTF-8")
109
110 If the database is using a non-default port, it must be specified::
111
112 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com:1984/orclpdb1",
113 encoding="UTF-8")
114
115 The Easy Connect syntax supports Oracle Database service names. It cannot be
116 used with the older System Identifiers (SID).
117
118 The Easy Connect syntax has been extended in recent versions of Oracle Database
119 client since its introduction in 10g. Check the Easy Connect Naming method in
120 `Oracle Net Service Administrator's Guide
121 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
122 id=GUID-B0437826-43C1-49EC-A94D-B650B6A4A6EE>`__ for the syntax to use in your
123 version of the Oracle Client libraries.
124
125 If you are using Oracle Client 19c, the latest `Easy Connect Plus
126 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
127 id=GUID-8C85D289-6AF3-41BC-848B-BF39D32648BA>`__ syntax allows the use of
128 multiple hosts or ports, along with optional entries for the wallet location,
129 the distinguished name of the database server, and even lets some network
130 configuration options be set. This means that a :ref:`sqlnet.ora <optnetfiles>`
131 file is not needed for some common connection scenarios.
132
133 Oracle Net Connect Descriptor Strings
134 -------------------------------------
135
136 The :meth:`cx_Oracle.makedsn()` function can be used to construct a connect
137 descriptor string for the data source name parameter ``dsn`` of
138 :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()`. The
139 :meth:`~cx_Oracle.makedsn()` function accepts the database hostname, the port
140 number, and the service name. It also supports :ref:`sharding <connsharding>`
141 syntax.
142
143 For example, to connect to the Oracle Database service ``orclpdb1`` that is
144 running on the host ``dbhost.example.com`` with the default Oracle
145 Database port 1521, use::
146
147 dsn = cx_Oracle.makedsn("dbhost.example.com", 1521, service_name="orclpdb1")
148 connection = cx_Oracle.connect("hr", userpwd, dsn, encoding="UTF-8")
149
150 Note the use of the named argument ``service_name``. By default, the third
151 parameter of :meth:`~cx_Oracle.makedsn()` is a database System Identifier (SID),
152 not a service name. However, almost all current databases use service names.
153
154 The value of ``dsn`` in this example is the connect descriptor string::
155
156 (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=dbhost.example.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orclpdb1)))
157
158 You can manually create similar connect descriptor strings. This lets you
159 extend the syntax, for example to support failover. These strings can be
160 embedded directly in the application::
161
162 dsn = """(DESCRIPTION=
163 (FAILOVER=on)
164 (ADDRESS_LIST=
165 (ADDRESS=(PROTOCOL=tcp)(HOST=sales1-svr)(PORT=1521))
166 (ADDRESS=(PROTOCOL=tcp)(HOST=sales2-svr)(PORT=1521)))
167 (CONNECT_DATA=(SERVICE_NAME=sales.example.com)))"""
168
169 connection = cx_Oracle.connect("hr", userpwd, dsn, encoding="UTF-8")
170
171 .. _netservice:
172
173 Net Service Names for Connection Strings
174 ----------------------------------------
175
176 Connect Descriptor Strings are commonly stored in a :ref:`tnsnames.ora
177 <optnetfiles>` file and associated with a Net Service Name. This name can be
178 used directly for the data source name parameter ``dsn`` of
179 :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()`. For example,
180 given a ``tnsnames.ora`` file with the following contents::
181
182 ORCLPDB1 =
183 (DESCRIPTION =
184 (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521))
185 (CONNECT_DATA =
186 (SERVER = DEDICATED)
187 (SERVICE_NAME = orclpdb1)
188 )
189 )
190
191 then you could connect using the following code::
192
193 connection = cx_Oracle.connect("hr", userpwd, "orclpdb1", encoding="UTF-8")
194
195 For more information about Net Service Names, see
196 `Database Net Services Reference
197 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-12C94B15-2CE1-4B98-9D0C-8226A9DDF4CB>`__.
198
199 JDBC and Oracle SQL Developer Connection Strings
200 ------------------------------------------------
201
202 The cx_Oracle connection string syntax is different to Java JDBC and the common
203 Oracle SQL Developer syntax. If these JDBC connection strings reference a
204 service name like::
205
206 jdbc:oracle:thin:@hostname:port/service_name
207
208 for example::
209
210 jdbc:oracle:thin:@dbhost.example.com:1521/orclpdb1
211
212 then use Oracle's Easy Connect syntax in cx_Oracle::
213
214 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com:1521/orclpdb1", encoding="UTF-8")
215
216 Alternatively, if a JDBC connection string uses an old-style Oracle SID "system
217 identifier", and the database does not have a service name::
218
219 jdbc:oracle:thin:@hostname:port:sid
220
221 for example::
222
223 jdbc:oracle:thin:@dbhost.example.com:1521:orcl
224
225 then a connect descriptor string from ``makedsn()`` can be used in the
226 application::
227
228 dsn = cx_Oracle.makedsn("dbhost.example.com", 1521, sid="orcl")
229 connection = cx_Oracle.connect("hr", userpwd, dsn, encoding="UTF-8")
230
231 Alternatively, create a ``tnsnames.ora`` (see :ref:`optnetfiles`) entry, for
232 example::
233
234 finance =
235 (DESCRIPTION =
236 (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521))
237 (CONNECT_DATA =
238 (SID = ORCL)
239 )
240 )
241
242 This can be referenced in cx_Oracle::
243
244 connection = cx_Oracle.connect("hr", userpwd, "finance", encoding="UTF-8")
245
246 .. _connpool:
247
248 Connection Pooling
249 ==================
250
251 cx_Oracle's connection pooling lets applications create and maintain a pool of
252 connections to the database. Connection pooling is important for performance
253 when applications frequently connect and disconnect from the database. The pool
254 implementation uses Oracle's `session pool technology
255 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
256 id=GUID-F9662FFB-EAEF-495C-96FC-49C6D1D9625C>`__ which supports Oracle's
257 :ref:`high availability <highavailability>` features and is recommended for
258 applications that must be reliable. This also means that small pools can be
259 useful for applications that want a few connections available for infrequent
260 use.
261
262 A connection pool is created by calling :meth:`~cx_Oracle.SessionPool()`. This
263 is generally called during application initialization. The initial pool size
264 and the maximum pool size are provided at the time of pool creation. When the
265 pool needs to grow, new connections are created automatically. The pool can
266 shrink back to the minimum size when connections are no longer in use. For
267 pools created with :ref:`external authentication <extauth>`, with
268 :ref:`homogeneous <connpooltypes>` set to False, or when using :ref:`drcp`, then
269 the number of connections initially created is zero even if a larger value is
270 specified for ``min``. Also in these cases the pool increment is always 1,
271 regardless of the value of ``increment``.
272
273 After a pool has been created, connections can be obtained from it by calling
274 :meth:`~SessionPool.acquire()`. These connections can be used in the same way
275 that standalone connections are used.
276
277 Connections acquired from the pool should be released back to the pool using
278 :meth:`SessionPool.release()` or :meth:`Connection.close()` when they are no
279 longer required. Otherwise, they will be released back to the pool
280 automatically when all of the variables referencing the connection go out of
281 scope. This make connections available for other users of the pool.
282
283 The session pool can be completely closed using :meth:`SessionPool.close()`.
284
285 The example below shows how to connect to Oracle Database using a
286 connection pool:
287
288 .. code-block:: python
289
290 # Create the session pool
291 pool = cx_Oracle.SessionPool("hr", userpwd,
292 "dbhost.example.com/orclpdb1", min=2, max=5, increment=1, encoding="UTF-8")
293
294 # Acquire a connection from the pool
295 connection = pool.acquire()
296
297 # Use the pooled connection
298 cursor = connection.cursor()
299 for result in cursor.execute("select * from mytab"):
300 print(result)
301
302 # Release the connection to the pool
303 pool.release(connection)
304
305 # Close the pool
306 pool.close()
307
308 Other :meth:`cx_Oracle.SessionPool()` options can be used at pool creation. For
309 example the ``getmode`` value can be set so that any ``aquire()`` call will wait
310 for a connection to become available if all are currently in use, for example:
311
312 .. code-block:: python
313
314 # Create the session pool
315 pool = cx_Oracle.SessionPool("hr", userpwd, "dbhost.example.com/orclpdb1",
316 min=2, max=5, increment=1, getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT, encoding="UTF-8")
317
318
319 Applications that are using connections concurrently in multiple threads should
320 set the ``threaded`` parameter to *True* when creating a connection pool:
321
322 .. code-block:: python
323
324 # Create the session pool
325 pool = cx_Oracle.SessionPool("hr", userpwd, "dbhost.example.com/orclpdb1",
326 min=2, max=5, increment=1, threaded=True, encoding="UTF-8")
327
328
329 See `ConnectionPool.py
330 <https://github.com/oracle/python-cx_Oracle/tree/master/samples/ConnectionPool.py>`__
331 for an example.
332
333 Before :meth:`SessionPool.acquire()` returns, cx_Oracle does a lightweight check
334 to see if the network transport for the selected connection is still open. If
335 it is not, then :meth:`~SessionPool.acquire()` will clean up the connection and
336 return a different one. This check will not detect cases such as where the
337 database session has been killed by the DBA, or reached a database resource
338 manager quota limit. To help in those cases, :meth:`~SessionPool.acquire()`
339 will also do a full :ref:`round-trip <roundtrips>` ping to the database when it
340 is about to return a connection that was unused in the pool for 60 seconds. If
341 the ping fails, the connection will be discarded and another one obtained before
342 :meth:`~SessionPool.acquire()` returns to the application. Because this full
343 ping is time based, it won't catch every failure. Also network timeouts and
344 session kills may occur after :meth:`~SessionPool.acquire()` and before
345 :meth:`Cursor.execute()`. To handle these cases, applications need to check
346 for errors after each :meth:`~Cursor.execute()` and make application-specific
347 decisions about retrying work if there was a connection failure. Oracle's
348 :ref:`Application Continuity <highavailability>` can do this automatically in
349 some cases. Note both the lightweight and full ping connection checks can mask
350 performance-impacting configuration issues, for example firewalls killing
351 connections, so monitor the connection rate in `AWR
352 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
353 for an unexpected value. You can explicitly initiate a full ping to check
354 connection liveness with :meth:`Connection.ping()` but overuse will impact
355 performance and scalability.
356
357 Connection Pool Sizing
358 ----------------------
359
360 The Oracle Real-World Performance Group's recommendation is to use fixed size
361 connection pools. The values of ``min`` and ``max`` should be the same (and the
362 ``increment`` equal to zero). This avoids connection storms which can decrease
363 throughput. See `Guideline for Preventing Connection Storms: Use Static Pools
364 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-7DFBA826-7CC0-4D16-B19C-31D168069B54>`__,
365 which contains more details about sizing of pools. Having a fixed size will
366 guarantee that the database can handle the upper pool size. For example, if a
367 pool needs to grow but the database resources are limited, then
368 :meth:`SessionPool.acquire()` may return errors such as ORA-28547. With a fixed
369 pool size, this class of error will occur when the pool is created, allowing you
370 to change the size before users access the application. With a dynamically
371 growing pool, the error may occur much later after the pool has been in use for
372 some time.
373
374 The Real-World Performance Group also recommends keeping pool sizes small, as
375 they may perform better than larger pools. The pool attributes should be
376 adjusted to handle the desired workload within the bounds of available resources
377 in cx_Oracle and the database.
378
379 Make sure the :ref:`firewall <hanetwork>`, `resource manager
380 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-2BEF5482-CF97-4A85-BD90-9195E41E74EF>`__
381 or user profile `IDLE_TIME
382 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-ABC7AE4D-64A8-4EA9-857D-BEF7300B64C3>`__
383 do not expire idle sessions, since this will require connections be recreated,
384 which will impact performance and scalability.
385
386 .. _sessioncallback:
387
388 Session CallBacks for Setting Pooled Connection State
389 -----------------------------------------------------
390
391 Applications can set "session" state in each connection. Examples of session
392 state are NLS settings from ``ALTER SESSION`` statements. Pooled connections
393 will retain their session state after they have been released back to the pool.
394 However, because pools can grow, or connections in the pool can be recreated,
395 there is no guarantee a subsequent :meth:`~SessionPool.acquire()` call will
396 return a database connection that has any particular state.
397
398 The :meth:`~cx_Oracle.SessionPool()` parameter ``sessionCallback``
399 enables efficient setting of session state so that connections have a
400 known session state, without requiring that state to be explicitly set
401 after each :meth:`~SessionPool.acquire()` call.
402
403 Connections can also be tagged when they are released back to the pool. The
404 tag is a user-defined string that represents the session state of the
405 connection. When acquiring connections, a particular tag can be requested. If
406 a connection with that tag is available, it will be returned. If not, then
407 another session will be returned. By comparing the actual and requested tags,
408 applications can determine what exact state a session has, and make any
409 necessary changes.
410
411 The session callback can be a Python function or a PL/SQL procedure.
412
413 There are three common scenarios for ``sessionCallback``:
414
415 - When all connections in the pool should have the same state, use a
416 Python callback without tagging.
417
418 - When connections in the pool require different state for different
419 users, use a Python callback with tagging.
420
421 - When using :ref:`drcp`: use a PL/SQL callback with tagging.
422
423
424 **Python Callback**
425
426 If the ``sessionCallback`` parameter is a Python procedure, it will be called
427 whenever :meth:`~SessionPool.acquire()` will return a newly created database
428 connection that has not been used before. It is also called when connection
429 tagging is being used and the requested tag is not identical to the tag in the
430 connection returned by the pool.
431
432 An example is:
433
434 .. code-block:: python
435
436 # Set the NLS_DATE_FORMAT for a session
437 def initSession(connection, requestedTag):
438 cursor = connection.cursor()
439 cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI'")
440
441 # Create the pool with session callback defined
442 pool = cx_Oracle.SessionPool("hr", userpwd, "orclpdb1",
443 sessionCallback=initSession, encoding="UTF-8")
444
445 # Acquire a connection from the pool (will always have the new date format)
446 connection = pool.acquire()
447
448 If needed, the ``initSession()`` procedure is called internally before
449 ``acquire()`` returns. It will not be called when previously used connections
450 are returned from the pool. This means that the ALTER SESSION does not need to
451 be executed after every ``acquire()`` call. This improves performance and
452 scalability.
453
454 In this example tagging was not being used, so the ``requestedTag`` parameter
455 is ignored.
456
457 Note: if you need to execute multiple SQL statements in the callback, use an
458 anonymous PL/SQL block to save :ref:`round-trips <roundtrips>` of repeated
459 ``execute()`` calls. With ALTER SESSION, pass multiple settings in the one
460 statement:
461
462 .. code-block:: python
463
464 cursor.execute("""
465 begin
466 execute immediate
467 'alter session set nls_date_format = ''YYYY-MM-DD'' nls_language = AMERICAN';
468 -- other SQL statements could be put here
469 end;""")
470
471 **Connection Tagging**
472
473 Connection tagging is used when connections in a pool should have differing
474 session states. In order to retrieve a connection with a desired state, the
475 ``tag`` attribute in :meth:`~SessionPool.acquire()` needs to be set.
476
477 When cx_Oracle is using Oracle Client libraries 12.2 or later, then cx_Oracle
478 uses 'multi-property tags' and the tag string must be of the form of one or
479 more "name=value" pairs separated by a semi-colon, for example
480 ``"loc=uk;lang=cy"``.
481
482 When a connection is requested with a given tag, and a connection with that tag
483 is not present in the pool, then a new connection, or an existing connection
484 with cleaned session state, will be chosen by the pool and the session callback
485 procedure will be invoked. The callback can then set desired session state and
486 update the connection's tag. However if the ``matchanytag`` parameter of
487 :meth:`~SessionPool.acquire()` is *True*, then any other tagged connection may
488 be chosen by the pool and the callback procedure should parse the actual and
489 requested tags to determine which bits of session state should be reset.
490
491 The example below demonstrates connection tagging:
492
493 .. code-block:: python
494
495 def initSession(connection, requestedTag):
496 if requestedTag == "NLS_DATE_FORMAT=SIMPLE":
497 sql = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'"
498 elif requestedTag == "NLS_DATE_FORMAT=FULL":
499 sql = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI'"
500 cursor = connection.cursor()
501 cursor.execute(sql)
502 connection.tag = requestedTag
503
504 pool = cx_Oracle.SessionPool("hr", userpwd, "orclpdb1",
505 sessionCallback=initSession, encoding="UTF-8")
506
507 # Two connections with different session state:
508 connection1 = pool.acquire(tag = "NLS_DATE_FORMAT=SIMPLE")
509 connection2 = pool.acquire(tag = "NLS_DATE_FORMAT=FULL")
510
511 See `SessionCallback.py
512 <https://github.com/oracle/python-cx_Oracle/tree/master/
513 samples/SessionCallback.py>`__ for an example.
514
515 **PL/SQL Callback**
516
517 When cx_Oracle uses Oracle Client 12.2 or later, the session callback can also
518 be the name of a PL/SQL procedure. A PL/SQL callback will be initiated only
519 when the tag currently associated with a connection does not match the tag that
520 is requested. A PL/SQL callback is most useful when using :ref:`drcp` because
521 DRCP does not require a :ref:`round-trip <roundtrips>` to invoke a PL/SQL
522 session callback procedure.
523
524 The PL/SQL session callback should accept two VARCHAR2 arguments:
525
526 .. code-block:: sql
527
528 PROCEDURE myPlsqlCallback (
529 requestedTag IN VARCHAR2,
530 actualTag IN VARCHAR2
531 );
532
533 The logic in this procedure can parse the actual tag in the session that has
534 been selected by the pool and compare it with the tag requested by the
535 application. The procedure can then change any state required before the
536 connection is returned to the application from :meth:`~SessionPool.acquire()`.
537
538 If the ``matchanytag`` attribute of :meth:`~SessionPool.acquire()` is *True*,
539 then a connection with any state may be chosen by the pool.
540
541 Oracle 'multi-property tags' must be used. The tag string must be of the form
542 of one or more "name=value" pairs separated by a semi-colon, for example
543 ``"loc=uk;lang=cy"``.
544
545 In cx_Oracle set ``sessionCallback`` to the name of the PL/SQL procedure. For
546 example:
547
548 .. code-block:: python
549
550 pool = cx_Oracle.SessionPool("hr", userpwd, "dbhost.example.com/orclpdb1:pooled",
551 sessionCallback="myPlsqlCallback", encoding="UTF-8")
552
553 connection = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE",
554 # DRCP options, if you are using DRCP
555 cclass='MYCLASS', purity=cx_Oracle.ATTR_PURITY_SELF)
556
557 See `SessionCallbackPLSQL.py
558 <https://github.com/oracle/python-cx_Oracle/tree/master/
559 samples/SessionCallbackPLSQL.py>`__ for an example.
560
561 .. _connpooltypes:
562
563 Heterogeneous and Homogeneous Connection Pools
564 ----------------------------------------------
565
566 By default, connection pools are ‘homogeneous’, meaning that all connections
567 use the same database credentials. However, if the pool option ``homogeneous``
568 is False at the time of pool creation, then a ‘heterogeneous’ pool will be
569 created. This allows different credentials to be used each time a connection
570 is acquired from the pool with :meth:`~SessionPool.acquire()`.
571
572 **Heterogeneous Pools**
573
574 When a heterogeneous pool is created by setting ``homogeneous`` to False and no
575 credentials are supplied during pool creation, then a user name and password
576 may be passed to :meth:`~SessionPool.acquire()` as shown in this example:
577
578 .. code-block:: python
579
580 pool = cx_Oracle.SessionPool(dsn="dbhost.example.com/orclpdb1", homogeneous=False,
581 encoding="UTF-8")
582 connection = pool.acquire(user="hr", password=userpwd)
583
584 .. _drcp:
585
586 Database Resident Connection Pooling (DRCP)
587 ===========================================
588
589 `Database Resident Connection Pooling (DRCP)
590 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
591 id=GUID-015CA8C1-2386-4626-855D-CC546DDC1086>`__ enables database resource
592 sharing for applications that run in multiple client processes, or run on
593 multiple middle-tier application servers. By default each connection from
594 Python will use one database server process. DRCP allows pooling of these
595 server processes. This reduces the amount of memory required on the database
596 host. The DRCP pool can be shared by multiple applications.
597
598 DRCP is useful for applications which share the same database credentials, have
599 similar session settings (for example date format settings or PL/SQL package
600 state), and where the application gets a database connection, works on it for a
601 relatively short duration, and then releases it.
602
603 Applications can choose whether or not to use pooled connections at runtime.
604
605 For efficiency, it is recommended that DRCP connections should be used
606 in conjunction with cx_Oracle’s local :ref:`connection pool <connpool>`.
607
608 **Using DRCP in Python**
609
610 Using DRCP with cx_Oracle applications involves the following steps:
611
612 1. Configuring and enabling DRCP in the database
613 2. Configuring the application to use a DRCP connection
614 3. Deploying the application
615
616 **Configuring and enabling DRCP**
617
618 Every instance of Oracle Database uses a single, default connection
619 pool. The pool can be configured and administered by a DBA using the
620 ``DBMS_CONNECTION_POOL`` package:
621
622 .. code-block:: sql
623
624 EXECUTE DBMS_CONNECTION_POOL.CONFIGURE_POOL(
625 pool_name => 'SYS_DEFAULT_CONNECTION_POOL',
626 minsize => 4,
627 maxsize => 40,
628 incrsize => 2,
629 session_cached_cursors => 20,
630 inactivity_timeout => 300,
631 max_think_time => 600,
632 max_use_session => 500000,
633 max_lifetime_session => 86400)
634
635 Alternatively the method ``DBMS_CONNECTION_POOL.ALTER_PARAM()`` can
636 set a single parameter:
637
638 .. code-block:: sql
639
640 EXECUTE DBMS_CONNECTION_POOL.ALTER_PARAM(
641 pool_name => 'SYS_DEFAULT_CONNECTION_POOL',
642 param_name => 'MAX_THINK_TIME',
643 param_value => '1200')
644
645 The ``inactivity_timeout`` setting terminates idle pooled servers, helping
646 optimize database resources. To avoid pooled servers permanently being held
647 onto by a selfish Python script, the ``max_think_time`` parameter can be set.
648 The parameters ``num_cbrok`` and ``maxconn_cbrok`` can be used to distribute
649 the persistent connections from the clients across multiple brokers. This may
650 be needed in cases where the operating system per-process descriptor limit is
651 small. Some customers have found that having several connection brokers
652 improves performance. The ``max_use_session`` and ``max_lifetime_session``
653 parameters help protect against any unforeseen problems affecting server
654 processes. The default values will be suitable for most users. See the
655 `Oracle DRCP documentation
656 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
657 id=GUID-015CA8C1-2386-4626-855D-CC546DDC1086>`__ for details on parameters.
658
659 In general, if pool parameters are changed, the pool should be restarted,
660 otherwise server processes will continue to use old settings.
661
662 There is a ``DBMS_CONNECTION_POOL.RESTORE_DEFAULTS()`` procedure to
663 reset all values.
664
665 When DRCP is used with RAC, each database instance has its own connection
666 broker and pool of servers. Each pool has the identical configuration. For
667 example, all pools start with ``minsize`` server processes. A single
668 DBMS_CONNECTION_POOL command will alter the pool of each instance at the same
669 time. The pool needs to be started before connection requests begin. The
670 command below does this by bringing up the broker, which registers itself with
671 the database listener:
672
673 .. code-block:: sql
674
675 EXECUTE DBMS_CONNECTION_POOL.START_POOL()
676
677 Once enabled this way, the pool automatically restarts when the database
678 instance restarts, unless explicitly stopped with the
679 ``DBMS_CONNECTION_POOL.STOP_POOL()`` command:
680
681 .. code-block:: sql
682
683 EXECUTE DBMS_CONNECTION_POOL.STOP_POOL()
684
685 The pool cannot be stopped while connections are open.
686
687 **Application Deployment for DRCP**
688
689 In order to use DRCP, the ``cclass`` and ``purity`` parameters should
690 be passed to :meth:`cx_Oracle.connect()` or :meth:`SessionPool.acquire()`. If
691 ``cclass`` is not set, the pooled server sessions will not be reused optimally,
692 and the DRCP statistic views will record large values for NUM_MISSES.
693
694 The DRCP ``purity`` can be one of ``ATTR_PURITY_NEW``, ``ATTR_PURITY_SELF``,
695 or ``ATTR_PURITY_DEFAULT``. The value ``ATTR_PURITY_SELF`` allows reuse of
696 both the pooled server process and session memory, giving maximum benefit from
697 DRCP. See the Oracle documentation on `benefiting from scalability
698 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
699 id=GUID-661BB906-74D2-4C5D-9C7E-2798F76501B3>`__.
700
701 The connection string used for :meth:`~cx_Oracle.connect()` or
702 :meth:`~SessionPool.acquire()` must request a pooled server by
703 following one of the syntaxes shown below:
704
705 Using Oracle’s Easy Connect syntax, the connection would look like:
706
707 .. code-block:: python
708
709 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orcl:pooled",
710 encoding="UTF-8")
711
712 Or if you connect using a Net Service Name named ``customerpool``:
713
714 .. code-block:: python
715
716 connection = cx_Oracle.connect("hr", userpwd, "customerpool", encoding="UTF-8")
717
718 Then only the Oracle Network configuration file ``tnsnames.ora`` needs
719 to be modified::
720
721 customerpool = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)
722 (HOST=dbhost.example.com)
723 (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=CUSTOMER)
724 (SERVER=POOLED)))
725
726 If these changes are made and the database is not actually configured for DRCP,
727 or the pool is not started, then connections will not succeed and an error will
728 be returned to the Python application.
729
730 Although applications can choose whether or not to use pooled connections at
731 runtime, care must be taken to configure the database appropriately for the
732 number of expected connections, and also to stop inadvertent use of non-DRCP
733 connections leading to a database server resource shortage. Conversely, avoid
734 using DRCP connections for long-running operations.
735
736 The example below shows how to connect to Oracle Database using Database
737 Resident Connection Pooling:
738
739 .. code-block:: python
740
741 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orcl:pooled",
742 cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF, encoding="UTF-8")
743
744 The example below shows connecting to Oracle Database using DRCP and
745 cx_Oracle's connection pooling:
746
747 .. code-block:: python
748
749 mypool = cx_Oracle.SessionPool("hr", userpwd, "dbhost.example.com/orcl:pooled",
750 encoding="UTF-8")
751 connection = mypool.acquire(cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF)
752
753 For more information about DRCP see `Oracle Database Concepts Guide
754 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
755 id=GUID-531EEE8A-B00A-4C03-A2ED-D45D92B3F797>`__, and for DRCP Configuration
756 see `Oracle Database Administrator's Guide
757 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
758 id=GUID-82FF6896-F57E-41CF-89F7-755F3BC9C924>`__.
759
760 **Closing Connections**
761
762 Python scripts where cx_Oracle connections do not go out of scope quickly
763 (which releases them), or do not currently use :meth:`Connection.close()`,
764 should be examined to see if :meth:`~Connection.close()` can be used, which
765 then allows maximum use of DRCP pooled servers by the database:
766
767 .. code-block:: python
768
769 # Do some database operations
770 connection = mypool.acquire(cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF)
771 . . .
772 connection.close();
773
774 # Do lots of non-database work
775 . . .
776
777 # Do some more database operations
778 connection = mypool.acquire(cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF)
779 . . .
780 connection.close();
781
782 **Monitoring DRCP**
783
784 Data dictionary views are available to monitor the performance of DRCP.
785 Database administrators can check statistics such as the number of busy and
786 free servers, and the number of hits and misses in the pool against the total
787 number of requests from clients. The views are:
788
789 * ``DBA_CPOOL_INFO``
790 * ``V$PROCESS``
791 * ``V$SESSION``
792 * ``V$CPOOL_STATS``
793 * ``V$CPOOL_CC_STATS``
794 * ``V$CPOOL_CONN_INFO``
795
796 **DBA_CPOOL_INFO View**
797
798 ``DBA_CPOOL_INFO`` displays configuration information about the DRCP pool. The
799 columns are equivalent to the ``dbms_connection_pool.configure_pool()``
800 settings described in the table of DRCP configuration options, with the
801 addition of a ``STATUS`` column. The status is ``ACTIVE`` if the pool has been
802 started and ``INACTIVE`` otherwise. Note the pool name column is called
803 ``CONNECTION_POOL``. This example checks whether the pool has been started and
804 finds the maximum number of pooled servers::
805
806 SQL> SELECT connection_pool, status, maxsize FROM dba_cpool_info;
807
808 CONNECTION_POOL STATUS MAXSIZE
809 ---------------------------- ---------- ----------
810 SYS_DEFAULT_CONNECTION_POOL ACTIVE 40
811
812 **V$PROCESS and V$SESSION Views**
813
814 The ``V$SESSION`` view shows information about the currently active DRCP
815 sessions. It can also be joined with ``V$PROCESS`` via
816 ``V$SESSION.PADDR = V$PROCESS.ADDR`` to correlate the views.
817
818 **V$CPOOL_STATS View**
819
820 The ``V$CPOOL_STATS`` view displays information about the DRCP statistics for
821 an instance. The V$CPOOL_STATS view can be used to assess how efficient the
822 pool settings are. T his example query shows an application using the pool
823 effectively. The low number of misses indicates that servers and sessions were
824 reused. The wait count shows just over 1% of requests had to wait for a pooled
825 server to become available::
826
827 NUM_REQUESTS NUM_HITS NUM_MISSES NUM_WAITS
828 ------------ ---------- ---------- ----------
829 10031 99990 40 1055
830
831 If ``cclass`` was set (allowing pooled servers and sessions to be
832 reused) then NUM_MISSES will be low. If the pool maxsize is too small for
833 the connection load, then NUM_WAITS will be high.
834
835 **V$CPOOL_CC_STATS View**
836
837 The view ``V$CPOOL_CC_STATS`` displays information about the connection class
838 level statistics for the pool per instance::
839
840 SQL> SELECT cclass_name, num_requests, num_hits, num_misses
841 FROM v$cpool_cc_stats;
842
843 CCLASS_NAME NUM_REQUESTS NUM_HITS NUM_MISSES
844 -------------------------------- ------------ ---------- ----------
845 HR.MYCLASS 100031 99993 38
846
847 **V$CPOOL_CONN_INFO View**
848
849 The ``V$POOL_CONN_INFO`` view gives insight into client processes that are
850 connected to the connection broker, making it easier to monitor and trace
851 applications that are currently using pooled servers or are idle. This view was
852 introduced in Oracle 11gR2.
853
854 You can monitor the view ``V$CPOOL_CONN_INFO`` to, for example, identify
855 misconfigured machines that do not have the connection class set correctly.
856 This view maps the machine name to the class name::
857
858 SQL> SELECT cclass_name, machine FROM v$cpool_conn_info;
859
860 CCLASS_NAME MACHINE
861 --------------------------------------- ------------
862 CJ.OCI:SP:wshbIFDtb7rgQwMyuYvodA cjlinux
863 . . .
864
865 In this example you would examine applications on ``cjlinux`` and make
866 sure ``cclass`` is set.
867
868
869 .. _proxyauth:
870
871 Connecting Using Proxy Authentication
872 =====================================
873
874 Proxy authentication allows a user (the "session user") to connect to Oracle
875 Database using the credentials of a 'proxy user'. Statements will run as the
876 session user. Proxy authentication is generally used in three-tier applications
877 where one user owns the schema while multiple end-users access the data. For
878 more information about proxy authentication, see the `Oracle documentation
879 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
880 id=GUID-D77D0D4A-7483-423A-9767-CBB5854A15CC>`__.
881
882 An alternative to using proxy users is to set
883 :attr:`Connection.client_identifier` after connecting and use its value in
884 statements and in the database, for example for :ref:`monitoring
885 <endtoendtracing>`.
886
887 The following proxy examples use these schemas. The ``mysessionuser`` schema is
888 granted access to use the password of ``myproxyuser``:
889
890 .. code-block:: sql
891
892 CREATE USER myproxyuser IDENTIFIED BY myproxyuserpw;
893 GRANT CREATE SESSION TO myproxyuser;
894
895 CREATE USER mysessionuser IDENTIFIED BY itdoesntmatter;
896 GRANT CREATE SESSION TO mysessionuser;
897
898 ALTER USER mysessionuser GRANT CONNECT THROUGH myproxyuser;
899
900 After connecting to the database, the following query can be used to show the
901 session and proxy users:
902
903 .. code-block:: sql
904
905 SELECT SYS_CONTEXT('USERENV', 'PROXY_USER'),
906 SYS_CONTEXT('USERENV', 'SESSION_USER')
907 FROM DUAL;
908
909 Standalone connection examples:
910
911 .. code-block:: python
912
913 # Basic Authentication without a proxy
914 connection = cx_Oracle.connect("myproxyuser", "myproxyuserpw", "dbhost.example.com/orclpdb1",
915 encoding="UTF-8")
916 # PROXY_USER: None
917 # SESSION_USER: MYPROXYUSER
918
919 # Basic Authentication with a proxy
920 connection = cx_Oracle.connect(user="myproxyuser[mysessionuser]", "myproxyuserpw",
921 "dbhost.example.com/orclpdb1", encoding="UTF-8")
922 # PROXY_USER: MYPROXYUSER
923 # SESSION_USER: MYSESSIONUSER
924
925 Pooled connection examples:
926
927 .. code-block:: python
928
929 # Basic Authentication without a proxy
930 pool = cx_Oracle.SessionPool("myproxyuser", "myproxyuser", "dbhost.example.com/orclpdb1",
931 encoding="UTF-8")
932 connection = pool.acquire()
933 # PROXY_USER: None
934 # SESSION_USER: MYPROXYUSER
935
936 # Basic Authentication with proxy
937 pool = cx_Oracle.SessionPool("myproxyuser[mysessionuser]", "myproxyuser",
938 "dbhost.example.com/orclpdb1", homogeneous=False, encoding="UTF-8")
939 connection = pool.acquire()
940 # PROXY_USER: MYPROXYUSER
941 # SESSION_USER: MYSESSIONUSER
942
943 Note the use of a :ref:`heterogeneous <connpooltypes>` pool in the example
944 above. This is required in this scenario.
945
946 .. _extauth:
947
948 Connecting Using External Authentication
949 ========================================
950
951 Instead of storing the database username and password in Python scripts or
952 environment variables, database access can be authenticated by an outside
953 system. External Authentication allows applications to validate user access by
954 an external password store (such as an Oracle Wallet), by the operating system,
955 or with an external authentication service.
956
957 Using an Oracle Wallet for External Authentication
958 --------------------------------------------------
959
960 The following steps give an overview of using an Oracle Wallet. Wallets should
961 be kept securely. Wallets can be managed with `Oracle Wallet Manager
962 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
963 id=GUID-E3E16C82-E174-4814-98D5-EADF1BCB3C37>`__.
964
965 In this example the wallet is created for the ``myuser`` schema in the directory
966 ``/home/oracle/wallet_dir``. The ``mkstore`` command is available from a full
967 Oracle client or Oracle Database installation. If you have been given wallet by
968 your DBA, skip to step 3.
969
970 1. First create a new wallet as the ``oracle`` user::
971
972 mkstore -wrl "/home/oracle/wallet_dir" -create
973
974 This will prompt for a new password for the wallet.
975
976 2. Create the entry for the database user name and password that are currently
977 hardcoded in your Python scripts. Use either of the methods shown below.
978 They will prompt for the wallet password that was set in the first step.
979
980 **Method 1 - Using an Easy Connect string**::
981
982 mkstore -wrl "/home/oracle/wallet_dir" -createCredential dbhost.example.com/orclpdb1 myuser myuserpw
983
984 **Method 2 - Using a connect name identifier**::
985
986 mkstore -wrl "/home/oracle/wallet_dir" -createCredential mynetalias myuser myuserpw
987
988 The alias key ``mynetalias`` immediately following the
989 ``-createCredential`` option will be the connect name to be used in Python
990 scripts. If your application connects with multiple different database
991 users, you could create a wallet entry with different connect names for
992 each.
993
994 You can see the newly created credential with::
995
996 mkstore -wrl "/home/oracle/wallet_dir" -listCredential
997
998 3. Skip this step if the wallet was created using an Easy Connect String.
999 Otherwise, add an entry in :ref:`tnsnames.ora <optnetfiles>` for the connect
1000 name as follows::
1001
1002 mynetalias =
1003 (DESCRIPTION =
1004 (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521))
1005 (CONNECT_DATA =
1006 (SERVER = DEDICATED)
1007 (SERVICE_NAME = orclpdb1)
1008 )
1009 )
1010
1011 The file uses the description for your existing database and sets the
1012 connect name alias to ``mynetalias``, which is the identifier used when
1013 adding the wallet entry.
1014
1015 4. Add the following wallet location entry in the :ref:`sqlnet.ora
1016 <optnetfiles>` file, using the ``DIRECTORY`` you created the wallet in::
1017
1018 WALLET_LOCATION =
1019 (SOURCE =
1020 (METHOD = FILE)
1021 (METHOD_DATA =
1022 (DIRECTORY = /home/oracle/wallet_dir)
1023 )
1024 )
1025 SQLNET.WALLET_OVERRIDE = TRUE
1026
1027 Examine the Oracle documentation for full settings and values.
1028
1029 5. Ensure the configuration files are in a default location or set TNS_ADMIN is
1030 set to the directory containing them. See :ref:`optnetfiles`.
1031
1032 With an Oracle wallet configured, and readable by you, your scripts
1033 can connect using::
1034
1035 connection = cx_Oracle.connect(dsn="mynetalias", encoding="UTF-8")
1036
1037 or::
1038
1039 pool = cx_Oracle.SessionPool(externalauth=True, homogeneous=False, dsn="mynetalias",
1040 encoding="UTF-8")
1041 pool.acquire()
1042
1043 The ``dsn`` must match the one used in the wallet.
1044
1045 After connecting, the query::
1046
1047 SELECT SYS_CONTEXT('USERENV', 'SESSION_USER') FROM DUAL;
1048
1049 will show::
1050
1051 MYUSER
1052
1053 .. note::
1054
1055 Wallets are also used to configure TLS connections. If you are using a
1056 wallet like this, you may need a database username and password in
1057 :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()` calls.
1058
1059 **External Authentication and Proxy Authentication**
1060
1061 The following examples show external wallet authentication combined with
1062 :ref:`proxy authentication <proxyauth>`. These examples use the wallet
1063 configuration from above, with the addition of a grant to another user::
1064
1065 ALTER USER mysessionuser GRANT CONNECT THROUGH myuser;
1066
1067 After connection, you can check who the session user is with:
1068
1069 .. code-block:: sql
1070
1071 SELECT SYS_CONTEXT('USERENV', 'PROXY_USER'),
1072 SYS_CONTEXT('USERENV', 'SESSION_USER')
1073 FROM DUAL;
1074
1075 Standalone connection example:
1076
1077 .. code-block:: python
1078
1079 # External Authentication with proxy
1080 connection = cx_Oracle.connect(user="[mysessionuser]", dsn="mynetalias", encoding="UTF-8")
1081 # PROXY_USER: MYUSER
1082 # SESSION_USER: MYSESSIONUSER
1083
1084 Pooled connection example:
1085
1086 .. code-block:: python
1087
1088 # External Authentication with proxy
1089 pool = cx_Oracle.SessionPool(externalauth=True, homogeneous=False, dsn="mynetalias",
1090 encoding="UTF-8")
1091 pool.acquire(user="[mysessionuser]")
1092 # PROXY_USER: MYUSER
1093 # SESSION_USER: MYSESSIONUSER
1094
1095 The following usage is not supported:
1096
1097 .. code-block:: python
1098
1099 pool = cx_Oracle.SessionPool("[mysessionuser]", externalauth=True, homogeneous=False,
1100 dsn="mynetalias", encoding="UTF-8")
1101 pool.acquire()
1102
1103
1104 Operating System Authentication
1105 -------------------------------
1106
1107 With Operating System authentication, Oracle allows user authentication to be
1108 performed by the operating system. The following steps give an overview of how
1109 to implement OS Authentication on Linux.
1110
1111 1. Login to your computer. The commands used in these steps assume the
1112 operating system user name is "oracle".
1113
1114 2. Login to SQL*Plus as the SYSTEM user and verify the value for the
1115 ``OS_AUTHENT_PREFIX`` parameter::
1116
1117 SQL> SHOW PARAMETER os_authent_prefix
1118
1119 NAME TYPE VALUE
1120 ------------------------------------ ----------- ------------------------------
1121 os_authent_prefix string ops$
1122
1123 3. Create an Oracle database user using the ``os_authent_prefix`` determined in
1124 step 2, and the operating system user name:
1125
1126 .. code-block:: sql
1127
1128 CREATE USER ops$oracle IDENTIFIED EXTERNALLY;
1129 GRANT CONNECT, RESOURCE TO ops$oracle;
1130
1131 In Python, connect using the following code::
1132
1133 connection = cx_Oracle.connect(dsn="mynetalias", encoding="UTF-8")
1134
1135 Your session user will be ``OPS$ORACLE``.
1136
1137 If your database is not on the same computer as python, you can perform testing
1138 by setting the database configuration parameter ``remote_os_authent=true``.
1139 Beware this is insecure.
1140
1141 See `Oracle Database Security Guide
1142 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
1143 id=GUID-37BECE32-58D5-43BF-A098-97936D66968F>`__ for more information about
1144 Operating System Authentication.
1145
1146 Privileged Connections
1147 ======================
1148
1149 The ``mode`` parameter of the function :meth:`cx_Oracle.connect()` specifies
1150 the database privilege that you want to associate with the user.
1151
1152 The example below shows how to connect to Oracle Database as SYSDBA:
1153
1154 .. code-block:: python
1155
1156 connection = cx_Oracle.connect("sys", syspwd, "dbhost.example.com/orclpdb1",
1157 mode=cx_Oracle.SYSDBA, encoding="UTF-8")
1158
1159 cursor = con.cursor()
1160 sql = "GRANT SYSOPER TO hr"
1161 cursor.execute(sql)
1162
1163 This is equivalent to executing the following in SQL*Plus:
1164
1165 .. code-block:: sql
1166
1167 CONNECT sys/syspwd AS SYSDBA
1168
1169 GRANT SYSOPER TO hr;
1170
1171 .. _netencrypt:
1172
1173 Securely Encrypting Network Traffic to Oracle Database
1174 ======================================================
1175
1176 You can encrypt data transferred between the Oracle Database and the Oracle
1177 Client libraries used by cx_Oracle so that unauthorized parties are not able to
1178 view plain text values as the data passes over the network. The easiest
1179 configuration is Oracle’s native network encryption. The standard SSL protocol
1180 can also be used if you have a PKI, but setup is necessarily more involved.
1181
1182 With native network encryption, the client and database server negotiate a key
1183 using Diffie-Hellman key exchange. This provides protection against
1184 man-in-the-middle attacks.
1185
1186 Native network encryption can be configured by editing Oracle Net’s optional
1187 :ref:`sqlnet.ora <optnetfiles>` configuration file, on either the database
1188 server and/or on each cx_Oracle 'client' machine. Parameters control whether
1189 data integrity checking and encryption is required or just allowed, and which
1190 algorithms the client and server should consider for use.
1191
1192 As an example, to ensure all connections to the database are checked for
1193 integrity and are also encrypted, create or edit the Oracle Database
1194 ``$ORACLE_HOME/network/admin/sqlnet.ora`` file. Set the checksum negotiation
1195 to always validate a checksum and set the checksum type to your desired value.
1196 The network encryption settings can similarly be set. For example, to use the
1197 SHA512 checksum and AES256 encryption use::
1198
1199 SQLNET.CRYPTO_CHECKSUM_SERVER = required
1200 SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA512)
1201 SQLNET.ENCRYPTION_SERVER = required
1202 SQLNET.ENCRYPTION_TYPES_SERVER = (AES256)
1203
1204 If you definitely know that the database server enforces integrity and
1205 encryption, then you do not need to configure cx_Oracle separately. However
1206 you can also, or alternatively, do so depending on your business needs. Create
1207 a ``sqlnet.ora`` on your client machine and locate it with other
1208 :ref:`optnetfiles`::
1209
1210 SQLNET.CRYPTO_CHECKSUM_CLIENT = required
1211 SQLNET.CRYPTO_CHECKSUM_TYPES_CLIENT = (SHA512)
1212 SQLNET.ENCRYPTION_CLIENT = required
1213 SQLNET.ENCRYPTION_TYPES_CLIENT = (AES256)
1214
1215 The client and server sides can negotiate the protocols used if the settings
1216 indicate more than one value is accepted.
1217
1218 Note that these are example settings only. You must review your security
1219 requirements and read the documentation for your Oracle version. In particular
1220 review the available algorithms for security and performance.
1221
1222 The ``NETWORK_SERVICE_BANNER`` column of the database view
1223 `V$SESSION_CONNECT_INFO
1224 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
1225 id=GUID-9F0DCAEA-A67E-4183-89E7-B1555DC591CE>`__ can be used to verify the
1226 encryption status of a connection.
1227
1228 For more information on Oracle Data Network Encryption and Integrity,
1229 configuring SSL network encryption and Transparent Data Encryption of
1230 data-at-rest in the database, see `Oracle Database Security Guide
1231 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
1232 id=GUID-41040F53-D7A6-48FA-A92A-0C23118BC8A0>`__.
1233
1234
1235 Resetting Passwords
1236 ===================
1237
1238 After connecting, passwords can be changed by calling
1239 :meth:`Connection.changepassword()`:
1240
1241 .. code-block:: python
1242
1243 # Get the passwords from somewhere, such as prompting the user
1244 oldpwd = getpass.getpass("Old Password for %s: " % username)
1245 newpwd = getpass.getpass("New Password for %s: " % username)
1246
1247 connection.changepassword(oldpwd, newpwd)
1248
1249 When a password has expired and you cannot connect directly, you can connect
1250 and change the password in one operation by using the ``newpassword`` parameter
1251 of the function :meth:`cx_Oracle.connect()` constructor:
1252
1253 .. code-block:: python
1254
1255 # Get the passwords from somewhere, such as prompting the user
1256 oldpwd = getpass.getpass("Old Password for %s: " % username)
1257 newpwd = getpass.getpass("New Password for %s: " % username)
1258
1259 connection = cx_Oracle.connect(username, oldpwd, "dbhost.example.com/orclpdb1",
1260 newpassword=newpwd, encoding="UTF-8")
1261
1262 .. _autononmousdb:
1263
1264 Connecting to Autononmous Databases
1265 ===================================
1266
1267 To enable connection to Oracle Autonomous Database in Oracle Cloud, a wallet
1268 needs be downloaded from the cloud GUI, and cx_Oracle needs to be configured to
1269 use it. A database username and password is still required. The wallet only
1270 enables SSL/TLS.
1271
1272 Install the Wallet and Network Configuration Files
1273 --------------------------------------------------
1274
1275 From the Oracle Cloud console for the database, download the wallet zip file. It
1276 contains the wallet and network configuration files. Note: keep wallet files in
1277 a secure location and share them only with authorized users.
1278
1279 Unzip the wallet zip file.
1280
1281 For cx_Oracle, only these files from the zip are needed:
1282
1283 - ``tnsnames.ora`` - Maps net service names used for application connection strings to your database services
1284 - ``sqlnet.ora`` - Configures Oracle Network settings
1285 - ``cwallet.sso`` - Enables SSL/TLS connections
1286
1287 The other files and the wallet password are not needed.
1288
1289 Place these files as shown in :ref:`Optional Oracle Net Configuration Files <optnetfiles>`.
1290
1291 Run Your Application
1292 --------------------
1293
1294 The ``tnsnames.ora`` file contains net service names for various levels of
1295 database service. For example, if you create a database called CJDB1 with the
1296 Always Free services from the `Oracle Cloud Free Tier
1297 <https://www.oracle.com//cloud/free/>`__, then you might decide to use the
1298 connection string in ``tnsnames.ora`` called ``cjdb1_high``.
1299
1300 Update your application to use your schema username, its database password, and
1301 a net service name, for example:
1302
1303 .. code-block:: python
1304
1305 connection = cx_Oracle.connect("scott", userpwd, "cjdb1_high", encoding="UTF-8")
1306
1307 Once you have set Oracle environment variables required by your application,
1308 such as ``TNS_ADMIN``, you can start your application.
1309
1310 If you need to create a new database schema so you do not login as the
1311 privileged ADMIN user, refer to the relevant Oracle Cloud documentation, for
1312 example see `Create Database Users
1313 <https://docs.oracle.com/en/cloud/paas/atp-cloud/atpud/manage.html>`__ in the
1314 Oracle Autonomous Transaction Processing Dedicated Deployments manual.
1315
1316 Access Through a Proxy
1317 ----------------------
1318
1319 If you are behind a firewall, you can tunnel TLS/SSL connections via a proxy
1320 using `HTTPS_PROXY
1321 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-C672E92D-CE32-4759-9931-92D7960850F7>`__
1322 in the connect descriptor. Successful connection depends on specific proxy
1323 configurations. Oracle does not recommend doing this when performance is
1324 critical.
1325
1326 Edit ``sqlnet.ora`` and add a line:
1327
1328 SQLNET.USE_HTTPS_PROXY=on
1329
1330 Edit ``tnsnames.ora`` and add an ``HTTPS_PROXY`` proxy name and
1331 ``HTTPS_PROXY_PORT`` port to the connect descriptor address list of any service
1332 name you plan to use, for example:
1333
1334
1335 cjdb1_high = (description=
1336 (address=
1337 (https_proxy=myproxy.example.com)(https_proxy_port=80)
1338 (protocol=tcps)(port=1522)(host= . . . )
1339
1340 .. _connsharding:
1341
1342 Connecting to Sharded Databases
1343 ===============================
1344
1345 `Oracle Sharding
1346 <https://www.oracle.com/database/technologies/high-availability/sharding.html>`__
1347 can be used to horizontally partition data across independent databases. A
1348 database table can be split so each shard contains a table with the same columns
1349 but a different subset of rows. These tables are known as sharded tables.
1350 Sharding is configured in Oracle Database, see the `Oracle Sharding
1351 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=SHARD>`__ manual.
1352 Sharding requires Oracle Database and Oracle Client libraries 12.2, or later.
1353
1354 The :meth:`cx_Oracle.connect()` and :meth:`SessionPool.acquire()` functions
1355 accept ``shardingkey`` and ``supershardingkey`` parameters that are a sequence
1356 of values used to route the connection directly to a given shard. A sharding
1357 key is always required. A super sharding key is additionally required when
1358 using composite sharding, which is when data has been partitioned by a list or
1359 range (the super sharding key), and then further partitioned by a sharding key.
1360
1361 When creating a connection pool, the :meth:`cx_Oracle.SessionPool()` attribute
1362 ``maxSessionsPerShard`` can be set. This is used to balance connections in the
1363 pool equally across shards. It requires Oracle Client libraries 18.3, or later.
1364
1365 Shard key values may be of type string (mapping to VARCHAR2 shard keys), number
1366 (NUMBER), bytes (RAW), or date (DATE). Multiple types may be used in each
1367 array. Sharding keys of TIMESTAMP type are not supported.
1368
1369 When connected to a shard, queries will only return data from that shard. For
1370 queries that need to access data from multiple shards, connections can be
1371 established to the coordinator shard catalog database. In this case, no shard
1372 key or super shard key is used.
1373
1374 As an example of direct connection, if sharding had been configured on a single
1375 VARCHAR2 column like:
1376
1377 .. code-block:: sql
1378
1379 CREATE SHARDED TABLE customers (
1380 cust_id NUMBER,
1381 cust_name VARCHAR2(30),
1382 class VARCHAR2(10) NOT NULL,
1383 signup_date DATE,
1384 cust_code RAW(20),
1385 CONSTRAINT cust_name_pk PRIMARY KEY(cust_name))
1386 PARTITION BY CONSISTENT HASH (cust_name)
1387 PARTITIONS AUTO TABLESPACE SET ts1;
1388
1389 then direct connection to a shard can be made by passing a single sharding key:
1390
1391 .. code-block:: python
1392
1393 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
1394 encoding="UTF-8", shardingkey=["SCOTT"])
1395
1396 Numbers keys can be used in a similar way:
1397
1398 .. code-block:: python
1399
1400 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
1401 encoding="UTF-8", shardingkey=[110])
1402
1403 When sharding by DATE, you can connect like:
1404
1405 .. code-block:: python
1406
1407 import datetime
1408
1409 d = datetime.datetime(2014, 7, 3)
1410
1411 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
1412 encoding="UTF-8", shardingkey=[d])
1413
1414 When sharding by RAW, you can connect like:
1415
1416 .. code-block:: python
1417
1418 b = b'\x01\x04\x08';
1419
1420 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
1421 encoding="UTF-8", shardingkey=[b])
1422
1423 Multiple keys can be specified, for example:
1424
1425 .. code-block:: python
1426
1427 keyArray = [70, "SCOTT", "gold", b'\x00\x01\x02']
1428
1429 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
1430 encoding="UTF-8", shardingkey=keyArray)
1431
1432 A super sharding key example is:
1433
1434 .. code-block:: python
1435
1436 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
1437 encoding="UTF-8", supershardingkey=["goldclass"], shardingkey=["SCOTT"])
0 .. _cqn:
1
2 ***********************************
3 Continuous Query Notification (CQN)
4 ***********************************
5
6 `Continuous Query Notification (CQN)
7 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
8 id=GUID-373BAF72-3E63-42FE-8BEA-8A2AEFBF1C35>`__ allows applications to receive
9 notifications when a table changes, such as when rows have been updated,
10 regardless of the user or the application that made the change. This can be
11 useful in many circumstances, such as near real-time monitoring, auditing
12 applications, or for such purposes as mid-tier cache invalidation. A cache
13 might hold some values that depend on data in a table. If the data in the
14 table changes, the cached values must then be updated with the new information.
15
16 CQN notification behavior is widely configurable. Choices include specifying
17 what types of SQL should trigger a notification, whether notifications should
18 survive database loss, and control over unsubscription. You can also choose
19 whether notification messages will include ROWIDs of affected rows.
20
21 By default, object-level (previously known as Database Change Notification)
22 occurs and the Python notification method is invoked whenever a database
23 transaction is committed that changes an object that a registered query
24 references, regardless of whether the actual query result changed. However if
25 the :meth:`subscription <Connection.subscribe>` option ``qos`` is
26 :data:`cx_Oracle.SUBSCR_QOS_QUERY` then query-level notification occurs. In
27 this mode, the database notifies the application whenever a transaction changing
28 the result of the registered query is committed.
29
30 CQN is best used to track infrequent data changes.
31
32
33 Requirements
34 ============
35
36 Before using CQN, users must have appropriate permissions:
37
38 .. code-block:: sql
39
40 GRANT CHANGE NOTIFICATION TO <user-name>;
41
42 To use CQN, connections must have ``events`` mode set to ``True``, for
43 example:
44
45 .. code-block:: python
46
47 connection = cx_Oracle.connect(userName, password, "dbhost.example.com/orclpdb1", events=True)
48
49 The default CQN connection mode means the database must be able to connect back
50 to the application using cx_Oracle in order to receive notification events.
51 Alternatively, when using Oracle Database and Oracle client libraries 19.4, or
52 later, subscriptions can set the optional ``clientInitiated`` parameter to
53 ``True``, see ``Connection.subscribe()`` below.
54
55 The default CQN connection mode typically means that the machine running
56 cx_Oracle needs a fixed IP address. Note :meth:`Connection.subscribe()` does
57 not verify that this reverse connection is possible. If there is any problem
58 sending a notification, then the callback method will not be invoked.
59 Configuration options can include an IP address and port on which cx_Oracle will
60 listen for notifications; otherwise, the database chooses values.
61
62
63 Creating a Subscription
64 =======================
65
66 Subscriptions allow Python to receives notifications for events that take place
67 in the database that match the given parameters.
68
69 For example, a basic CQN subscription might be created like:
70
71 .. code-block:: python
72
73 connection.subscribe(namespace=cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE,
74 callback=MyCallback)
75
76 See :meth:`Connection.subscribe()` for details on all of the parameters.
77
78 See :ref:`cqn-operation-codes` for the types of operations that are supported.
79
80 See :ref:`subscr-qos` for the quality of service values that are supported.
81
82 See :ref:`subscr-namespaces` and :ref:`subscr-protocols` for the namespaces and
83 protocols that are supported.
84
85 See :ref:`subscrobj` for more details on the subscription object that is
86 created.
87
88 When using Oracle Database and Oracle client libraries 19.4, or later, the
89 optional subscription parameter ``clientInitiated`` can be set:
90
91 .. code-block:: python
92
93 connection.subscribe(namespace= . . ., callback=MyCallback, clientInitiated=True)
94
95 This enables CQN "client initiated" connections which internally use the same
96 approach as normal cx_Oracle connections to the database, and do not require the
97 database to be able to connect back to the application. Since client initiated
98 connections do not need special network configuration they have ease-of-use and
99 security advantages.
100
101
102 Registering Queries
103 ===================
104
105 Once a subscription has been created, one or more queries must be registered by
106 calling :meth:`Subscription.registerquery()`. Registering a query behaves
107 similarly to :meth:`Cursor.execute()`, but only queries are permitted and the
108 ``args`` parameter must be a sequence or dictionary.
109
110 An example script to receive query notifications when the 'CUSTOMER' table data
111 changes is:
112
113 .. code-block:: python
114
115 def CQNCallback(message):
116 print("Notification:")
117 for query in message.queries:
118 for tab in query.tables:
119 print("Table:", tab.name)
120 print("Operation:", tab.operation)
121 for row in tab.rows:
122 if row.operation & cx_Oracle.OPCODE_INSERT:
123 print("INSERT of rowid:", row.rowid)
124 if row.operation & cx_Oracle.OPCODE_DELETE:
125 print("DELETE of rowid:", row.rowid)
126
127 subscr = connection.subscribe(namespace=cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE,
128 callback=CQNCallback,
129 operations=cx_Oracle.OPCODE_INSERT | cx_Oracle.OPCODE_DELETE,
130 qos = cx_Oracle.SUBSCR_QOS_QUERY | cx_Oracle.SUBSCR_QOS_ROWIDS)
131 subscr.registerquery("select * from regions")
132 input("Hit enter to stop CQN demo\n")
133
134 Running the above script, shows the initial output as::
135
136 Hit enter to stop CQN demo
137
138 Use SQL*Plus or another tool to commit a change to the table:
139
140 .. code-block:: sql
141
142 insert into regions values(120, 'L');
143 commit;
144
145 When the commit is executed, a notification will be received by the callback
146 which should print something like the following::
147
148 Hit enter to stop CQN demo
149 Notification:
150 Table: HR.REGIONS
151 Operation: 2
152 INSERT of rowid: AAA7EsAAHAAAFS/AAA
153
154 See `GitHub Samples
155 <https://github.com/oracle/python-cx_Oracle/blob/master/samples/CQN.py>`__
156 for a runnable CQN example.
0 .. _exception:
1
2 ******************
3 Exception Handling
4 ******************
5
6 All exceptions raised by cx_Oracle are inherited from :attr:`cx_Oracle.Error`.
7 See :ref:`Exceptions <exceptions>` for more details on the various exceptions
8 defined by cx_Oracle. See the exception handling section in the
9 :ref:`API manual <exchandling>` for more details on the information available
10 when an exception is raised.
11
12 Applications can catch exceptions as needed. For example, when trying to add a
13 customer that already exists in the database, the following could could be used
14 to catch the exception:
15
16 .. code-block:: python
17
18 try:
19 cursor.execute("insert into customer values (101, 'Customer A')")
20 except cx_Oracle.IntegrityError:
21 print("Customer ID already exists")
22 else:
23 print("Customer added")
24
25
26 If information about the exception needs to be processed instead, the following
27 code can be used:
28
29 .. code-block:: python
30
31 try:
32 cursor.execute("insert into customer values (101, 'Customer A')")
33 except cx_Oracle.IntegrityError as e:
34 errorObj, = e.args
35 print("Customer ID already exists")
36 print("Error Code:", errorObj.code)
37 print("Error Message:", errorObj.message)
38 else:
39 print("Customer added")
0 .. _globalization:
1
2 ********************************
3 Character Sets and Globalization
4 ********************************
5
6 Data fetched from, and sent to, Oracle Database will be mapped between the
7 database character set and the "Oracle client" character set of the Oracle
8 Client libraries used by cx_Oracle. If data cannot be correctly mapped between
9 client and server character sets, then it may be corrupted or queries may fail
10 with :ref:`"codec can't decode byte" <codecerror>`.
11
12 cx_Oracle uses Oracle’s National Language Support (NLS) to assist in
13 globalizing applications. As well as character set support, there are many
14 other features that will be useful in applications. See the
15 `Database Globalization Support Guide
16 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=NLSPG>`__.
17
18
19 Setting the Client Character Set
20 ================================
21
22 In cx_Oracle 8 the default encoding used for all character data changed to
23 "UTF-8". This universal encoding is suitable for most applications. If you
24 have a special need, you can pass the ``encoding`` and ``nencoding`` parameters
25 to the :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool` methods to
26 specify different Oracle Client character sets. For example:
27
28 .. code-block:: python
29
30 import cx_Oracle
31 connection = cx_Oracle.connect(connectString, encoding="US-ASCII",
32 nencoding="UTF-8")
33
34 The ``encoding`` parameter affects character data such as VARCHAR2 and CLOB
35 columns. The ``nencoding`` parameter affects "National Character" data such as
36 NVARCHAR2 and NCLOB. If you are not using national character types, then you
37 can omit ``nencoding``. Both the ``encoding`` and ``nencoding`` parameters are
38 expected to be one of the `Python standard encodings
39 <https://docs.python.org/3/library/codecs.html#standard-encodings>`__ such as
40 ``UTF-8``. Do not accidentally use ``UTF8``, which Oracle uses to specify the
41 older Unicode 3.0 Universal character set, ``CESU-8``. Note that Oracle does
42 not recognize all of the encodings that Python recognizes. You can see which
43 encodings are usable in cx_Oracle by issuing this query:
44
45 .. code-block:: sql
46
47 select distinct utl_i18n.map_charset(value)
48 from v$nls_valid_values
49 where parameter = 'CHARACTERSET'
50 and utl_i18n.map_charset(value) is not null
51 order by 1
52
53 .. note::
54
55 From cx_Oracle 8, it is no longer possible to change the character set
56 using the ``NLS_LANG`` environment variable. The character set component
57 of that variable is ignored. The language and territory components of
58 ``NLS_LANG`` are still respected by the Oracle Client libraries.
59
60 Character Set Example
61 ---------------------
62
63 The script below tries to display data containing a Euro symbol from the
64 database.
65
66 .. code-block:: python
67
68 connection = cx_Oracle.connect(userName, password, "dbhost.example.com/orclpdb1",
69 encoding="US-ASCII")
70 cursor = connection.cursor()
71 for row in cursor.execute("select nvarchar2_column from nchar_test"):
72 print(row)
73
74 Because the '€' symbol is not supported by the ``US-ASCII`` character set, all
75 '€' characters are replaced by '¿' in the cx_Oracle output::
76
77 ('¿',)
78
79 When the ``encoding`` parameter is removed (or set to "UTF-8") during connection:
80
81 .. code-block:: python
82
83 connection = cx_Oracle.connect(userName, password, "dbhost.example.com/orclpdb1")
84
85 Then the output displays the Euro symbol as desired::
86
87 ('€',)
88
89 .. _findingcharset:
90
91 Finding the Database and Client Character Set
92 ---------------------------------------------
93
94 To find the database character set, execute the query:
95
96 .. code-block:: sql
97
98 SELECT value AS db_charset
99 FROM nls_database_parameters
100 WHERE parameter = 'NLS_CHARACTERSET';
101
102 To find the database 'national character set' used for NCHAR and related types,
103 execute the query:
104
105 .. code-block:: sql
106
107 SELECT value AS db_ncharset
108 FROM nls_database_parameters
109 WHERE parameter = 'NLS_NCHAR_CHARACTERSET';
110
111 To find the current "client" character set used by cx_Oracle, execute the
112 query:
113
114 .. code-block:: sql
115
116 SELECT DISTINCT client_charset AS client_charset
117 FROM v$session_connect_info
118 WHERE sid = SYS_CONTEXT('USERENV', 'SID');
119
120 If these character sets do not match, characters transferred over Oracle Net
121 will be mapped from one character set to another. This may impact performance
122 and may result in invalid data.
123
124 Setting the Oracle Client Locale
125 ================================
126
127 You can use the ``NLS_LANG`` environment variable to set the language and
128 territory used by the Oracle Client libraries. For example, on Linux you could
129 set::
130
131 export NLS_LANG=JAPANESE_JAPAN
132
133 The language ("JAPANESE" in this example) specifies conventions such as the
134 language used for Oracle Database messages, sorting, day names, and month
135 names. The territory ("JAPAN") specifies conventions such as the default date,
136 monetary, and numeric formats. If the language is not specified, then the value
137 defaults to AMERICAN. If the territory is not specified, then the value is
138 derived from the language value. See `Choosing a Locale with the NLS_LANG
139 Environment Variable
140 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-86A29834-AE29-4BA5-8A78-E19C168B690A>`__
141
142 If the ``NLS_LANG`` environment variable is set in the application with
143 ``os.environ['NLS_LANG']``, it must be set before any connection pool is
144 created, or before any standalone connections are created.
145
146 Other Oracle globalization variables, such as ``NLS_DATE_FORMAT`` can also be
147 set to change the behavior of cx_Oracle, see `Setting NLS Parameters
148 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
149 id=GUID-6475CA50-6476-4559-AD87-35D431276B20>`__.
0 .. _highavailability:
1
2 ********************************
3 High Availability with cx_Oracle
4 ********************************
5
6 Applications can utilize many features for high availability (HA) during planned and
7 unplanned outages in order to:
8
9 * Reduce application downtime
10 * Eliminate compromises between high availability and performance
11 * Increase operational productivity
12
13 .. _harecommend:
14
15 General HA Recommendations
16 --------------------------
17
18 General recommendations for creating highly available cx_Oracle programs are:
19
20 * Tune operating system and Oracle Network parameters to avoid long TCP timeouts, to prevent firewalls killing connections, and to avoid connection storms.
21 * Implement application error handling and recovery.
22 * 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.
23 * 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).
24 * Utilize Oracle Database technologies such as `RAC <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=RACAD>`__ or standby databases.
25 * Configure database services to emit :ref:`FAN <fan>` events.
26 * 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.
27 * Test all scenarios thoroughly.
28
29 .. _hanetwork:
30
31 Network Configuration
32 ---------------------
33
34 The operating system TCP and :ref:`Oracle Net configuration <optnetfiles>`
35 should be configured for performance and availability.
36
37 Options such as `SQLNET.OUTBOUND_CONNECT_TIMEOUT
38 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-0857C817-675F-4CF0-BFBB-C3667F119176>`__,
39 `SQLNET.RECV_TIMEOUT
40 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-4A19D81A-75F0-448E-B271-24E5187B5909>`__
41 and `SQLNET.SEND_TIMEOUT
42 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-48547756-9C0B-4D14-BE85-E7ADDD1A3A66>`__
43 can be explored.
44
45 `Oracle Net Services
46 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=NETRF>`__ options may
47 also be useful for high availability and performance tuning. For example the
48 database's `listener.ora` file can have `RATE_LIMIT
49 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-F302BF91-64F2-4CE8-A3C7-9FDB5BA6DCF8>`__
50 and `QUEUESIZE
51 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FF87387C-1779-4CC3-932A-79BB01391C28>`__
52 parameters that can help handle connection storms.
53
54 With Oracle Client 19c, `EXPIRE_TIME
55 <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>`__
56 can be used in :ref:`tnsnames.ora <optnetfiles>` connect descriptors to prevent
57 firewalls from terminating idle connections and to adjust keepalive timeouts.
58 The general recommendation for ``EXPIRE_TIME`` is to use a value that is
59 slightly less than half of the termination period. In older versions of Oracle
60 Client, a ``tnsnames.ora`` connect descriptor option `ENABLE=BROKEN
61 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-7A18022A-E40D-4880-B3CE-7EE9864756CA>`_
62 can be used instead of ``EXPIRE_TIME``. These settings can also aid detection
63 of a terminated remote database server.
64
65 When cx_Oracle uses :ref:`Oracle Client libraries 19c <archfig>`, then the
66 :ref:`Easy Connect Plus syntax <easyconnect>` syntax enables some options to be
67 used without needing a ``sqlnet.ora`` file. For example, if your firewall times
68 out every 4 minutes, and you cannot alter the firewall settings, then you may
69 decide to use ``EXPIRE_TIME`` in your connect string to send a probe every 2
70 minutes to the database to keep connections 'alive'::
71
72 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1?expire_time=2")
73
74 .. _fan:
75
76 Fast Application Notification (FAN)
77 -----------------------------------
78
79 Users of `Oracle Database FAN
80 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-F3FBE48B-468B-4393-8B0C-D5C8E0E4374D>`__
81 must connect to a FAN-enabled database service. The application should have
82 ``events`` set to True when connecting. This value can also be changed via
83 :ref:`Oracle Client Configuration <optclientfiles>`.
84
85 FAN support is useful for planned and unplanned outages. It provides immediate
86 notification to cx_Oracle following outages related to the database, computers,
87 and networks. Without FAN, cx_Oracle can hang until a TCP timeout occurs and an
88 error is returned, which might be several minutes.
89
90 FAN allows cx_Oracle to provide high availability features without the
91 application being aware of an outage. Unused, idle connections in a
92 :ref:`connection pool <connpool>` will be automatically cleaned up. A future
93 :meth:`SessionPool.acquire()` call will establish a fresh connection to a
94 surviving database instance without the application being aware of any service
95 disruption.
96
97 To handle errors that affect active connections, you can add application logic
98 to re-connect (this will connect to a surviving database instance) and replay
99 application logic without having to return an error to the application user.
100
101 FAN benefits users of Oracle Database's clustering technology `Oracle RAC
102 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D04AA2A7-2E68-4C5C-BD6E-36C62427B98E>`__
103 because connections to surviving database instances can be immediately made.
104 Users of Oracle's Data Guard with a broker will get FAN events generated when
105 the standby database goes online. Standalone databases will send FAN events
106 when the database restarts.
107
108 For a more information on FAN see the `white paper on Fast Application
109 Notification
110 <http://www.oracle.com/technetwork/database/options/clustering/applicationcontinuity/learnmore/fastapplicationnotification12c-2538999.pdf>`__.
111
112 .. _appcont:
113
114 Application Continuity (AC)
115 ---------------------------
116
117 Oracle Application Continuity and Transparent Application Continuity are Oracle
118 Database technologies that record application interaction with the database and,
119 in the event of a database instance outage, attempt to replay the interaction on
120 a surviving database instance. If successful, users will be unaware of any
121 database issue. AC and TAC are best suited for OLTP applications.
122
123 When AC or TAC are configured on the database service, they are transparently
124 available to cx_Oracle applications.
125
126 You must thoroughly test your application because not all lower level calls in
127 the cx_Oracle implementation can be replayed.
128
129 See `OCI and Application Continuity
130 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A8DD9422-2F82-42A9-9555-134296416E8F>`__
131 for more information.
132
133 .. _tg:
134
135 Transaction Guard
136 -----------------
137
138 cx_Oracle supports `Transaction Guard
139 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
140 id=GUID-A675AF7B-6FF0-460D-A6E6-C15E7C328C8F>`__ which enables Python
141 application to verify the success or failure of the last transaction in the
142 event of an unplanned outage. This feature is available when both client and
143 database are 12.1 or higher.
144
145 Using Transaction Guard helps to:
146
147 * Preserve the commit outcome
148 * Ensure a known outcome for every transaction
149
150 See `Oracle Database Development Guide
151 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
152 id=GUID-6C5880E5-C45F-4858-A069-A28BB25FD1DB>`__ for more information about
153 using Transaction Guard.
154
155 When an error occurs during commit, the Python application can acquire the
156 logical transaction id (``ltxid``) from the connection and then call a
157 procedure to determine the outcome of the commit for this logical transaction
158 id.
159
160 Follow the steps below to use the Transaction Guard feature in Python:
161
162 1. Grant execute privileges to the database users who will be checking the
163 outcome of the commit. Login as SYSDBA and run the following command:
164
165 .. code-block:: sql
166
167 GRANT EXECUTE ON DBMS_APP_CONT TO <username>;
168
169 2. Create a new service by executing the following PL/SQL block as SYSDBA.
170 Replace the ``<service-name>``, ``<network-name>`` and
171 ``<retention-value>`` values with suitable values. It is important that the
172 ``COMMIT_OUTCOME`` parameter be set to true for Transaction Guard to
173 function properly.
174
175 .. code-block:: sql
176
177 DECLARE
178 t_Params dbms_service.svc_parameter_array;
179 BEGIN
180 t_Params('COMMIT_OUTCOME') := 'true';
181 t_Params('RETENTION_TIMEOUT') := <retention-value>;
182 DBMS_SERVICE.CREATE_SERVICE('<service-name>', '<network-name>', t_Params);
183 END;
184 /
185
186 3. Start the service by executing the following PL/SQL block as SYSDBA:
187
188 .. code-block:: sql
189
190 BEGIN
191 DBMS_SERVICE.start_service('<service-name>');
192 END;
193 /
194
195 Ensure the service is running by examining the output of the following query:
196
197 .. code-block:: sql
198
199 SELECT name, network_name FROM V$ACTIVE_SERVICES ORDER BY 1;
200
201
202 **Python Application code requirements to use Transaction Guard**
203
204 In the Python application code:
205
206 * Use the connection attribute :attr:`~Connection.ltxid` to determine the
207 logical transaction id.
208 * Call the ``DBMS_APP_CONT.GET_LTXID_OUTCOME`` PL/SQL procedure with the
209 logical transaction id acquired from the connection attribute. This returns
210 a boolean value indicating if the last transaction was committed and whether
211 the last call was completed successfully or not.
212
213 See the `Transaction Guard Sample
214 <https://github.com/oracle/python-cx_Oracle/blob/master/
215 samples/TransactionGuard.py>`__ for further details.
0 .. _initialization:
1
2 **************************
3 cx_Oracle 8 Initialization
4 **************************
5
6 The cx_Oracle module loads Oracle Client libraries which communicate over
7 Oracle Net to an existing database. The Oracle Client libraries need to be
8 installed separately. See :ref:`installation`. Oracle Net is not a separate
9 product: it is how the Oracle Client and Oracle Database communicate.
10
11 .. figure:: /images/cx_Oracle_arch.png
12
13 cx_Oracle Architecture
14
15 .. _libinit:
16
17 Locating the Oracle Client Libraries
18 ====================================
19
20 cx_Oracle dynamically loads the Oracle Client libraries using a search
21 heuristic. Only the first set of libraries found are loaded. The libraries
22 can be in an installation of Oracle Instant Client, in a full Oracle Client
23 installation, or in an Oracle Database installation (if Python is running on
24 the same machine as the database). The versions of Oracle Client and Oracle
25 Database do not have to be the same. For certified configurations see Oracle
26 Support's `Doc ID 207303.1
27 <https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__.
28
29 cx_Oracle looks for the Oracle Client libraries as follows:
30
31 * On Windows:
32
33 - In the ``lib_dir`` directory specified in a call to
34 :meth:`cx_Oracle.init_oracle_client()`. This directory should contain
35 the libraries from an unzipped Instant Client 'Basic' or 'Basic Light'
36 package. If you pass the library directory from a full client or
37 database installation, such as Oracle Database "XE" Express Edition, then
38 you will need to have previously set your environment to use that
39 software installation, otherwise files such as message files will not be
40 located. On Windows when the path contains backslashes, use a 'raw'
41 string like ``lib_dir = r"C:\instantclient_19_6"``. If the Oracle Client
42 libraries cannot be loaded from ``lib_dir``, then an exception is raised.
43
44 - If ``lib_dir`` was not specified, then Oracle Client libraries are looked
45 for in the directory where the cx_Oracle binary module is installed.
46 This directory should contain the libraries from an unzipped Instant
47 Client 'Basic' or 'Basic Light' package. If the libraries are not found,
48 no exception is raised and the search continues, see next bullet point.
49
50 - In the directories on the system library search path, e.g. the ``PATH``
51 environment variable. If the Oracle Client libraries cannot be loaded,
52 then an exception is raised.
53
54 * On macOS:
55
56 - In the ``lib_dir`` directory specified in a call to
57 :meth:`cx_Oracle.init_oracle_client()`. This directory should contain
58 the libraries from an unzipped Instant Client 'Basic' or 'Basic Light'
59 package. If the Oracle Client libraries cannot be loaded from
60 ``lib_dir``, then an exception is raised.
61
62 - If ``lib_dir`` was not specified, then Oracle Client libraries are looked
63 for in the directory where the cx_Oracle binary module is. This directory
64 should contain the libraries from an unzipped Instant Client 'Basic' or
65 'Basic Light' package. For example if
66 ``/Users/your_username/Library/Python/3.8/lib/python/site-packages``
67 contains ``cx_Oracle.cpython-38-darwin.so``, then you could run ``ln -s
68 ~/instantclient_19_3/libclntsh.dylib
69 ~/Library/Python/3.8/lib/python/site-packages``. If the libraries are not
70 found, no exception is raised and the search continues, see next bullet
71 point.
72
73 - In the directories on the system library search path, e.g. ``~/lib/`` and
74 ``/usr/local/lib``, or in ``$DYLD_LIBRARY_PATH``. These paths will vary
75 with macOS version and Python version. Any value in
76 ``DYLD_LIBRARY_PATH`` will not propagate to a sub-shell. If the Oracle
77 Client libraries cannot be loaded, then an exception is raised.
78
79 * On Linux and related platforms:
80
81 - In the ``lib_dir`` directory specified in a call to
82 :meth:`cx_Oracle.init_oracle_client()`. Note on Linux this is only
83 useful to force immediate loading of the libraries because the libraries
84 must also be in the system library search path. This directory should
85 contain the libraries from an unzipped Instant Client 'Basic' or 'Basic
86 Light' package. If you pass the library directory from a full client or
87 database installation, such as Oracle Database "XE" Express Edition then
88 you will need to have previously set the ``ORACLE_HOME`` environment
89 variable. If the Oracle Client libraries cannot be loaded from
90 ``lib_dir``, then an exception is raised.
91
92 - If ``lib_dir`` was not specified, then Oracle Client libraries are looked
93 for in the operating system library search path, such as configured with
94 ``ldconfig`` or set in the environment variable ``LD_LIBRARY_PATH``. On
95 some UNIX platforms an OS specific equivalent, such as ``LIBPATH`` or
96 ``SHLIB_PATH`` is used instead of ``LD_LIBRARY_PATH``. If the libraries
97 are not found, no exception is raised and the search continues, see next
98 bullet point.
99
100 - In ``$ORACLE_HOME/lib``. Note the environment variable ``ORACLE_HOME``
101 should only ever be set when you have a full database installation or
102 full client installation. It should not be set if you are using Oracle
103 Instant Client. The ``ORACLE_HOME`` variable, and other necessary
104 variables, should be set before starting Python. See :ref:`envset`. If
105 the Oracle Client libraries cannot be loaded, then an exception is
106 raised.
107
108 If you call :meth:`cx_Oracle.init_oracle_client()` with a ``lib_dir``
109 parameter, the Oracle Client libraries are loaded immediately from that
110 directory. If you call :meth:`cx_Oracle.init_oracle_client()` but do *not* set
111 the ``lib_dir`` parameter, the Oracle Client libraries are loaded immediately
112 using the search heuristic above. If you do not call
113 :meth:`cx_Oracle.init_oracle_client()`, then the libraries are loaded using the
114 search heuristic when the first cx_Oracle function that depends on the
115 libraries is called, for example when a connection pool is created. If there
116 is a problem loading the libraries, then an exception is raised.
117
118 Make sure the Python process has directory and file access permissions for the
119 Oracle Client libraries. On Linux ensure a ``libclntsh.so`` file exists. On
120 macOS ensure a ``libclntsh.dylib`` file exists. cx_Oracle will not directly
121 load ``libclntsh.*.XX.1`` files in ``lib_dir`` or from the directory where the
122 cx_Oracle binary module is. Note other libraries used by ``libclntsh*`` are
123 also required.
124
125 To trace the loading of Oracle Client libraries, the environment variable
126 ``DPI_DEBUG_LEVEL`` can be set to 64 before starting Python. For example, on
127 Linux, you might use::
128
129 $ export DPI_DEBUG_LEVEL=64
130 $ python myapp.py 2> log.txt
131
132
133 .. _usinginitoracleclient:
134
135 Using cx_Oracle.init_oracle_client() to set the Oracle Client directory
136 -----------------------------------------------------------------------
137
138 Applications can call the function :meth:`cx_Oracle.init_oracle_client()` to
139 specify the directory containing Oracle Instant Client libraries. The Oracle
140 Client Libraries are loaded when ``init_oracle_client()`` is called. For
141 example, if the Oracle Instant Client Libraries are in
142 ``C:\oracle\instantclient_19_9`` on Windows or
143 ``$HOME/Downloads/instantclient_19_8`` on macOS, then you can use:
144
145 .. code-block:: python
146
147 import cx_Oracle
148 import sys
149 import os
150
151 try:
152 if sys.platform.startswith("darwin"):
153 lib_dir = os.path.join(os.environ.get("HOME"), "Downloads",
154 "instantclient_19_8")
155 cx_Oracle.init_oracle_client(lib_dir=lib_dir)
156 elif sys.platform.startswith("win32"):
157 cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_9")
158 except Exception as err:
159 print("Whoops!")
160 print(err);
161 sys.exit(1);
162
163 Note the use of a 'raw' string ``r"..."`` on Windows so that backslashes are
164 treated as directory separators.
165
166 The :meth:`~cx_Oracle.init_oracle_client()` function can only be called once.
167
168 If you set ``lib_dir`` on Linux and related platforms, you must still have
169 configured the system library search path to include that directory before
170 starting Python.
171
172 On any operating system, if you set ``lib_dir`` to the library directory of a
173 full database or full client installation, you will need to have previously set
174 the Oracle environment, for example by setting the ``ORACLE_HOME`` environment
175 variable. Otherwise you will get errors like ORA-1804. You should set this,
176 and other Oracle environment variables, before starting Python, as
177 shown in :ref:`envset`.
178
179 .. _optnetfiles:
180
181 Optional Oracle Net Configuration Files
182 =======================================
183
184 Optional Oracle Net configuration files are read when cx_Oracle is loaded.
185 These files affect connections and applications. The common files are:
186
187 * ``tnsnames.ora``: A configuration file that defines databases addresses
188 for establishing connections. See :ref:`Net Service Name for Connection
189 Strings <netservice>`.
190
191 * ``sqlnet.ora``: A profile configuration file that may contain information
192 on features such as connection failover, network encryption, logging, and
193 tracing. See `Oracle Net Services Reference
194 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
195 id=GUID-19423B71-3F6C-430F-84CC-18145CC2A818>`__ for more information.
196
197 The files should be in a directory accessible to Python, not on the database
198 server host.
199
200 For example, if the file ``/etc/my-oracle-config/tnsnames.ora`` should be used,
201 you can call :meth:`cx_Oracle.init_oracle_client()`:
202
203 .. code-block:: python
204
205 import cx_Oracle
206 import sys
207
208 try:
209 cx_Oracle.init_oracle_client(config_dir="/etc/my-oracle-config")
210 except Exception as err:
211 print("Whoops!")
212 print(err);
213 sys.exit(1);
214
215 This is equivalent to setting the environment variable `TNS_ADMIN
216 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-12C94B15-2CE1-4B98-9D0C-8226A9DDF4CB>`__
217 to ``/etc/my-oracle-config``.
218
219 If :meth:`~cx_Oracle.init_oracle_client()` is not called, or it is called but
220 ``config_dir`` is not specified, then default directories searched for the
221 configuration files. They include:
222
223 * ``$TNS_ADMIN``
224 * ``/opt/oracle/instantclient_19_6/network/admin`` if Instant Client is in ``/opt/oracle/instantclient_19_6``.
225 * ``/usr/lib/oracle/19.6/client64/lib/network/admin`` if Oracle 19.6 Instant Client RPMs are used on Linux.
226 * ``$ORACLE_HOME/network/admin`` if cx_Oracle is using libraries from a database installation.
227
228 A wallet configuration file ``cwallet.sso`` for secure connection can be
229 located with, or separately from, the ``tnsnames.ora`` and ``sqlnet.ora``
230 files. It should be securely stored. The ``sqlnet.ora`` file's
231 ``WALLET_LOCATION`` path should be set to the directory containing
232 ``cwallet.sso``. For Oracle Autonomous Database use of wallets, see
233 :ref:`autononmousdb`.
234
235 Note the :ref:`easyconnect` can set many common configuration options without
236 needing ``tnsnames.ora`` or ``sqlnet.ora`` files.
237
238 The section :ref:`Network Configuration <hanetwork>` has some discussion about
239 Oracle Net configuration.
240
241 .. _optclientfiles:
242
243 Optional Oracle Client Configuration Files
244 ==========================================
245
246 When cx_Oracle uses Oracle Client libraries version 12.1, or later, an optional
247 client parameter file called ``oraaccess.xml`` can be used to configure some
248 behviors of those libraries, such as statement caching and prefetching. This can
249 be useful if the application cannot be altered. The file is read from the same
250 directory as the `Optional Oracle Net Configuration Files`_.
251
252 A sample ``oraaccess.xml`` file that sets the Oracle client ‘prefetch’ value to
253 1000 rows. This value affects every SQL query in the application::
254
255 <?xml version="1.0"?>
256 <oraaccess xmlns="http://xmlns.oracle.com/oci/oraaccess"
257 xmlns:oci="http://xmlns.oracle.com/oci/oraaccess"
258 schemaLocation="http://xmlns.oracle.com/oci/oraaccess
259 http://xmlns.oracle.com/oci/oraaccess.xsd">
260 <default_parameters>
261 <prefetch>
262 <rows>1000</rows>
263 </prefetch>
264 </default_parameters>
265 </oraaccess>
266
267 Prefetching is the number of additional rows the underlying Oracle client
268 library fetches whenever cx_Oracle requests query data from the database.
269 Prefetching is a tuning option to maximize data transfer efficiency and minimize
270 :ref:`round-trips <roundtrips>` to the database. The prefetch size does not
271 affect when, or how many, rows are returned by cx_Oracle to the application.
272 The cache management is transparently handled by the Oracle client libraries.
273 Note, standard cx_Oracle fetch tuning is via :attr:`Cursor.arraysize`, but
274 changing the prefetch value can be useful in some cases such as when modifying
275 the application is not feasible.
276
277 The `oraaccess.xml` file has other uses including:
278
279 - Changing the value of Fast Application Notification :ref:`FAN <fan>` events which affects notifications and Runtime Load Balancing (RLB).
280 - Configuring `Client Result Caching <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D2FA7B29-301B-4AB8-8294-2B1B015899F9>`__ parameters
281 - Turning on `Client Statement Cache Auto-tuning <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-6E21AA56-5BBE-422A-802C-197CAC8AAEA4>`__
282
283 Refer to the documentation on `oraaccess.xml
284 <https://www.oracle.com/pls/topic/lookup?
285 ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__
286 for more details.
287
288 .. _envset:
289
290 Oracle Environment Variables
291 ============================
292
293 Some common environment variables that influence cx_Oracle are shown below. The
294 variables that may be needed depend on how Python is installed, how you connect
295 to the database, and what optional settings are desired. It is recommended to
296 set Oracle variables in the environment before invoking Python, however they may
297 also be set in the application with ``os.putenv()`` before the first connection
298 is established. System environment variables like ``LD_LIBRARY_PATH`` must be
299 set before Python starts.
300
301 .. list-table:: Common Oracle environment variables
302 :header-rows: 1
303 :widths: 1 2
304 :align: left
305
306 * - Oracle Environment Variables
307 - Purpose
308 * - LD_LIBRARY_PATH
309 - The library search path for platforms like Linux should include the
310 Oracle libraries, for example ``$ORACLE_HOME/lib`` or
311 ``/opt/instantclient_19_3``. This variable is not needed if the
312 libraries are located by an alternative method, such as with
313 ``ldconfig``. On other UNIX platforms you may need to set an OS
314 specific equivalent, such as ``LIBPATH`` or ``SHLIB_PATH``.
315 * - PATH
316 - The library search path for Windows should include the location where
317 ``OCI.DLL`` is found. Not needed if you set ``lib_dir`` in a call to
318 :meth:`cx_Oracle.init_oracle_client()`
319 * - TNS_ADMIN
320 - The directory of optional Oracle Client configuration files such as
321 ``tnsnames.ora`` and ``sqlnet.ora``. Not needed if the configuration
322 files are in a default location or if ``config_dir`` was not used in
323 :meth:`cx_Oracle.init_oracle_client()`. See :ref:`optnetfiles`.
324 * - ORA_SDTZ
325 - The default session time zone.
326 * - ORA_TZFILE
327 - The name of the Oracle time zone file to use. See below.
328 * - ORACLE_HOME
329 - The directory containing the Oracle Database software. The directory
330 and various configuration files must be readable by the Python process.
331 This variable should not be set if you are using Oracle Instant Client.
332 * - NLS_LANG
333 - Determines the 'national language support' globalization options for
334 cx_Oracle. Note: from cx_Oracle 8, the character set component is
335 ignored and only the language and territory components of ``NLS_LANG``
336 are used. The character set can instead be specified during connection
337 or connection pool creation. See :ref:`globalization`.
338 * - NLS_DATE_FORMAT, NLS_TIMESTAMP_FORMAT
339 - Often set in Python applications to force a consistent date format
340 independent of the locale. The variables are ignored if the environment
341 variable ``NLS_LANG`` is not set.
342
343 Oracle Instant Client includes a small and big time zone file, for example
344 ``timezone_32.dat`` and ``timezlrg_32.dat``. The versions can be shown by running
345 the utility ``genezi -v`` located in the Instant Client directory. The small file
346 contains only the most commonly used time zones. By default the larger
347 ``timezlrg_n.dat`` file is used. If you want to use the smaller ``timezone_n.dat``
348 file, then set the ``ORA_TZFILE`` environment variable to the name of the file
349 without any directory prefix, for example ``export ORA_TZFILE=timezone_32.dat``.
350 With Oracle Instant Client 12.2 or later, you can also use an external time zone
351 file. Create a subdirectory ``oracore/zoneinfo`` under the Instant Client
352 directory, and move the file into it. Then set ``ORA_TZFILE`` to the file name,
353 without any directory prefix. The ``genezi -v`` utility will show the time zone
354 file in use.
355
356 If cx_Oracle is using Oracle Client libraries from an Oracle Database or full
357 Oracle Client software installation, and you want to use a non-default time zone
358 file, then set ``ORA_TZFILE`` to the file name with a directory prefix, for
359 example: ``export ORA_TZFILE=/opt/oracle/myconfig/timezone_31.dat``.
360
361 The Oracle Database documentation contains more information about time zone
362 files, see `Choosing a Time Zone File
363 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-805AB986-DE12-4FEA-AF56-5AABCD2132DF>`__.
364
365 .. _otherinit:
366
367 Other cx_Oracle Initialization
368 ==============================
369
370 The :meth:`cx_Oracle.init_oracle_client()` function allows ``driver_name`` and
371 ``error_url`` parameters to be set. These are useful for applications whose
372 end-users are not aware cx_Oracle is being used. An example of setting the
373 parameters is:
374
375 .. code-block:: python
376
377 import cx_Oracle
378 import sys
379
380 try:
381 cx_Oracle.init_oracle_client(driver_name = "My Great App : 3.1.4",
382 error_url: "https://example.com/MyInstallInstructions.html")
383 except Exception as err:
384 print("Whoops!")
385 print(err);
386 sys.exit(1);
387
388 The convention for ``driver_name`` is to separate the product name from the
389 product version by a colon and single blank characters. The value will be shown
390 in Oracle Database views like ``V$SESSION_CONNECT_INFO``. If this parameter is
391 not specified, then the value "cx_Oracle : *version*" is used.
392
393 The ``error_url`` string will be shown in the exception raised if
394 ``init_oracle_client()`` cannot load the Oracle Client libraries. This allows
395 applications that use cx_Oracle to refer users to application-specific
396 installation instructions. If this value is not specified, then the
397 :ref:`installation` URL is used.
0 .. _installation:
1
2 ************************
3 cx_Oracle 8 Installation
4 ************************
5
6 Overview
7 ========
8
9 To use cx_Oracle 8 with Python and Oracle Database you need:
10
11 - Python 3.5 and higher. Older versions of cx_Oracle may work with older
12 versions of Python.
13
14 - Oracle Client libraries. These can be from the free `Oracle Instant
15 Client
16 <https://www.oracle.com/database/technologies/instant-client.html>`__,
17 or those included in Oracle Database if Python is on the same
18 machine as the database. Oracle client libraries versions 19, 18, 12,
19 and 11.2 are supported on Linux, Windows and macOS. Users have
20 also reported success with other platforms. Use the latest client possible:
21 Oracle's standard client-server version interoperability allows connection to
22 both older and newer databases.
23
24 - An Oracle Database, either local or remote.
25
26 The cx_Oracle module loads Oracle Client libraries which communicate
27 over Oracle Net to an existing database. Oracle Net is not a separate
28 product: it is how the Oracle Client and Oracle Database communicate.
29
30 .. figure:: /images/cx_Oracle_arch.png
31
32 cx_Oracle Architecture
33
34
35 Quick Start cx_Oracle Installation
36 ==================================
37
38 - Install `Python <https://www.python.org/downloads>`__ 3, if not already
39 available. On macOS you must always install your own Python.
40
41 Python 3.5 and higher are supported by cx_Oracle 8. If you use Python 2,
42 then the older cx_Oracle 7.3 will install.
43
44 - Install cx_Oracle from `PyPI
45 <https://pypi.org/project/cx-Oracle/>`__ with:
46
47 .. code-block:: shell
48
49 python -m pip install cx_Oracle --upgrade
50
51 Note: if a binary wheel package is not available for your platform,
52 the source package will be downloaded instead. This will be compiled
53 and the resulting binary installed.
54
55 The ``--user`` option may be useful, if you don't have permission to write to
56 system directories:
57
58 .. code-block:: shell
59
60 python -m pip install cx_Oracle --upgrade --user
61
62 If you are behind a proxy, add a proxy server to the command, for example add
63 ``--proxy=http://proxy.example.com:80``
64
65 - Add Oracle 19, 18, 12 or 11.2 client libraries to your operating system
66 library search path such as ``PATH`` on Windows or ``LD_LIBRARY_PATH`` on
67 Linux. On macOS use :meth:`~cx_Oracle.init_oracle_client()` in your
68 application to pass the Oracle Client directory name, see :ref:`libinit`.
69 This is also usable on Windows.
70
71 To get the libraries:
72
73 - If your database is on a remote computer, then download and unzip the client
74 libraries from the free `Oracle Instant Client
75 <https://www.oracle.com/database/technologies/instant-client.html>`__
76 "Basic" or "Basic Light" package for your operating system
77 architecture.
78
79 Instant Client on Windows requires an appropriate Microsoft Windows
80 Redistributables, see :ref:`wininstall`. On Linux, the ``libaio``
81 (sometimes called ``libaio1``) package is needed. Oracle Linux 8 also
82 needs the ``libnsl`` package.
83
84 - Alternatively, use the client libraries already available in a
85 locally installed database such as the free `Oracle Database
86 Express Edition ("XE")
87 <https://www.oracle.com/database/technologies/appdev/xe.html>`__
88 release.
89
90 Version 19, 18 and 12.2 client libraries can connect to Oracle Database 11.2
91 or greater. Version 12.1 client libraries can connect to Oracle Database 10.2
92 or greater. Version 11.2 client libraries can connect to Oracle Database 9.2
93 or greater.
94
95 - Create a script like the one below:
96
97 .. code-block:: python
98
99 # myscript.py
100
101 import cx_Oracle
102
103 # Connect as user "hr" with password "welcome" to the "orclpdb1" service running on this computer.
104 connection = cx_Oracle.connect("hr", "welcome", "localhost/orclpdb1")
105
106 cursor = connection.cursor()
107 cursor.execute("""
108 SELECT first_name, last_name
109 FROM employees
110 WHERE department_id = :did AND employee_id > :eid""",
111 did = 50,
112 eid = 190)
113 for fname, lname in cursor:
114 print("Values:", fname, lname)
115
116 Locate your Oracle Database username and password, and the database
117 connection string. The connection string is commonly of the format
118 ``hostname/servicename``, using the hostname where the database is
119 running, and using the service name of the Oracle Database instance.
120
121 Substitute your username, password and connection string in the
122 code. Run the Python script, for example::
123
124 python myscript.py
125
126 You can learn how to use cx_Oracle from the :ref:`API documentation <module>`
127 and `samples
128 <https://github.com/oracle/python-cx_Oracle/blob/master/samples>`__.
129
130 If you run into installation trouble, check out the section on `Troubleshooting`_.
131
132
133 Oracle Client and Oracle Database Interoperability
134 ==================================================
135
136 cx_Oracle requires Oracle Client libraries. The libraries provide the
137 necessary network connectivity to access an Oracle Database instance.
138 They also provide basic and advanced connection management and data
139 features to cx_Oracle.
140
141 The simplest way to get Oracle Client libraries is to install the free
142 `Oracle Instant Client
143 <https://www.oracle.com/database/technologies/instant-client.html>`__
144 "Basic" or "Basic Light" package. The libraries are also available in
145 any Oracle Database installation or full Oracle Client installation.
146
147 Oracle's standard client-server network interoperability allows
148 connections between different versions of Oracle Client libraries and
149 Oracle Database. For certified configurations see Oracle Support's
150 `Doc ID 207303.1
151 <https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__.
152 In summary, Oracle Client 19, 18 and 12.2 can connect to Oracle Database 11.2 or
153 greater. Oracle Client 12.1 can connect to Oracle Database 10.2 or
154 greater. Oracle Client 11.2 can connect to Oracle Database 9.2 or
155 greater. The technical restrictions on creating connections may be
156 more flexible. For example Oracle Client 12.2 can successfully
157 connect to Oracle Database 10.2.
158
159 cx_Oracle uses the shared library loading mechanism available on each
160 supported platform to load the Oracle Client libraries at runtime. It
161 does not need to be rebuilt for different versions of the libraries.
162 Since a single cx_Oracle binary can use different client versions and
163 also access multiple database versions, it is important your
164 application is tested in your intended release environments. Newer
165 Oracle clients support new features, such as the `oraaccess.xml
166 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__ external configuration
167 file available with 12.1 or later clients, session pool improvements,
168 improved high availability features, call timeouts, and `other enhancements
169 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D60519C3-406F-4588-8DA1-D475D5A3E1F6>`__.
170
171 The cx_Oracle function :func:`~cx_Oracle.clientversion()` can be used
172 to determine which Oracle Client version is in use and the attribute
173 :attr:`Connection.version` can be used to determine which Oracle
174 Database version a connection is accessing. These can then be used to
175 adjust application behavior accordingly. Attempts to use some Oracle
176 features that are not supported by a particular client/server
177 combination may result in runtime errors. These include:
178
179 - when attempting to access attributes that are not supported by the
180 current Oracle Client library you will get the error "ORA-24315: illegal
181 attribute type"
182
183 - when attempting to use implicit results with Oracle Client 11.2
184 against Oracle Database 12c you will get the error "ORA-29481:
185 Implicit results cannot be returned to client"
186
187 - when attempting to get array DML row counts with Oracle Client
188 11.2 you will get the error "DPI-1050: Oracle Client library must be at
189 version 12.1 or higher"
190
191
192 Installing cx_Oracle on Linux
193 =============================
194
195 This section discusses the generic installation methods on Linux. To use Python
196 and cx_Oracle RPM packages from yum on Oracle Linux, see :ref:`oraclelinux`.
197
198 Install cx_Oracle
199 -----------------
200
201 The generic way to install cx_Oracle on Linux is to use Python's `Pip
202 <https://pip.readthedocs.io/en/latest/installing/>`__ package to
203 install cx_Oracle from `PyPI
204 <https://pypi.org/project/cx-Oracle/>`__:
205
206 .. code-block:: shell
207
208 python -m pip install cx_Oracle --upgrade
209
210 The ``--user`` option may be useful, if you don't have permission to write to
211 system directories:
212
213 .. code-block:: shell
214
215 python -m pip install cx_Oracle --upgrade --user
216
217 If you are behind a proxy, add a proxy server to the command, for example add
218 ``--proxy=http://proxy.example.com:80``
219
220 This will download and install a pre-compiled binary `if one is
221 available <https://pypi.org/project/cx-Oracle/>`__ for your
222 architecture. If a pre-compiled binary is not available, the source
223 will be downloaded, compiled, and the resulting binary installed.
224 Compiling cx_Oracle requires the ``Python.h`` header file. If you are
225 using the default ``python`` package, this file is in the ``python-devel``
226 package or equivalent.
227
228 Install Oracle Client
229 ---------------------
230
231 Using cx_Oracle requires Oracle Client libraries to be installed.
232 These provide the necessary network connectivity allowing cx_Oracle
233 to access an Oracle Database instance.
234
235 - If your database is on a remote computer, then download the free `Oracle
236 Instant Client
237 <https://www.oracle.com/database/technologies/instant-client.html>`__
238 "Basic" or "Basic Light" package for your operating system
239 architecture. Use the RPM or ZIP packages, based on your
240 preferences.
241
242 - Alternatively, use the client libraries already available in a
243 locally installed database such as the free `Oracle Database
244 Express Edition ("XE")
245 <https://www.oracle.com/database/technologies/appdev/xe.html>`__
246 release.
247
248 Oracle Instant Client Zip Files
249 +++++++++++++++++++++++++++++++
250
251 To use cx_Oracle with Oracle Instant Client zip files:
252
253 1. Download an Oracle 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip file: `64-bit
254 <https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html>`__
255 or `32-bit
256 <https://www.oracle.com/database/technologies/instant-client/linux-x86-32-downloads.html>`__, matching your
257 Python architecture.
258
259 The latest version is recommended. Oracle Instant Client 19 will
260 connect to Oracle Database 11.2 or later.
261
262 2. Unzip the package into a single directory that is accessible to your
263 application. For example:
264
265 .. code-block:: shell
266
267 mkdir -p /opt/oracle
268 cd /opt/oracle
269 unzip instantclient-basic-linux.x64-19.9.0.0.0dbru.zip
270
271 3. Install the ``libaio`` package with sudo or as the root user. For example::
272
273 sudo yum install libaio
274
275 On some Linux distributions this package is called ``libaio1`` instead.
276
277 On recent Linux versions, such as Oracle Linux 8, you may also need to
278 install the ``libnsl`` package.
279
280 4. If there is no other Oracle software on the machine that will be
281 impacted, permanently add Instant Client to the runtime link
282 path. For example, with sudo or as the root user:
283
284 .. code-block:: shell
285
286 sudo sh -c "echo /opt/oracle/instantclient_19_9 > /etc/ld.so.conf.d/oracle-instantclient.conf"
287 sudo ldconfig
288
289 Alternatively, set the environment variable ``LD_LIBRARY_PATH`` to
290 the appropriate directory for the Instant Client version. For
291 example::
292
293 export LD_LIBRARY_PATH=/opt/oracle/instantclient_19_9:$LD_LIBRARY_PATH
294
295 5. If you use optional Oracle configuration files such as ``tnsnames.ora``,
296 ``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files
297 in an accessible directory, for example in
298 ``/opt/oracle/your_config_dir``. Then use:
299
300 .. code-block:: python
301
302 import cx_Oracle
303 cx_Oracle.init_oracle_client(config_dir="/home/your_username/oracle/your_config_dir")
304
305 Or set the environment variable ``TNS_ADMIN`` to that directory name.
306
307 Alternatively, put the files in the ``network/admin`` subdirectory of Instant
308 Client, for example in ``/opt/oracle/instantclient_19_9/network/admin``.
309 This is the default Oracle configuration directory for executables linked
310 with this Instant Client.
311
312 Oracle Instant Client RPMs
313 ++++++++++++++++++++++++++
314
315 To use cx_Oracle with Oracle Instant Client RPMs:
316
317 1. Download an Oracle 19, 18, 12, or 11.2 "Basic" or "Basic Light" RPM: `64-bit
318 <https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html>`__
319 or `32-bit
320 <https://www.oracle.com/database/technologies/instant-client/linux-x86-32-downloads.html>`__, matching your
321 Python architecture.
322
323 Oracle's yum server has `Instant Client RPMs for Oracle Linux 8
324 <https://yum.oracle.com/repo/OracleLinux/OL8/oracle/instantclient/x86_64/index.html>`__,
325 `Instant Client RPMs for Oracle Linux 7
326 <https://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/index.html>`__
327 and `Instant Client RPMs for Oracle Linux 6
328 <https://yum.oracle.com/repo/OracleLinux/OL6/oracle/instantclient/x86_64/index.html>`__
329 that can be downloaded without needing a click-through.
330
331 The latest version is recommended. Oracle Instant Client 19 will
332 connect to Oracle Database 11.2 or later.
333
334 2. Install the downloaded RPM with sudo or as the root user. For example:
335
336 .. code-block:: shell
337
338 sudo yum install oracle-instantclient19.9-basic-19.9.0.0.0-1.x86_64.rpm
339
340 Yum will automatically install required dependencies, such as ``libaio``.
341
342 On recent Linux versions, such as Oracle Linux 8, you may need to manually
343 install the ``libnsl`` package.
344
345 3. For Instant Client 19, the system library search path is
346 automatically configured during installation.
347
348 For older versions, if there is no other Oracle software on the machine that will be
349 impacted, permanently add Instant Client to the runtime link
350 path. For example, with sudo or as the root user:
351
352 .. code-block:: shell
353
354 sudo sh -c "echo /usr/lib/oracle/18.3/client64/lib > /etc/ld.so.conf.d/oracle-instantclient.conf"
355 sudo ldconfig
356
357 Alternatively, for version 18 and earlier, every shell running
358 Python will need to have the environment variable
359 ``LD_LIBRARY_PATH`` set to the appropriate directory for the
360 Instant Client version. For example::
361
362 export LD_LIBRARY_PATH=/usr/lib/oracle/18.3/client64/lib:$LD_LIBRARY_PATH
363
364 4. If you use optional Oracle configuration files such as ``tnsnames.ora``,
365 ``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files
366 in an accessible directory, for example in
367 ``/opt/oracle/your_config_dir``. Then use:
368
369 .. code-block:: python
370
371 import cx_Oracle
372 cx_Oracle.init_oracle_client(config_dir="/opt/oracle/your_config_dir")
373
374 Or set the environment variable ``TNS_ADMIN`` to that directory name.
375
376 Alternatively, put the files in the ``network/admin`` subdirectory of Instant
377 Client, for example in ``/usr/lib/oracle/19.9/client64/lib/network/admin``.
378 This is the default Oracle configuration directory for executables linked
379 with this Instant Client.
380
381 Local Database or Full Oracle Client
382 ++++++++++++++++++++++++++++++++++++
383
384 cx_Oracle applications can use Oracle Client 19, 18, 12, or 11.2 libraries
385 from a local Oracle Database or full Oracle Client installation.
386
387 The libraries must be either 32-bit or 64-bit, matching your
388 Python architecture.
389
390 1. Set required Oracle environment variables by running the Oracle environment
391 script. For example:
392
393 .. code-block:: shell
394
395 source /usr/local/bin/oraenv
396
397 For Oracle Database Express Edition ("XE") 11.2, run:
398
399 .. code-block:: shell
400
401 source /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh
402
403 2. Optional Oracle configuration files such as ``tnsnames.ora``,
404 ``sqlnet.ora`` or ``oraaccess.xml`` can be placed in
405 ``$ORACLE_HOME/network/admin``.
406
407 Alternatively, Oracle configuration files can be put in another,
408 accessible directory. Then set the environment variable
409 ``TNS_ADMIN`` to that directory name.
410
411
412 .. _oraclelinux:
413
414 Installing cx_Oracle RPMs on Oracle Linux
415 =========================================
416
417 Python and cx_Oracle RPM packages are available from the `Oracle Linux yum server
418 <https://yum.oracle.com/>`__. Various versions of Python are easily installed.
419 Using the yum server makes it easy to keep up to date.
420
421 Installation instructions are at `Oracle Linux for Python
422 Developers <https://yum.oracle.com/oracle-linux-python.html>`__.
423
424 .. _wininstall:
425
426 Installing cx_Oracle on Windows
427 ===============================
428
429 Install cx_Oracle
430 -----------------
431
432 Use Python's `Pip <https://pip.readthedocs.io/en/latest/installing/>`__
433 package to install cx_Oracle from `PyPI
434 <https://pypi.org/project/cx-Oracle/>`__::
435
436 python -m pip install cx_Oracle --upgrade
437
438 If you are behind a proxy, specify your proxy server:
439
440 .. code-block:: shell
441
442 python -m pip install cx_Oracle --proxy=http://proxy.example.com:80 --upgrade
443
444 This will download and install a pre-compiled binary `if one is
445 available <https://pypi.org/project/cx-Oracle/>`__ for your
446 architecture. If a pre-compiled binary is not available, the source
447 will be downloaded, compiled, and the resulting binary installed.
448
449 Install Oracle Client
450 ---------------------
451
452 Using cx_Oracle requires Oracle Client libraries to be installed.
453 These provide the necessary network connectivity allowing cx_Oracle
454 to access an Oracle Database instance. Oracle Client versions 19, 18,
455 12 and 11.2 are supported.
456
457 - If your database is on a remote computer, then download the free `Oracle
458 Instant Client
459 <https://www.oracle.com/database/technologies/instant-client.html>`__
460 "Basic" or "Basic Light" package for your operating system
461 architecture.
462
463 - Alternatively, use the client libraries already available in a
464 locally installed database such as the free `Oracle Database
465 Express Edition ("XE")
466 <https://www.oracle.com/database/technologies/appdev/xe.html>`__
467 release.
468
469
470 Oracle Instant Client Zip Files
471 +++++++++++++++++++++++++++++++
472
473 To use cx_Oracle with Oracle Instant Client zip files:
474
475 1. Download an Oracle 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip
476 file: `64-bit
477 <https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html>`__
478 or `32-bit
479 <https://www.oracle.com/database/technologies/instant-client/microsoft-windows-32-downloads.html>`__, matching your
480 Python architecture.
481
482 The latest version is recommended. Oracle Instant Client 19 will
483 connect to Oracle Database 11.2 or later.
484
485 Windows 7 users: Note that Oracle 19c is not supported on Windows 7.
486
487 2. Unzip the package into a directory that is accessible to your
488 application. For example unzip
489 ``instantclient-basic-windows.x64-19.9.0.0.0dbru.zip`` to
490 ``C:\oracle\instantclient_19_9``.
491
492 3. Oracle Instant Client libraries require a Visual Studio redistributable with
493 a 64-bit or 32-bit architecture to match Instant Client's architecture.
494 Each Instant Client version requires a different redistributable version:
495
496 - For Instant Client 19 install `VS 2017 <https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads>`__.
497 - For Instant Client 18 or 12.2 install `VS 2013 <https://support.microsoft.com/en-us/kb/2977003#bookmark-vs2013>`__
498 - For Instant Client 12.1 install `VS 2010 <https://support.microsoft.com/en-us/kb/2977003#bookmark-vs2010>`__
499 - 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>`__
500
501 Configure Oracle Instant Client
502 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
503
504 1. There are several alternative ways to tell cx_Oracle where your Oracle Client
505 libraries are, see :ref:`initialization`.
506
507 * With Oracle Instant Client you can use :meth:`~cx_Oracle.init_oracle_client()`
508 in your application, for example:
509
510 .. code-block:: python
511
512 import cx_Oracle
513 cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_9")
514
515 Note a 'raw' string is used because backslashes occur in the path.
516
517 * Alternatively, add the Oracle Instant Client directory to the ``PATH``
518 environment variable. The directory must occur in ``PATH`` before any
519 other Oracle directories. Restart any open command prompt windows.
520
521 * Another way to set ``PATH`` is to use a batch file that sets it before Python
522 is executed, for example::
523
524 REM mypy.bat
525 SET PATH=C:\oracle\instantclient_19_9;%PATH%
526 python %*
527
528 Invoke this batch file every time you want to run Python.
529
530 2. If you use optional Oracle configuration files such as ``tnsnames.ora``,
531 ``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files
532 in an accessible directory, for example in
533 ``C:\oracle\your_config_dir``. Then use:
534
535 .. code-block:: python
536
537 import cx_Oracle
538 cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_9",
539 config_dir=r"C:\oracle\your_config_dir")
540
541 Or set the environment variable ``TNS_ADMIN`` to that directory name.
542
543 Alternatively, put the files in a ``network\admin`` subdirectory of
544 Instant Client, for example in
545 ``C:\oracle\instantclient_19_9\network\admin``. This is the default
546 Oracle configuration directory for executables linked with this
547 Instant Client.
548
549
550 Local Database or Full Oracle Client
551 ++++++++++++++++++++++++++++++++++++
552
553 cx_Oracle applications can use Oracle Client 19, 18, 12, or 11.2
554 libraries libraries from a local Oracle Database or full Oracle
555 Client.
556
557 The Oracle libraries must be either 32-bit or 64-bit, matching your
558 Python architecture.
559
560 1. Set the environment variable ``PATH`` to include the path that contains
561 ``OCI.DLL``, if it is not already set.
562
563 Restart any open command prompt windows.
564
565 2. Optional Oracle configuration files such as ``tnsnames.ora``,
566 ``sqlnet.ora`` or ``oraaccess.xml`` can be placed in the
567 ``network\admin`` subdirectory of the Oracle Database software
568 installation.
569
570 Alternatively, pass ``config_dir`` to :meth:`~cx_Oracle.init_oracle_client()`
571 as shown in the previous section, or set ``TNS_ADMIN`` to the directory name.
572
573 Installing cx_Oracle on macOS
574 =============================
575
576 Install Python
577 --------------
578
579 Make sure you are not using the bundled Python. This has restricted
580 entitlements and will fail to load Oracle client libraries. Instead use
581 `Homebrew <https://brew.sh>`__ or `Python.org
582 <https://www.python.org/downloads>`__.
583
584 Install cx_Oracle
585 -----------------
586
587 Use Python's `Pip <https://pip.readthedocs.io/en/latest/installing/>`__
588 package to install cx_Oracle from `PyPI
589 <https://pypi.org/project/cx-Oracle/>`__:
590
591 .. code-block:: shell
592
593 python -m pip install cx_Oracle --upgrade
594
595 The ``--user`` option may be useful, if you don't have permission to write to
596 system directories:
597
598 .. code-block:: shell
599
600 python -m pip install cx_Oracle --upgrade --user
601
602 If you are behind a proxy, add a proxy server to the command, for example add
603 ``--proxy=http://proxy.example.com:80``
604
605 The source will be downloaded, compiled, and the resulting binary
606 installed.
607
608 Install Oracle Instant Client
609 -----------------------------
610
611 Oracle Instant Client provides the network connectivity for accessing Oracle
612 Database.
613
614 Manual Installation
615 +++++++++++++++++++
616
617 * Download the **Basic** 64-bit DMG from `Oracle
618 <https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html>`__.
619
620 * In Finder, double click on the DMG to mount it.
621
622 * Open a terminal window and run the install script in the mounted package, for example:
623
624 .. code-block:: shell
625
626 /Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru/install_ic.sh
627
628 This copies the contents to ``$HOME/Downloads/instantclient_19_8``.
629
630 * In Finder, eject the mounted Instant Client package.
631
632 If you have multiple Instant Client DMG packages mounted, you only need to run
633 ``install_ic.sh`` once. It will copy all mounted Instant Client DMG packages at
634 the same time.
635
636 Scripted Installation
637 +++++++++++++++++++++
638
639 Instant Client installation can alternatively be scripted, for example:
640
641 .. code-block:: shell
642
643 cd $HOME/Downloads
644 curl -O https://download.oracle.com/otn_software/mac/instantclient/198000/instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg
645 hdiutil mount instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg
646 /Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru/install_ic.sh
647 hdiutil unmount /Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru
648
649 The Instant Client directory will be ``$HOME/Downloads/instantclient_19_8``.
650
651
652 Configure Oracle Instant Client
653 -------------------------------
654
655 1. Call :meth:`~cx_Oracle.init_oracle_client()` once in your application:
656
657 .. code-block:: python
658
659 import cx_Oracle
660 cx_Oracle.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8")
661
662 2. If you use optional Oracle configuration files such as ``tnsnames.ora``,
663 ``sqlnet.ora`` or ``oraaccess.xml`` with Oracle Instant Client, then put the
664 files in an accessible directory, for example in
665 ``/Users/your_username/oracle/your_config_dir``. Then use:
666
667 .. code-block:: python
668
669 import cx_Oracle
670 cx_Oracle.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8",
671 config_dir="/Users/your_username/oracle/your_config_dir")
672
673 Or set the environment variable ``TNS_ADMIN`` to that directory name.
674
675 Alternatively, put the files in the ``network/admin`` subdirectory of Oracle
676 Instant Client, for example in
677 ``/Users/your_username/Downloads/instantclient_19_8/network/admin``. This is the
678 default Oracle configuration directory for executables linked with this
679 Instant Client.
680
681 Installing cx_Oracle without Internet Access
682 ============================================
683
684 To install cx_Oracle on a computer that is not connected to the
685 internet, download the appropriate cx_Oracle file from `PyPI
686 <https://pypi.org/project/cx-Oracle/#files>`__. Transfer this file to
687 the offline computer and install it with::
688
689 python -m pip install "<file_name>"
690
691 Then follow the general cx_Oracle platform installation instructions
692 to install Oracle client libraries.
693
694 Install Using GitHub
695 ====================
696
697 In order to install using the source on GitHub, use the following commands::
698
699 git clone https://github.com/oracle/python-cx_Oracle.git cx_Oracle
700 cd cx_Oracle
701 git submodule init
702 git submodule update
703 python setup.py install
704
705 Note that if you download a source zip file directly from GitHub then
706 you will also need to download an `ODPI-C
707 <https://github.com/oracle/odpi>`__ source zip file and extract it
708 inside the directory called "odpi".
709
710 cx_Oracle source code is also available from oss.oracle.com. This can
711 be cloned with::
712
713 git clone git://oss.oracle.com/git/oracle/python-cx_Oracle.git cx_Oracle
714 cd cx_Oracle
715 git submodule init
716 git submodule update
717
718
719 Install Using Source from PyPI
720 ==============================
721
722 The source package can be downloaded manually from
723 `PyPI <https://pypi.org/project/cx-Oracle/>`__ and extracted, after
724 which the following commands should be run::
725
726 python setup.py build
727 python setup.py install
728
729
730 Upgrading from Older Versions
731 =============================
732
733 Review the :ref:`release notes <releasenotes>` for deprecations and modify any
734 affected code.
735
736 If you are upgrading from cx_Oracle 7 note these changes:
737
738 - The default character set used by cx_Oracle 8 is now "UTF-8". Also, the
739 character set component of the ``NLS_LANG`` environment variable is
740 ignored. If you need to change the character set, then pass ``encoding``
741 and ``nendcoding`` parameters when creating a connection or connection
742 pool. See :ref:`globalization`.
743
744 - Any uses of ``type(var)`` need to be changed to ``var.type``.
745
746 - Any uses of ``var.type is not None`` need to be changed to
747 ``isinstance(var.type, cx_Oracle.ObjectType)``
748
749 - Note that ``TIMESTAMP WITH TIME ZONE`` columns will now be reported as
750 :data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` instead of
751 :data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`.
752
753 - Note that ``TIMESTAMP WITH LOCAL TIME ZONE`` columns will now be reported
754 as :data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` instead of
755 :data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`.
756
757 - Note that ``BINARY_FLOAT`` columns will now be reported as
758 :data:`cx_Oracle.DB_TYPE_BINARY_FLOAT` instead of
759 :data:`cx_Oracle.NATIVE_DOUBLE` in :data:`Cursor.description`.
760
761 If you are upgrading from cx_Oracle 5 note these installation changes:
762
763 - When using Oracle Instant Client, you should not set ``ORACLE_HOME``.
764
765 - On Linux, cx_Oracle 6 and higher no longer uses Instant Client RPMs
766 automatically. You must set ``LD_LIBRARY_PATH`` or use ``ldconfig`` to
767 locate the Oracle client library.
768
769 - PyPI no longer allows Windows installers or Linux RPMs to be
770 hosted. Use the supplied cx_Oracle Wheels instead, or use RPMs
771 from Oracle, see :ref:`oraclelinux`.
772
773 .. _python2:
774
775 Installing cx_Oracle in Python 2
776 ================================
777
778 cx_Oracle 7.3 was the last version with support for Python 2.
779
780 If you install cx_Oracle in Python 2 using the commands provided above, then
781 cx_Oracle 7.3 will be installed. This is equivalent to using a command like::
782
783 python -m pip install cx_Oracle==7.3 --upgrade --user
784
785 For other installation options such as installing through a proxy, see
786 instructions above. Make sure the Oracle Client libraries are in the system
787 library search path because cx_Oracle 7 does not support the
788 :meth:`cx_Oracle.init_oracle_client()` method and does not support loading the
789 Oracle Client libraries from the directory containing the cx_Oracle module
790 binary.
791
792 Installing cx_Oracle 5.3
793 ========================
794
795 If you require cx_Oracle 5.3, download a Windows installer from `PyPI
796 <https://pypi.org/project/cx-Oracle/>`__ or use ``python -m pip
797 install cx-oracle==5.3`` to install from source.
798
799 Very old versions of cx_Oracle can be found in the files section at
800 `SourceForce <https://sourceforge.net/projects/cx-oracle/files/>`__.
801
802
803 Troubleshooting
804 ===============
805
806 If installation fails:
807
808 - Use option ``-v`` with pip. Review your output and logs. Try to install
809 using a different method. **Google anything that looks like an error.**
810 Try some potential solutions.
811
812 - Was there a network connection error? Do you need to set the
813 environment variables ``http_proxy`` and/or ``https_proxy``? Or
814 try ``pip install --proxy=http://proxy.example.com:80 cx_Oracle
815 --upgrade``?
816
817 - If upgrading gave no errors but the old version is still
818 installed, try ``pip install cx_Oracle --upgrade
819 --force-reinstall``
820
821 - If you do not have access to modify your system version of
822 Python, can you use ``pip install cx_Oracle --upgrade --user``
823 or venv?
824
825 - Do you get the error "``No module named pip``"? The pip module is builtin
826 to Python but is sometimes removed by the OS. Use the venv module
827 (builtin to Python 3.x) or virtualenv module instead.
828
829 - Do you get the error "``fatal error: dpi.h: No such file or directory``"
830 when building from source code? Ensure that your source installation has
831 a subdirectory called "odpi" containing files. If missing, review the
832 section on `Install Using GitHub`_.
833
834 If using cx_Oracle fails:
835
836 - Do you get the error "``DPI-1047: Oracle Client library cannot be
837 loaded``"?
838
839 - On Windows and macOS, try using :meth:`~cx_Oracle.init_oracle_client()`.
840 See :ref:`usinginitoracleclient`.
841
842 - Check that Python and your Oracle Client libraries are both 64-bit, or
843 both 32-bit. The ``DPI-1047`` message will tell you whether the 64-bit
844 or 32-bit Oracle Client is needed for your Python.
845
846 - Set the environment variable ``DPI_DEBUG_LEVEL`` to 64 and restart
847 cx_Oracle. The trace messages will show how and where cx_Oracle is
848 looking for the Oracle Client libraries.
849
850 At a Windows command prompt, this could be done with::
851
852 set DPI_DEBUG_LEVEL=64
853
854 On Linux and macOS, you might use::
855
856 export DPI_DEBUG_LEVEL=64
857
858 - On Windows, if you used :meth:`~cx_Oracle.init_oracle_client()` and have
859 a full database installation, make sure this database is the `currently
860 configured database
861 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-33D575DD-47FF-42B1-A82F-049D3F2A8791>`__.
862
863 - On Windows, if you are not using
864 :meth:`~cx_Oracle.init_oracle_client()`, then restart your command prompt
865 and use ``set PATH`` to check the environment variable has the correct
866 Oracle Client listed before any other Oracle directories.
867
868 - On Windows, use the ``DIR`` command to verify that ``OCI.DLL`` exists in
869 the directory passed to ``init_oracle_client()`` or set in ``PATH``.
870
871 - On Windows, check that the correct `Windows Redistributables
872 <https://oracle.github.io/odpi/doc/installation.html#windows>`__ have
873 been installed.
874
875 - On Linux, check the ``LD_LIBRARY_PATH`` environment variable contains
876 the Oracle Client library directory. If you are using Oracle Instant
877 Client, a preferred alternative is to ensure a file in the
878 ``/etc/ld.so.conf.d`` directory contains the path to the Instant Client
879 directory, and then run ``ldconfig``.
880
881 - On macOS, make sure you are not using the bundled Python (use `Homebrew
882 <https://brew.sh>`__ or `Python.org
883 <https://www.python.org/downloads>`__ instead). If you are not using
884 :meth:`~cx_Oracle.init_oracle_client()`, then put the Oracle Instant
885 Client libraries in ``~/lib`` or ``/usr/local/lib``.
886
887 - If you got "``DPI-1072: the Oracle Client library version is
888 unsupported``", then review the installation requirements. cx_Oracle
889 needs Oracle client libraries 11.2 or later. Note that version 19 is not
890 supported on Windows 7. Similar steps shown above for ``DPI-1047`` may
891 help.
892
893 - If you have multiple versions of Python installed, make sure you are
894 using the correct python and pip (or python3 and pip3) executables.
0 .. _introduction:
1
2 *************************
3 Introduction to cx_Oracle
4 *************************
5
6 cx_Oracle is a Python extension module that enables Python access to Oracle
7 Database. It conforms to the `Python Database API v2.0 Specification
8 <https://www.python.org/dev/peps/pep-0249/>`__ with a considerable number of
9 additions and a couple of exclusions.
10
11 Architecture
12 ------------
13
14 Python programs call cx_Oracle functions. Internally cx_Oracle dynamically
15 loads Oracle Client libraries to access Oracle Database. The database can be on
16 the same machine as Python, or it can be remote.
17
18 .. _archfig:
19 .. figure:: /images/cx_Oracle_arch.png
20
21 cx_Oracle Architecture
22
23 cx_Oracle is typically installed from `PyPI
24 <https://pypi.org/project/cx-Oracle/>`__ using `pip
25 <http://pip.readthedocs.io/en/latest/installing/>`__. The Oracle Client
26 libraries need to be installed separately. The libraries can be obtained from
27 an installation of `Oracle Instant Client
28 <https://www.oracle.com/database/technologies/instant-client.html>`__, from a
29 full Oracle Client installation, or even from an Oracle Database installation
30 (if Python is running on the same machine as the database).
31
32 Some behaviors of the Oracle Client libraries can optionally be configured with
33 an ``oraaccess.xml`` file, for example to enable auto-tuning of a statement
34 cache. See :ref:`optclientfiles`.
35
36 The Oracle Net layer can optionally be configured with files such as
37 ``tnsnames.ora`` and ``sqlnet.ora``, for example to enable :ref:`network
38 encryption <netencrypt>`. See :ref:`optnetfiles`.
39
40 Oracle environment variables that are set before cx_Oracle first creates a
41 database connection will affect cx_Oracle behavior. Optional variables include
42 NLS_LANG, NLS_DATE_FORMAT and TNS_ADMIN. See :ref:`envset`.
43
44 Features
45 --------
46
47 The cx_Oracle feature highlights are:
48
49 * Easily installed from PyPI
50 * Support for multiple Oracle Client and Database versions
51 * Execution of SQL and PL/SQL statements
52 * Extensive Oracle data type support, including large objects (CLOB and
53 BLOB) and binding of SQL objects
54 * Connection management, including connection pooling
55 * Oracle Database High Availability features
56 * Full use of Oracle Network Service infrastructure, including encrypted
57 network traffic and security features
58
59 A complete list of supported features can be seen `here
60 <https://oracle.github.io/python-cx_Oracle/index.html#features>`_.
61
62 Getting Started
63 ---------------
64
65 Install cx_Oracle using the :ref:`installation <installation>` steps.
66
67 Create a script ``query.py`` as shown below:
68
69 .. code-block:: python
70
71 # query.py
72
73 import cx_Oracle
74
75 # Establish the database connection
76 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1")
77
78 # Obtain a cursor
79 cursor = connection.cursor()
80
81 # Data for binding
82 managerId = 145
83 firstName = "Peter"
84
85 # Execute the query
86 sql = """SELECT first_name, last_name
87 FROM employees
88 WHERE manager_id = :mid AND first_name = :fn"""
89 cursor.execute(sql, mid = managerId, fn = firstName)
90
91 # Loop over the result set
92 for row in cursor:
93 print(row)
94
95 This uses Oracle's `sample HR schema
96 <https://github.com/oracle/db-sample-schemas>`__.
97
98 Simple :ref:`connection <connhandling>` to the database requires a username,
99 password and connection string. Locate your Oracle Database `user name and
100 password <https://www.youtube.com/watch?v=WDJacg0NuLo>`_ and the database
101 :ref:`connection string <connstr>`, and use them in ``query.py``. For
102 cx_Oracle the connection string is commonly of the format
103 ``hostname/servicename``, using the host name where the database is running and
104 the Oracle Database service name of the database instance.
105
106 The :ref:`cursor <cursorobj>` is the object that allows statements to be
107 executed and results (if any) fetched.
108
109 The data values in ``managerId`` and ``firstName`` are 'bound' to the statement
110 placeholder 'bind variables' ``:mid`` and ``:fn`` when the statement is
111 executed. This separates the statement text from the data, which helps avoid
112 SQL Injection security risks. :ref:`Binding <bind>` is also important for
113 performance and scalability.
114
115 The cursor allows rows to be iterated over and displayed.
116
117 Run the script::
118
119 python query.py
120
121 The output is::
122
123 ('Peter', 'Hall')
124 ('Peter', 'Tucker')
125
126 Examples and Tutorials
127 ----------------------
128
129 Runnable examples are in the `GitHub samples directory
130 <https://github.com/oracle/python-cx_Oracle/tree/master/samples>`__. A `Python
131 cx_Oracle tutorial
132 <https://oracle.github.io/python-cx_Oracle/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html>`__
133 is also available.
0 .. _jsondatatype:
1
2 *******************************
3 Working with the JSON Data Type
4 *******************************
5
6 Native support for JSON data was introduced in Oracle Database 12c. You can
7 use JSON with relational database features, including transactions, indexing,
8 declarative querying, and views. You can project JSON data relationally,
9 making it available for relational processes and tools. Also see
10 :ref:`Simple Oracle Document Access (SODA) <sodausermanual>`, which allows
11 access to JSON documents through a set of NoSQL-style APIs.
12
13 Prior to Oracle Database 21, JSON in relational tables is stored as BLOB, CLOB
14 or VARCHAR2 data, allowing easy access with cx_Oracle. Oracle Database 21
15 introduced a dedicated JSON data type with a new `binary storage format
16 <https://blogs.oracle.com/jsondb/osonformat>`__ that improves performance and
17 functionality. To use the new dedicated JSON type, the Oracle Database and
18 Oracle Client libraries must be version 21, or later. Also cx_Oracle must be
19 8.1, or later.
20
21 For more information about using JSON in Oracle Database see the
22 `Database JSON Developer's Guide
23 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN>`__.
24
25 In Oracle Database 21, to create a table with a column called ``JSON_DATA`` for
26 JSON data:
27
28 .. code-block:: sql
29
30 create table customers (
31 id integer not null primary key,
32 json_data json
33 );
34
35 For older Oracle Database versions the syntax is:
36
37 .. code-block:: sql
38
39 create table customers (
40 id integer not null primary key,
41 json_data blob check (json_data is json)
42 );
43
44 The check constraint with the clause ``IS JSON`` ensures only JSON data is
45 stored in that column.
46
47 The older syntax can still be used in Oracle Database 21, however the
48 recommendation is to move to the new JSON type. With the old syntax, the
49 storage can be BLOB, CLOB or VARCHAR2. Of these, BLOB is preferred to avoid
50 character set conversion overheads.
51
52 Using Oracle Database 21 and Oracle Client 21 with cx_Oracle 8.1 (or later),
53 you can insert by binding as shown below:
54
55 .. code-block:: python
56
57 import datetime
58
59 json_data = [
60 2.78,
61 True,
62 'Ocean Beach',
63 b'Some bytes',
64 {'keyA': 1, 'KeyB': 'Melbourne'},
65 datetime.date.today()
66 ]
67
68 var = cursor.var(cx_Oracle.DB_TYPE_JSON)
69 var.setvalue(0, json_data)
70 cursor.execute("insert into customers values (:1, :2)", [123, var])
71
72 # or these two lines can replace the three previous lines
73 cursor.setinputsizes(None, cx_Oracle.DB_TYPE_JSON)
74 cursor.execute("insert into customers values (:1, :2)", [123, json_data])
75
76 Fetching with:
77
78 .. code-block:: python
79
80 for row in cursor.execute("SELECT c.json_data FROM customers c"):
81 print(row)
82
83 gives output like::
84
85 ([Decimal('2.78'), True, 'Ocean Beach',
86 b'Some bytes',
87 {'keyA': Decimal('1'), 'KeyB': 'Melbourne'},
88 datetime.datetime(2020, 12, 2, 0, 0)],)
89
90 With the older BLOB storage, or to insert JSON strings, use:
91
92 .. code-block:: python
93
94 import json
95
96 customer_data = dict(name="Rod", dept="Sales", location="Germany")
97 cursor.execute("insert into customers (id, json_data) values (:1, :2)",
98 [1, json.dumps(customer_data)])
99
100
101 IN Bind Type Mapping
102 ====================
103
104 When binding to a JSON value, the type parameter for the variable must be
105 specified as :data:`cx_Oracle.DB_TYPE_JSON`. Python values are converted to
106 JSON values as shown in the following table. The 'SQL Equivalent' syntax can
107 be used in SQL INSERT and UPDATE statements if specific attribute types are
108 needed but there is no direct mapping from Python.
109
110 .. list-table::
111 :header-rows: 1
112 :widths: 1 1 1
113 :align: left
114
115 * - Python Type or Value
116 - JSON Attribute Type or Value
117 - SQL Equivalent Example
118 * - None
119 - null
120 - NULL
121 * - True
122 - true
123 - n/a
124 * - False
125 - false
126 - n/a
127 * - int
128 - NUMBER
129 - json_scalar(1)
130 * - float
131 - NUMBER
132 - json_scalar(1)
133 * - decimal.Decimal
134 - NUMBER
135 - json_scalar(1)
136 * - str
137 - VARCHAR2
138 - json_scalar('String')
139 * - datetime.date
140 - TIMESTAMP
141 - json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD'))
142 * - datetime.datetime
143 - TIMESTAMP
144 - json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD'))
145 * - bytes
146 - RAW
147 - json_scalar(utl_raw.cast_to_raw('A raw value'))
148 * - list
149 - Array
150 - json_array(1, 2, 3 returning json)
151 * - dict
152 - Object
153 - json_object(key 'Fred' value json_scalar(5), key 'George' value json_scalar('A string') returning json)
154 * - n/a
155 - CLOB
156 - json_scalar(to_clob('A short CLOB'))
157 * - n/a
158 - BLOB
159 - json_scalar(to_blob(utl_raw.cast_to_raw('A short BLOB')))
160 * - n/a
161 - DATE
162 - json_scalar(to_date('2020-03-10', 'YYYY-MM-DD'))
163 * - n/a
164 - INTERVAL YEAR TO MONTH
165 - json_scalar(to_yminterval('+5-9'))
166 * - n/a
167 - INTERVAL DAY TO SECOND
168 - json_scalar(to_dsinterval('P25DT8H25M'))
169 * - n/a
170 - BINARY_DOUBLE
171 - json_scalar(to_binary_double(25))
172 * - n/a
173 - BINARY_FLOAT
174 - json_scalar(to_binary_float(15.5))
175
176 An example of creating a CLOB attribute with key ``mydocument`` in a JSON column
177 using SQL is:
178
179 .. code-block:: python
180
181 cursor.execute("""
182 insert into mytab (myjsoncol) values
183 (json_object(key 'mydocument' value json_scalar(to_clob(:b))
184 returning json))""",
185 ['A short CLOB'])
186
187 When `mytab` is queried in cx_Oracle, the CLOB data will be returned as a
188 Python string, as shown by the following table. Output might be like::
189
190 {mydocument: 'A short CLOB'}
191
192
193 Query and OUT Bind Type Mapping
194 ===============================
195
196 When getting Oracle Database 21 JSON values from the database, the following
197 attribute mapping occurs:
198
199 .. list-table::
200 :header-rows: 1
201 :widths: 1 1
202 :align: left
203
204 * - Database JSON Attribute Type or Value
205 - Python Type or Value
206 * - null
207 - None
208 * - false
209 - False
210 * - true
211 - True
212 * - NUMBER
213 - decimal.Decimal
214 * - VARCHAR2
215 - str
216 * - RAW
217 - bytes
218 * - CLOB
219 - str
220 * - BLOB
221 - bytes
222 * - DATE
223 - datetime.datetime
224 * - TIMESTAMP
225 - datetime.datetime
226 * - INTERVAL YEAR TO MONTH
227 - not supported
228 * - INTERVAL DAY TO SECOND
229 - datetime.timedelta
230 * - BINARY_DOUBLE
231 - float
232 * - BINARY_FLOAT
233 - float
234 * - Arrays
235 - list
236 * - Objects
237 - dict
238
239 SQL/JSON Path Expressions
240 =========================
241
242 Oracle Database provides SQL access to JSON data using SQL/JSON path
243 expressions. A path expression selects zero or more JSON values that match, or
244 satisfy, it. Path expressions can use wildcards and array ranges. A simple
245 path expression is ``$.friends`` which is the value of the JSON field
246 ``friends``.
247
248 For example, the previously created ``customers`` table with JSON column
249 ``json_data`` can be queried like:
250
251 .. code-block:: sql
252
253 select c.json_data.location FROM customers c
254
255 With the JSON ``'{"name":"Rod","dept":"Sales","location":"Germany"}'`` stored
256 in the table, the queried value would be ``Germany``.
257
258 The JSON_EXISTS functions tests for the existence of a particular value within
259 some JSON data. To look for JSON entries that have a ``location`` field:
260
261 .. code-block:: python
262
263 for blob, in cursor.execute("""
264 select json_data
265 from customers
266 where json_exists(json_data, '$.location')"""):
267 data = json.loads(blob.read())
268 print(data)
269
270 This query might display::
271
272 {'name': 'Rod', 'dept': 'Sales', 'location': 'Germany'}
273
274 The SQL/JSON functions ``JSON_VALUE`` and ``JSON_QUERY`` can also be used.
275
276 Note that the default error-handling behavior for these functions is
277 ``NULL ON ERROR``, which means that no value is returned if an error occurs.
278 To ensure that an error is raised, use ``ERROR ON ERROR``.
279
280 For more information, see `SQL/JSON Path Expressions
281 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
282 id=GUID-2DC05D71-3D62-4A14-855F-76E054032494>`__
283 in the Oracle JSON Developer's Guide.
284
285
286 Accessing Relational Data as JSON
287 =================================
288
289 In Oracle Database 12.2, or later, the `JSON_OBJECT
290 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-1EF347AE-7FDA-4B41-AFE0-DD5A49E8B370>`__
291 function is a great way to convert relational table data to JSON:
292
293 .. code-block:: python
294
295 cursor.execute("""
296 select json_object('deptId' is d.department_id, 'name' is d.department_name) department
297 from departments d
298 where department_id < :did
299 order by d.department_id""",
300 [50]);
301 for row in cursor:
302 print(row)
303
304 This produces::
305
306 ('{"deptId":10,"name":"Administration"}',)
307 ('{"deptId":20,"name":"Marketing"}',)
308 ('{"deptId":30,"name":"Purchasing"}',)
309 ('{"deptId":40,"name":"Human Resources"}',)
0 .. _lobdata:
1
2 ************************
3 Using CLOB and BLOB Data
4 ************************
5
6 Oracle Database uses :ref:`lobobj` to store large data such as text, images,
7 videos and other multimedia formats. The maximum size of a LOB is limited to
8 the size of the tablespace storing it.
9
10 There are four types of LOB (large object):
11
12 * BLOB - Binary Large Object, used for storing binary data. cx_Oracle uses
13 the type :attr:`cx_Oracle.DB_TYPE_BLOB`.
14 * CLOB - Character Large Object, used for string strings in the database
15 character set format. cx_Oracle uses the type
16 :attr:`cx_Oracle.DB_TYPE_CLOB`.
17 * NCLOB - National Character Large Object, used for string strings in the
18 national character set format. cx_Oracle uses the type
19 :attr:`cx_Oracle.DB_TYPE_NCLOB`.
20 * BFILE - External Binary File, used for referencing a file stored on the
21 host operating system outside of the database. cx_Oracle uses the type
22 :attr:`cx_Oracle.DB_TYPE_BFILE`.
23
24 LOBs can be streamed to, and from, Oracle Database.
25
26 LOBs up to 1 GB in length can be also be handled directly as strings or bytes in
27 cx_Oracle. This makes LOBs easy to work with, and has significant performance
28 benefits over streaming. However it requires the entire LOB data to be present
29 in Python memory, which may not be possible.
30
31 See `GitHub <https://github.com/oracle/python-cx_Oracle/tree/master/samples>`__ for LOB examples.
32
33
34 Simple Insertion of LOBs
35 ------------------------
36
37 Consider a table with CLOB and BLOB columns:
38
39 .. code-block:: sql
40
41 CREATE TABLE lob_tbl (
42 id NUMBER,
43 c CLOB,
44 b BLOB
45 );
46
47 With cx_Oracle, LOB data can be inserted in the table by binding strings or
48 bytes as needed:
49
50 .. code-block:: python
51
52 with open('example.txt', 'r') as f:
53 textdata = f.read()
54
55 with open('image.png', 'rb') as f:
56 imgdata = f.read()
57
58 cursor.execute("""
59 insert into lob_tbl (id, c, b)
60 values (:lobid, :clobdata, :blobdata)""",
61 lobid=10, clobdata=textdata, blobdata=imgdata)
62
63 Note that with this approach, LOB data is limited to 1 GB in size.
64
65 .. _directlobs:
66
67 Fetching LOBs as Strings and Bytes
68 ----------------------------------
69
70 CLOBs and BLOBs smaller than 1 GB can queried from the database directly as
71 strings and bytes. This can be much faster than streaming.
72
73 A :attr:`Connection.outputtypehandler` or :attr:`Cursor.outputtypehandler` needs
74 to be used as shown in this example:
75
76 .. code-block:: python
77
78 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
79 if defaultType == cx_Oracle.DB_TYPE_CLOB:
80 return cursor.var(cx_Oracle.DB_TYPE_LONG, arraysize=cursor.arraysize)
81 if defaultType == cx_Oracle.DB_TYPE_BLOB:
82 return cursor.var(cx_Oracle.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
83
84 idVal = 1
85 textData = "The quick brown fox jumps over the lazy dog"
86 bytesData = b"Some binary data"
87 cursor.execute("insert into lob_tbl (id, c, b) values (:1, :2, :3)",
88 [idVal, textData, bytesData])
89
90 connection.outputtypehandler = OutputTypeHandler
91 cursor.execute("select c, b from lob_tbl where id = :1", [idVal])
92 clobData, blobData = cursor.fetchone()
93 print("CLOB length:", len(clobData))
94 print("CLOB data:", clobData)
95 print("BLOB length:", len(blobData))
96 print("BLOB data:", blobData)
97
98 This displays::
99
100 CLOB length: 43
101 CLOB data: The quick brown fox jumps over the lazy dog
102 BLOB length: 16
103 BLOB data: b'Some binary data'
104
105
106 Streaming LOBs (Read)
107 ---------------------
108
109 Without the output type handler, the CLOB and BLOB values are fetched as
110 :ref:`LOB objects<lobobj>`. The size of the LOB object can be obtained by
111 calling :meth:`LOB.size()` and the data can be read by calling
112 :meth:`LOB.read()`:
113
114 .. code-block:: python
115
116 idVal = 1
117 textData = "The quick brown fox jumps over the lazy dog"
118 bytesData = b"Some binary data"
119 cursor.execute("insert into lob_tbl (id, c, b) values (:1, :2, :3)",
120 [idVal, textData, bytesData])
121
122 cursor.execute("select b, c from lob_tbl where id = :1", [idVal])
123 b, c = cursor.fetchone()
124 print("CLOB length:", c.size())
125 print("CLOB data:", c.read())
126 print("BLOB length:", b.size())
127 print("BLOB data:", b.read())
128
129 This approach produces the same results as the previous example but it will
130 perform more slowly because it requires more :ref:`round-trips <roundtrips>` to
131 Oracle Database and has higher overhead. It is needed, however, if the LOB data
132 cannot be fetched as one block of data from the server.
133
134 To stream the BLOB column, the :meth:`LOB.read()` method can be called
135 repeatedly until all of the data has been read, as shown below:
136
137 .. code-block:: python
138
139 cursor.execute("select b from lob_tbl where id = :1", [10])
140 blob, = cursor.fetchone()
141 offset = 1
142 numBytesInChunk = 65536
143 with open("image.png", "wb") as f:
144 while True:
145 data = blob.read(offset, numBytesInChunk)
146 if data:
147 f.write(data)
148 if len(data) < numBytesInChunk:
149 break
150 offset += len(data)
151
152
153 Streaming LOBs (Write)
154 ----------------------
155
156 If a row containing a LOB is being inserted or updated, and the quantity of
157 data that is to be inserted or updated cannot fit in a single block of data,
158 the data can be streamed using the method :meth:`LOB.write()` instead as shown
159 in the following code:
160
161 .. code-block:: python
162
163 idVal = 9
164 lobVar = cursor.var(cx_Oracle.DB_TYPE_BLOB)
165 cursor.execute("""
166 insert into lob_tbl (id, b)
167 values (:1, empty_blob())
168 returning b into :2""", [idVal, lobVar])
169 blob, = lobVar.getvalue()
170 offset = 1
171 numBytesInChunk = 65536
172 with open("image.png", "rb") as f:
173 while True:
174 data = f.read(numBytesInChunk)
175 if data:
176 blob.write(data, offset)
177 if len(data) < numBytesInChunk:
178 break
179 offset += len(data)
180 connection.commit()
181
182
183 Temporary LOBs
184 --------------
185
186 All of the examples shown thus far have made use of permanent LOBs. These are
187 LOBs that are stored in the database. Oracle also supports temporary LOBs that
188 are not stored in the database but can be used to pass large quantities of
189 data. These LOBs use space in the temporary tablespace until all variables
190 referencing them go out of scope or the connection in which they are created is
191 explicitly closed.
192
193 When calling PL/SQL procedures with data that exceeds 32,767 bytes in length,
194 cx_Oracle automatically creates a temporary LOB internally and passes that
195 value through to the procedure. If the data that is to be passed to the
196 procedure exceeds that which can fit in a single block of data, however, you
197 can use the method :meth:`Connection.createlob()` to create a temporary LOB.
198 This LOB can then be read and written just like in the examples shown above for
199 persistent LOBs.
0 .. _plsqlexecution:
1
2 ****************
3 PL/SQL Execution
4 ****************
5
6 PL/SQL stored procedures, functions and anonymous blocks can be called from
7 cx_Oracle.
8
9 .. _plsqlproc:
10
11 PL/SQL Stored Procedures
12 ------------------------
13
14 The :meth:`Cursor.callproc()` method is used to call PL/SQL procedures.
15
16 If a procedure with the following definition exists:
17
18 .. code-block:: sql
19
20 create or replace procedure myproc (
21 a_Value1 number,
22 a_Value2 out number
23 ) as
24 begin
25 a_Value2 := a_Value1 * 2;
26 end;
27
28 then the following Python code can be used to call it:
29
30 .. code-block:: python
31
32 outVal = cursor.var(int)
33 cursor.callproc('myproc', [123, outVal])
34 print(outVal.getvalue()) # will print 246
35
36 Calling :meth:`Cursor.callproc()` actually generates an anonymous PL/SQL block
37 as shown below, which is then executed:
38
39 .. code-block:: python
40
41 cursor.execute("begin myproc(:1,:2); end;", [123, outval])
42
43 See :ref:`bind` for information on binding.
44
45
46 .. _plsqlfunc:
47
48 PL/SQL Stored Functions
49 -----------------------
50
51 The :meth:`Cursor.callfunc()` method is used to call PL/SQL functions.
52
53 The ``returnType`` parameter for :meth:`~Cursor.callfunc()` is
54 expected to be a Python type, one of the :ref:`cx_Oracle types <types>` or
55 an :ref:`Object Type <objecttype>`.
56
57 If a function with the following definition exists:
58
59 .. code-block:: sql
60
61 create or replace function myfunc (
62 a_StrVal varchar2,
63 a_NumVal number
64 ) return number as
65 begin
66 return length(a_StrVal) + a_NumVal * 2;
67 end;
68
69 then the following Python code can be used to call it:
70
71 .. code-block:: python
72
73 returnVal = cursor.callfunc("myfunc", int, ["a string", 15])
74 print(returnVal) # will print 38
75
76 A more complex example that returns a spatial (SDO) object can be seen below.
77 First, the SQL statements necessary to set up the example:
78
79 .. code-block:: sql
80
81 create table MyPoints (
82 id number(9) not null,
83 point sdo_point_type not null
84 );
85
86 insert into MyPoints values (1, sdo_point_type(125, 375, 0));
87
88 create or replace function spatial_queryfn (
89 a_Id number
90 ) return sdo_point_type is
91 t_Result sdo_point_type;
92 begin
93 select point
94 into t_Result
95 from MyPoints
96 where Id = a_Id;
97
98 return t_Result;
99 end;
100 /
101
102 The Python code that will call this procedure looks as follows:
103
104 .. code-block:: python
105
106 objType = connection.gettype("SDO_POINT_TYPE")
107 cursor = connection.cursor()
108 returnVal = cursor.callfunc("spatial_queryfn", objType, [1])
109 print("(%d, %d, %d)" % (returnVal.X, returnVal.Y, returnVal.Z))
110 # will print (125, 375, 0)
111
112 See :ref:`bind` for information on binding.
113
114
115 Anonymous PL/SQL Blocks
116 -----------------------
117
118 An anonymous PL/SQL block can be called as shown:
119
120 .. code-block:: python
121
122 var = cursor.var(int)
123 cursor.execute("""
124 begin
125 :outVal := length(:inVal);
126 end;""", inVal="A sample string", outVal=var)
127 print(var.getvalue()) # will print 15
128
129 See :ref:`bind` for information on binding.
130
131
132 Creating Stored Procedures and Packages
133 ---------------------------------------
134
135 To create PL/SQL stored procedures and packages, use :meth:`Cursor.execute()`
136 with a SQL CREATE command.
137
138 Creation warning messages can be found from database views like USER_ERRORS.
139
140 For example, creating a procedure with an error could be like:
141
142 .. code-block:: python
143
144 with connection.cursor() as cursor:
145 cursor.execute("""
146 create or replace procedure badproc (a in number) as
147 begin
148 WRONG WRONG WRONG
149 end;""")
150 cursor.execute("""
151 select line, position, text
152 from user_errors
153 where name = 'BADPROC' and type = 'PROCEDURE'
154 order by name, type, line, position""")
155 errors = cursor.fetchall()
156 if errors:
157 for info in errors:
158 print("Error at line {} position {}:\n{}".format(*info))
159 else:
160 print("Created successfully")
161
162 The output would be::
163
164 PLS-00103: Encountered the symbol "WRONG" when expecting one of the following:
165
166 := . ( @ % ;
167
168
169 Using DBMS_OUTPUT
170 -----------------
171
172 The standard way to print output from PL/SQL is with the package `DBMS_OUTPUT
173 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
174 id=GUID-C1400094-18D5-4F36-A2C9-D28B0E12FD8C>`__. Note, PL/SQL code that uses
175 ``DBMS_OUTPUT`` runs to completion before any output is available to the user.
176 Also, other database connections cannot access the buffer.
177
178 To use DBMS_OUTPUT:
179
180 * Call the PL/SQL procedure ``DBMS_OUTPUT.ENABLE()`` to enable output to be
181 buffered for the connection.
182 * Execute some PL/SQL that calls ``DBMS_OUTPUT.PUT_LINE()`` to put text in the
183 buffer.
184 * Call ``DBMS_OUTPUT.GET_LINE()`` or ``DBMS_OUTPUT.GET_LINES()`` repeatedly to
185 fetch the text from the buffer until there is no more output.
186
187
188 For example:
189
190 .. code-block:: python
191
192 # enable DBMS_OUTPUT
193 cursor.callproc("dbms_output.enable")
194
195 # execute some PL/SQL that calls DBMS_OUTPUT.PUT_LINE
196 cursor.execute("""
197 begin
198 dbms_output.put_line('This is the cx_Oracle manual');
199 dbms_output.put_line('Demonstrating how to use DBMS_OUTPUT');
200 end;""")
201
202 # tune this size for your application
203 chunk_size = 100
204
205 # create variables to hold the output
206 lines_var = cursor.arrayvar(str, chunk_size)
207 num_lines_var = cursor.var(int)
208 num_lines_var.setvalue(0, chunk_size)
209
210 # fetch the text that was added by PL/SQL
211 while True:
212 cursor.callproc("dbms_output.get_lines", (lines_var, num_lines_var))
213 num_lines = num_lines_var.getvalue()
214 lines = lines_var.getvalue()[:num_lines]
215 for line in lines:
216 print(line or "")
217 if num_lines < chunk_size:
218 break
219
220 This will produce the following output::
221
222 This is the cx_Oracle manual
223 Demonstrating use of DBMS_OUTPUT
224
225 An alternative is to call ``DBMS_OUTPUT.GET_LINE()`` once per output line, which
226 may be much slower:
227
228 .. code-block:: python
229
230 textVar = cursor.var(str)
231 statusVar = cursor.var(int)
232 while True:
233 cursor.callproc("dbms_output.get_line", (textVar, statusVar))
234 if statusVar.getvalue() != 0:
235 break
236 print(textVar.getvalue())
237
238 Implicit results
239 ----------------
240
241 Implicit results permit a Python program to consume cursors returned by a
242 PL/SQL block without the requirement to use OUT REF CURSOR parameters. The
243 method :meth:`Cursor.getimplicitresults()` can be used for this purpose. It
244 requires both the Oracle Client and Oracle Database to be 12.1 or higher.
245
246 An example using implicit results is as shown:
247
248 .. code-block:: python
249
250 cursor.execute("""
251 declare
252 cust_cur sys_refcursor;
253 sales_cur sys_refcursor;
254 begin
255 open cust_cur for SELECT * FROM cust_table;
256 dbms_sql.return_result(cust_cur);
257
258 open sales_cur for SELECT * FROM sales_table;
259 dbms_sql.return_result(sales_cur);
260 end;""")
261
262 for implicitCursor in cursor.getimplicitresults():
263 for row in implicitCursor:
264 print(row)
265
266 Data from both the result sets are returned::
267
268 (1, 'Tom')
269 (2, 'Julia')
270 (1000, 1, 'BOOKS')
271 (2000, 2, 'FURNITURE')
272
273 .. _ebr:
274
275 Edition-Based Redefinition (EBR)
276 --------------------------------
277
278 Oracle Database's `Edition-Based Redefinition
279 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
280 id=GUID-58DE05A0-5DEF-4791-8FA8-F04D11964906>`__ feature enables upgrading of
281 the database component of an application while it is in use, thereby minimizing
282 or eliminating down time. This feature allows multiple versions of views,
283 synonyms, PL/SQL objects and SQL Translation profiles to be used concurrently.
284 Different versions of the database objects are associated with an "edition".
285
286 The simplest way to set an edition is to pass the ``edition`` parameter to
287 :meth:`cx_Oracle.connect()` or :meth:`cx_Oracle.SessionPool()`:
288
289 .. code-block:: python
290
291 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
292 edition="newsales", encoding="UTF-8")
293
294
295 The edition could also be set by setting the environment variable
296 ``ORA_EDITION`` or by executing the SQL statement:
297
298 .. code-block:: sql
299
300 alter session set edition = <edition name>;
301
302 Regardless of which method is used to set the edition, the value that is in use
303 can be seen by examining the attribute :attr:`Connection.edition`. If no value
304 has been set, the value will be None. This corresponds to the database default
305 edition ``ORA$BASE``.
306
307 Consider an example where one version of a PL/SQL function ``Discount`` is
308 defined in the database default edition ``ORA$BASE`` and the other version of
309 the same function is defined in a user created edition ``DEMO``.
310
311 .. code-block:: sql
312
313 connect <username>/<password>
314
315 -- create function using the database default edition
316 CREATE OR REPLACE FUNCTION Discount(price IN NUMBER) RETURN NUMBER IS
317 BEGIN
318 return price * 0.9;
319 END;
320 /
321
322 A new edition named 'DEMO' is created and the user given permission to use
323 editions. The use of ``FORCE`` is required if the user already contains one or
324 more objects whose type is editionable and that also have non-editioned
325 dependent objects.
326
327 .. code-block:: sql
328
329 connect system/<password>
330
331 CREATE EDITION demo;
332 ALTER USER <username> ENABLE EDITIONS FORCE;
333 GRANT USE ON EDITION demo to <username>;
334
335 The ``Discount`` function for the demo edition is as follows:
336
337 .. code-block:: sql
338
339 connect <username>/<password>
340
341 alter session set edition = demo;
342
343 -- Function for the demo edition
344 CREATE OR REPLACE FUNCTION Discount(price IN NUMBER) RETURN NUMBER IS
345 BEGIN
346 return price * 0.5;
347 END;
348 /
349
350 The Python application can then call the required version of the PL/SQL
351 function as shown:
352
353 .. code-block:: python
354
355 connection = cx_Oracle.connect(<username>, <password>, "dbhost.example.com/orclpdb1",
356 encoding="UTF-8")
357 print("Edition is:", repr(connection.edition))
358
359 cursor = connection.cursor()
360 discountedPrice = cursor.callfunc("Discount", int, [100])
361 print("Price after discount is:", discountedPrice)
362
363 # Use the edition parameter for the connection
364 connection = cx_Oracle.connect(<username>, <password>, "dbhost.example.com/orclpdb1",
365 edition = "demo", encoding="UTF-8")
366 print("Edition is:", repr(connection.edition))
367
368 cursor = connection.cursor()
369 discountedPrice = cursor.callfunc("Discount", int, [100])
370 print("Price after discount is:", discountedPrice)
371
372 The output of the function call for the default and demo edition is as shown::
373
374 Edition is: None
375 Price after discount is: 90
376 Edition is: 'DEMO'
377 Price after discount is: 50
0 .. _sodausermanual:
1
2 ************************************
3 Simple Oracle Document Access (SODA)
4 ************************************
5
6 Overview
7 ========
8
9 Oracle Database Simple Oracle Document Access (SODA) allows documents to be
10 inserted, queried, and retrieved from Oracle Database using a set of
11 NoSQL-style cx_Oracle methods. Documents are generally JSON data but they can
12 be any data at all (including video, images, sounds, or other binary content).
13 Documents can be fetched from the database by key lookup or by using
14 query-by-example (QBE) pattern-matching.
15
16 SODA uses a SQL schema to store documents but you do not need to know SQL or
17 how the documents are stored. However, access via SQL does allow use of
18 advanced Oracle Database functionality such as analytics for reporting.
19
20 Oracle SODA implementations are also available in `Node.js
21 <https://oracle.github.io/node-oracledb/doc/api.html#sodaoverview>`__, `Java
22 <https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/java/adsda/index.html>`__,
23 `PL/SQL <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADSDP>`__,
24 `Oracle Call Interface
25 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-23206C89-891E-43D7-827C-5C6367AD62FD>`__
26 and via `REST
27 <https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/rest/index.html>`__.
28
29 For general information on SODA, see the `SODA home page
30 <https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/index.html>`__
31 and the Oracle Database `Introduction to Simple Oracle Document Access (SODA)
32 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADSDI>`__ manual.
33
34 For specified requirements see the cx_Oracle :ref:`SODA requirements <sodarequirements>`.
35
36 cx_Oracle uses the following objects for SODA:
37
38 * :ref:`SODA Database Object <sodadb>`: The top level object for cx_Oracle SODA
39 operations. This is acquired from an Oracle Database connection. A 'SODA
40 database' is an abstraction, allowing access to SODA collections in that
41 'SODA database', which then allow access to documents in those collections.
42 A SODA database is analogous to an Oracle Database user or schema, a
43 collection is analogous to a table, and a document is analogous to a table
44 row with one column for a unique document key, a column for the document
45 content, and other columns for various document attributes.
46
47 * :ref:`SODA Collection Object <sodacoll>`: Represents a collection of SODA
48 documents. By default, collections allow JSON documents to be stored. This
49 is recommended for most SODA users. However optional metadata can set
50 various details about a collection, such as its database storage, whether it
51 should track version and time stamp document components, how such components
52 are generated, and what document types are supported. By default, the name of
53 the Oracle Database table storing a collection is the same as the collection
54 name. Note: do not use SQL to drop the database table, since SODA metadata
55 will not be correctly removed. Use the :meth:`SodaCollection.drop()` method
56 instead.
57
58 * :ref:`SODA Document Object <sodadoc>`: Represents a document. Typically the
59 document content will be JSON. The document has properties including the
60 content, a key, timestamps, and the media type. By default, document keys
61 are automatically generated. See :ref:`SODA Document objects <sodadoc>` for
62 the forms of SodaDoc.
63
64 * :ref:`SODA Document Cursor <sodadoccur>`: A cursor object representing the
65 result of the :meth:`SodaOperation.getCursor()` method from a
66 :meth:`SodaCollection.find()` operation. It can be iterated over to access
67 each SodaDoc.
68
69 * :ref:`SODA Operation Object <sodaop>`: An internal object used with
70 :meth:`SodaCollection.find()` to perform read and write operations on
71 documents. Chained methods set properties on a SodaOperation object which is
72 then used by a terminal method to find, count, replace, or remove documents.
73 This is an internal object that should not be directly accessed.
74
75 SODA Examples
76 =============
77
78 Creating and adding documents to a collection can be done as follows:
79
80 .. code-block:: python
81
82 soda = connection.getSodaDatabase()
83
84 # create a new SODA collection; this will open an existing collection, if
85 # the name is already in use
86 collection = soda.createCollection("mycollection")
87
88 # insert a document into the collection; for the common case of a JSON
89 # document, the content can be a simple Python dictionary which will
90 # internally be converted to a JSON document
91 content = {'name': 'Matilda', 'address': {'city': 'Melbourne'}}
92 returnedDoc = collection.insertOneAndGet(content)
93 key = returnedDoc.key
94 print('The key of the new SODA document is: ', key)
95
96 By default, a system generated key is created when documents are inserted.
97 With a known key, you can retrieve a document:
98
99 .. code-block:: python
100
101 # this will return a dictionary (as was inserted in the previous code)
102 content = collection.find().key(key).getOne().getContent()
103 print(content)
104
105 You can also search for documents using query-by-example syntax:
106
107 .. code-block:: python
108
109 # Find all documents with names like 'Ma%'
110 print("Names matching 'Ma%'")
111 qbe = {'name': {'$like': 'Ma%'}}
112 for doc in collection.find().filter(qbe).getDocuments():
113 content = doc.getContent()
114 print(content["name"])
115
116 See the `samples directory
117 <https://github.com/oracle/python-cx_Oracle/tree/master/samples>`__
118 for runnable SODA examples.
119
120 --------------------
121 Committing SODA Work
122 --------------------
123
124 The general recommendation for SODA applications is to turn on
125 :attr:`~Connection.autocommit` globally:
126
127 .. code-block:: python
128
129 connection.autocommit = True
130
131 If your SODA document write operations are mostly independent of each other,
132 this removes the overhead of application transaction management and the need for
133 explicit :meth:`Connection.commit()` calls.
134
135 When deciding how to commit transactions, beware of transactional consistency
136 and performance requirements. If you are using individual SODA calls to insert
137 or update a large number of documents with individual calls, you should turn
138 :attr:`~Connection.autocommit` off and issue a single, explicit
139 :meth:`~Connection.commit()` after all documents have been processed. Also
140 consider using :meth:`SodaCollection.insertMany()` or
141 :meth:`SodaCollection.insertManyAndGet()` which have performance benefits.
142
143 If you are not autocommitting, and one of the SODA operations in your
144 transaction fails, then previous uncommitted operations will not be rolled back.
145 Your application should explicitly roll back the transaction with
146 :meth:`Connection.rollback()` to prevent any later commits from committing a
147 partial transaction.
148
149 Note:
150
151 - SODA DDL operations do not commit an open transaction the way that SQL always does for DDL statements.
152 - When :attr:`~Connection.autocommit` is ``True``, most SODA methods will issue a commit before successful return.
153 - SODA provides optimistic locking, see :meth:`SodaOperation.version()`.
154 - When mixing SODA and relational access, any commit or rollback on the connection will affect all work.
0 .. _sqlexecution:
1
2 *************
3 SQL Execution
4 *************
5
6 Executing SQL statements is the primary way in which a Python application
7 communicates with Oracle Database. Statements are executed using the methods
8 :meth:`Cursor.execute()` or :meth:`Cursor.executemany()`. Statements include
9 queries, Data Manipulation Language (DML), and Data Definition Language (DDL).
10 A few other `specialty statements
11 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
12 id=GUID-E1749EF5-2264-44DF-99EF-AEBEB943BED6>`__ can also be executed.
13
14 PL/SQL statements are discussed in :ref:`plsqlexecution`. Other chapters
15 contain information on specific data types and features. See :ref:`batchstmnt`,
16 :ref:`lobdata`, :ref:`jsondatatype`, and :ref:`xmldatatype`.
17
18 cx_Oracle can be used to execute individual statements, one at a time. It does
19 not read SQL*Plus ".sql" files. To read SQL files, use a technique like the one
20 in ``RunSqlScript()`` in `samples/SampleEnv.py
21 <https://github.com/oracle/python-cx_Oracle/blob/master/samples/SampleEnv.py>`__
22
23 SQL statements should not contain a trailing semicolon (";") or forward slash
24 ("/"). This will fail:
25
26 .. code-block:: python
27
28 cur.execute("select * from MyTable;")
29
30 This is correct:
31
32 .. code-block:: python
33
34 cur.execute("select * from MyTable")
35
36
37 SQL Queries
38 ===========
39
40 Queries (statements beginning with SELECT or WITH) can only be executed using
41 the method :meth:`Cursor.execute()`. Rows can then be iterated over, or can be
42 fetched using one of the methods :meth:`Cursor.fetchone()`,
43 :meth:`Cursor.fetchmany()` or :meth:`Cursor.fetchall()`. There is a
44 :ref:`default type mapping <defaultfetchtypes>` to Python types that can be
45 optionally :ref:`overridden <outputtypehandlers>`.
46
47 .. IMPORTANT::
48
49 Interpolating or concatenating user data with SQL statements, for example
50 ``cur.execute("SELECT * FROM mytab WHERE mycol = '" + myvar + "'")``, is a security risk
51 and impacts performance. Use :ref:`bind variables <bind>` instead. For
52 example, ``cur.execute("SELECT * FROM mytab WHERE mycol = :mybv", mybv=myvar)``.
53
54 .. _fetching:
55
56 Fetch Methods
57 -------------
58
59 After :meth:`Cursor.execute()`, the cursor is returned as a convenience. This
60 allows code to iterate over rows like:
61
62 .. code-block:: python
63
64 cur = connection.cursor()
65 for row in cur.execute("select * from MyTable"):
66 print(row)
67
68 Rows can also be fetched one at a time using the method
69 :meth:`Cursor.fetchone()`:
70
71 .. code-block:: python
72
73 cur = connection.cursor()
74 cur.execute("select * from MyTable")
75 while True:
76 row = cur.fetchone()
77 if row is None:
78 break
79 print(row)
80
81 If rows need to be processed in batches, the method :meth:`Cursor.fetchmany()`
82 can be used. The size of the batch is controlled by the ``numRows`` parameter,
83 which defaults to the value of :attr:`Cursor.arraysize`.
84
85 .. code-block:: python
86
87 cur = connection.cursor()
88 cur.execute("select * from MyTable")
89 numRows = 10
90 while True:
91 rows = cur.fetchmany(numRows)
92 if not rows:
93 break
94 for row in rows:
95 print(row)
96
97 If all of the rows need to be fetched, and can be contained in memory, the
98 method :meth:`Cursor.fetchall()` can be used.
99
100 .. code-block:: python
101
102 cur = connection.cursor()
103 cur.execute("select * from MyTable")
104 rows = cur.fetchall()
105 for row in rows:
106 print(row)
107
108 The fetch methods return data as tuples. To return results as dictionaries, see
109 :ref:`rowfactories`.
110
111 Closing Cursors
112 ---------------
113
114 A cursor may be used to execute multiple statements. Once it is no longer
115 needed, it should be closed by calling :meth:`~Cursor.close()` in order to
116 reclaim resources in the database. It will be closed automatically when the
117 variable referencing it goes out of scope (and no further references are
118 retained). One other way to control the lifetime of a cursor is to use a "with"
119 block, which ensures that a cursor is closed once the block is completed. For
120 example:
121
122 .. code-block:: python
123
124 with connection.cursor() as cursor:
125 for row in cursor.execute("select * from MyTable"):
126 print(row)
127
128 This code ensures that, once the block is completed, the cursor is closed and
129 resources have been reclaimed by the database. In addition, any attempt to use
130 the variable ``cursor`` outside of the block will simply fail.
131
132 .. _querymetadata:
133
134 Query Column Metadata
135 ---------------------
136
137 After executing a query, the column metadata such as column names and data types
138 can be obtained using :attr:`Cursor.description`:
139
140 .. code-block:: python
141
142 cur = connection.cursor()
143 cur.execute("select * from MyTable")
144 for column in cur.description:
145 print(column)
146
147 This could result in metadata like::
148
149 ('ID', <class 'cx_Oracle.DB_TYPE_NUMBER'>, 39, None, 38, 0, 0)
150 ('NAME', <class 'cx_Oracle.DB_TYPE_VARCHAR'>, 20, 20, None, None, 1)
151
152
153 .. _defaultfetchtypes:
154
155 Fetch Data Types
156 ----------------
157
158 The following table provides a list of all of the data types that cx_Oracle
159 knows how to fetch. The middle column gives the type that is returned in the
160 :ref:`query metadata <querymetadata>`. The last column gives the type of
161 Python object that is returned by default. Python types can be changed with
162 :ref:`Output Type Handlers <outputtypehandlers>`.
163
164 .. list-table::
165 :header-rows: 1
166 :widths: 1 1 1
167 :align: left
168
169 * - Oracle Database Type
170 - cx_Oracle Database Type
171 - Default Python type
172 * - BFILE
173 - :attr:`cx_Oracle.DB_TYPE_BFILE`
174 - :ref:`cx_Oracle.LOB <lobobj>`
175 * - BINARY_DOUBLE
176 - :attr:`cx_Oracle.DB_TYPE_BINARY_DOUBLE`
177 - float
178 * - BINARY_FLOAT
179 - :attr:`cx_Oracle.DB_TYPE_BINARY_FLOAT`
180 - float
181 * - BLOB
182 - :attr:`cx_Oracle.DB_TYPE_BLOB`
183 - :ref:`cx_Oracle.LOB <lobobj>`
184 * - CHAR
185 - :attr:`cx_Oracle.DB_TYPE_CHAR`
186 - str
187 * - CLOB
188 - :attr:`cx_Oracle.DB_TYPE_CLOB`
189 - :ref:`cx_Oracle.LOB <lobobj>`
190 * - CURSOR
191 - :attr:`cx_Oracle.DB_TYPE_CURSOR`
192 - :ref:`cx_Oracle.Cursor <cursorobj>`
193 * - DATE
194 - :attr:`cx_Oracle.DB_TYPE_DATE`
195 - datetime.datetime
196 * - INTERVAL DAY TO SECOND
197 - :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS`
198 - datetime.timedelta
199 * - JSON
200 - :attr:`cx_Oracle.DB_TYPE_JSON`
201 - dict, list or a scalar value [4]_
202 * - LONG
203 - :attr:`cx_Oracle.DB_TYPE_LONG`
204 - str
205 * - LONG RAW
206 - :attr:`cx_Oracle.DB_TYPE_LONG_RAW`
207 - bytes
208 * - NCHAR
209 - :attr:`cx_Oracle.DB_TYPE_NCHAR`
210 - str
211 * - NCLOB
212 - :attr:`cx_Oracle.DB_TYPE_NCLOB`
213 - :ref:`cx_Oracle.LOB <lobobj>`
214 * - NUMBER
215 - :attr:`cx_Oracle.DB_TYPE_NUMBER`
216 - float or int [1]_
217 * - NVARCHAR2
218 - :attr:`cx_Oracle.DB_TYPE_NVARCHAR`
219 - str
220 * - OBJECT [3]_
221 - :attr:`cx_Oracle.DB_TYPE_OBJECT`
222 - :ref:`cx_Oracle.Object <objecttype>`
223 * - RAW
224 - :attr:`cx_Oracle.DB_TYPE_RAW`
225 - bytes
226 * - ROWID
227 - :attr:`cx_Oracle.DB_TYPE_ROWID`
228 - str
229 * - TIMESTAMP
230 - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP`
231 - datetime.datetime
232 * - TIMESTAMP WITH LOCAL TIME ZONE
233 - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ`
234 - datetime.datetime [2]_
235 * - TIMESTAMP WITH TIME ZONE
236 - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ`
237 - datetime.datetime [2]_
238 * - UROWID
239 - :attr:`cx_Oracle.DB_TYPE_ROWID`
240 - str
241 * - VARCHAR2
242 - :attr:`cx_Oracle.DB_TYPE_VARCHAR`
243 - str
244
245 .. [1] If the precision and scale obtained from query column metadata indicate
246 that the value can be expressed as an integer, the value will be
247 returned as an int. If the column is unconstrained (no precision and
248 scale specified), the value will be returned as a float or an int
249 depending on whether the value itself is an integer. In all other cases
250 the value is returned as a float.
251 .. [2] The timestamps returned are naive timestamps without any time zone
252 information present.
253 .. [3] These include all user-defined types such as VARRAY, NESTED TABLE, etc.
254
255 .. [4] If the JSON is an object, then a dict is returned. If it is an array,
256 then a list is returned. If it is a scalar value, then that particular
257 scalar value is returned.
258
259
260 .. _outputtypehandlers:
261
262 Changing Fetched Data Types with Output Type Handlers
263 -----------------------------------------------------
264
265 Sometimes the default conversion from an Oracle Database type to a Python type
266 must be changed in order to prevent data loss or to fit the purposes of the
267 Python application. In such cases, an output type handler can be specified for
268 queries. Output type handlers do not affect values returned from
269 :meth:`Cursor.callfunc()` or :meth:`Cursor.callproc()`.
270
271 Output type handlers can be specified on the :attr:`connection
272 <Connection.outputtypehandler>` or on the :attr:`cursor
273 <Cursor.outputtypehandler>`. If specified on the cursor, fetch type handling is
274 only changed on that particular cursor. If specified on the connection, all
275 cursors created by that connection will have their fetch type handling changed.
276
277 The output type handler is expected to be a function with the following
278 signature::
279
280 handler(cursor, name, defaultType, size, precision, scale)
281
282 The parameters are the same information as the query column metadata found in
283 :attr:`Cursor.description`. The function is called once for each column that is
284 going to be fetched. The function is expected to return a
285 :ref:`variable object <varobj>` (generally by a call to :func:`Cursor.var()`)
286 or the value ``None``. The value ``None`` indicates that the default type
287 should be used.
288
289 Examples of output handlers are shown in :ref:`numberprecision` and
290 :ref:`directlobs`. Also see samples such as `samples/TypeHandlers.py
291 <https://github.com/oracle/python-cx_Oracle/blob/master/samples/TypeHandlers.py>`__
292
293 .. _numberprecision:
294
295 Fetched Number Precision
296 ------------------------
297
298 One reason for using an output type handler is to ensure that numeric precision
299 is not lost when fetching certain numbers. Oracle Database uses decimal numbers
300 and these cannot be converted seamlessly to binary number representations like
301 Python floats. In addition, the range of Oracle numbers exceeds that of
302 floating point numbers. Python has decimal objects which do not have these
303 limitations and cx_Oracle knows how to perform the conversion between Oracle
304 numbers and Python decimal values if directed to do so.
305
306 The following code sample demonstrates the issue:
307
308 .. code-block:: python
309
310 cur = connection.cursor()
311 cur.execute("create table test_float (X number(5, 3))")
312 cur.execute("insert into test_float values (7.1)")
313 connection.commit()
314 cur.execute("select * from test_float")
315 val, = cur.fetchone()
316 print(val, "* 3 =", val * 3)
317
318 This displays ``7.1 * 3 = 21.299999999999997``
319
320 Using Python decimal objects, however, there is no loss of precision:
321
322 .. code-block:: python
323
324 import decimal
325
326 def NumberToDecimal(cursor, name, defaultType, size, precision, scale):
327 if defaultType == cx_Oracle.DB_TYPE_NUMBER:
328 return cursor.var(decimal.Decimal, arraysize=cursor.arraysize)
329
330 cur = connection.cursor()
331 cur.outputtypehandler = NumberToDecimal
332 cur.execute("select * from test_float")
333 val, = cur.fetchone()
334 print(val, "* 3 =", val * 3)
335
336 This displays ``7.1 * 3 = 21.3``
337
338 The Python ``decimal.Decimal`` converter gets called with the string
339 representation of the Oracle number. The output from ``decimal.Decimal`` is
340 returned in the output tuple.
341
342 See `samples/ReturnNumbersAsDecimals.py
343 <https://github.com/oracle/python-cx_Oracle/blob/master/samples/ReturnNumbersAsDecimals.py>`__
344
345
346 .. _outconverters:
347
348 Changing Query Results with Outconverters
349 -----------------------------------------
350
351 cx_Oracle "outconverters" can be used with :ref:`output type handlers
352 <outputtypehandlers>` to change returned data.
353
354 For example, to make queries return empty strings instead of NULLs:
355
356 .. code-block:: python
357
358 def OutConverter(value):
359 if value is None:
360 return ''
361 return value
362
363 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
364 if defaultType in (cx_Oracle.DB_TYPE_VARCHAR, cx_Oracle.DB_TYPE_CHAR):
365 return cursor.var(str, size, cur.arraysize, outconverter=OutConverter)
366
367 connection.outputtypehandler = OutputTypeHandler
368
369
370 .. _rowfactories:
371
372 Changing Query Results with Rowfactories
373 ----------------------------------------
374
375 cx_Oracle "rowfactories" are methods called for each row that is retrieved from
376 the database. The :meth:`Cursor.rowfactory` method is called with the tuple that
377 would normally be returned from the database. The method can convert the tuple
378 to a different value and return it to the application in place of the tuple.
379
380 For example, to fetch each row of a query as a dictionary:
381
382 .. code-block:: python
383
384 cursor.execute("select * from locations where location_id = 1000")
385 columns = [col[0] for col in cursor.description]
386 cursor.rowfactory = lambda *args: dict(zip(columns, args))
387 data = cursor.fetchone()
388 print(data)
389
390 The output is::
391
392 {'LOCATION_ID': 1000, 'STREET_ADDRESS': '1297 Via Cola di Rie', 'POSTAL_CODE': '00989', 'CITY': 'Roma', 'STATE_PROVINCE': None, 'COUNTRY_ID': 'IT'}
393
394 If you join tables where the same column name occurs in both tables with
395 different meanings or values, then use a column alias in the query. Otherwise
396 only one of the similarly named columns will be included in the dictionary:
397
398 .. code-block:: sql
399
400 select
401 cat_name,
402 cats.color as cat_color,
403 dog_name,
404 dogs.color
405 from cats, dogs
406
407 .. _scrollablecursors:
408
409 Scrollable Cursors
410 ------------------
411
412 Scrollable cursors enable applications to move backwards, forwards, to skip
413 rows, and to move to a particular row in a query result set. The result set is
414 cached on the database server until the cursor is closed. In contrast, regular
415 cursors are restricted to moving forward.
416
417 A scrollable cursor is created by setting the parameter ``scrollable=True``
418 when creating the cursor. The method :meth:`Cursor.scroll()` is used to move to
419 different locations in the result set.
420
421 Examples are:
422
423 .. code-block:: python
424
425 cursor = connection.cursor(scrollable=True)
426 cursor.execute("select * from ChildTable order by ChildId")
427
428 cursor.scroll(mode="last")
429 print("LAST ROW:", cursor.fetchone())
430
431 cursor.scroll(mode="first")
432 print("FIRST ROW:", cursor.fetchone())
433
434 cursor.scroll(8, mode="absolute")
435 print("ROW 8:", cursor.fetchone())
436
437 cursor.scroll(6)
438 print("SKIP 6 ROWS:", cursor.fetchone())
439
440 cursor.scroll(-4)
441 print("SKIP BACK 4 ROWS:", cursor.fetchone())
442
443 .. _fetchobjects:
444
445 Fetching Oracle Database Objects and Collections
446 ------------------------------------------------
447
448 Oracle Database named object types and user-defined types can be fetched
449 directly in queries. Each item is represented as a :ref:`Python object
450 <objecttype>` corresponding to the Oracle Database object. This Python object
451 can be traversed to access its elements. Attributes including
452 :attr:`ObjectType.name` and :attr:`ObjectType.iscollection`, and methods
453 including :meth:`Object.aslist` and :meth:`Object.asdict` are available.
454
455 For example, if a table ``mygeometrytab`` contains a column ``geometry`` of
456 Oracle's predefined Spatial object type `SDO_GEOMETRY
457 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-683FF8C5-A773-4018-932D-2AF6EC8BC119>`__,
458 then it can be queried and printed:
459
460 .. code-block:: python
461
462 cur.execute("select geometry from mygeometrytab")
463 for obj, in cur:
464 dumpobject(obj)
465
466 Where ``dumpobject()`` is defined as:
467
468 .. code-block:: python
469
470 def dumpobject(obj, prefix = ""):
471 if obj.type.iscollection:
472 print(prefix, "[")
473 for value in obj.aslist():
474 if isinstance(value, cx_Oracle.Object):
475 dumpobject(value, prefix + " ")
476 else:
477 print(prefix + " ", repr(value))
478 print(prefix, "]")
479 else:
480 print(prefix, "{")
481 for attr in obj.type.attributes:
482 value = getattr(obj, attr.name)
483 if isinstance(value, cx_Oracle.Object):
484 print(prefix + " " + attr.name + ":")
485 dumpobject(value, prefix + " ")
486 else:
487 print(prefix + " " + attr.name + ":", repr(value))
488 print(prefix, "}")
489
490 This might produce output like::
491
492 {
493 SDO_GTYPE: 2003
494 SDO_SRID: None
495 SDO_POINT:
496 {
497 X: 1
498 Y: 2
499 Z: 3
500 }
501 SDO_ELEM_INFO:
502 [
503 1
504 1003
505 3
506 ]
507 SDO_ORDINATES:
508 [
509 1
510 1
511 5
512 7
513 ]
514 }
515
516 Other information on using Oracle objects is in :ref:`Using Bind Variables
517 <bind>`.
518
519 Performance-sensitive applications should consider using scalar types instead of
520 objects. If you do use objects, avoid calling :meth:`Connection.gettype()`
521 unnecessarily, and avoid objects with large numbers of attributes.
522
523 .. _rowlimit:
524
525 Limiting Rows
526 -------------
527
528 Query data is commonly broken into one or more sets:
529
530 - To give an upper bound on the number of rows that a query has to process,
531 which can help improve database scalability.
532
533 - To perform 'Web pagination' that allows moving from one set of rows to a
534 next, or previous, set on demand.
535
536 - For fetching of all data in consecutive small sets for batch processing.
537 This happens because the number of records is too large for Python to handle
538 at one time.
539
540 The latter can be handled by calling :meth:`Cursor.fetchmany()` with one
541 execution of the SQL query.
542
543 'Web pagination' and limiting the maximum number of rows are discussed in this
544 section. For each 'page' of results, a SQL query is executed to get the
545 appropriate set of rows from a table. Since the query may be executed more
546 than once, make sure to use :ref:`bind variables <bind>` for row numbers and
547 row limits.
548
549 Oracle Database 12c SQL introduced an ``OFFSET`` / ``FETCH`` clause which is
550 similar to the ``LIMIT`` keyword of MySQL. In Python you can fetch a set of
551 rows using:
552
553 .. code-block:: python
554
555 myoffset = 0 // do not skip any rows (start at row 1)
556 mymaxnumrows = 20 // get 20 rows
557
558 sql =
559 """SELECT last_name
560 FROM employees
561 ORDER BY last_name
562 OFFSET :offset ROWS FETCH NEXT :maxnumrows ROWS ONLY"""
563
564 cur = connection.cursor()
565 for row in cur.execute(sql, offset=myoffset, maxnumrows=mymaxnumrows):
566 print(row)
567
568 In applications where the SQL query is not known in advance, this method
569 sometimes involves appending the ``OFFSET`` clause to the 'real' user query. Be
570 very careful to avoid SQL injection security issues.
571
572 For Oracle Database 11g and earlier there are several alternative ways
573 to limit the number of rows returned. The old, canonical paging query
574 is::
575
576 SELECT *
577 FROM (SELECT a.*, ROWNUM AS rnum
578 FROM (YOUR_QUERY_GOES_HERE -- including the order by) a
579 WHERE ROWNUM <= MAX_ROW)
580 WHERE rnum >= MIN_ROW
581
582 Here, ``MIN_ROW`` is the row number of first row and ``MAX_ROW`` is the row
583 number of the last row to return. For example::
584
585 SELECT *
586 FROM (SELECT a.*, ROWNUM AS rnum
587 FROM (SELECT last_name FROM employees ORDER BY last_name) a
588 WHERE ROWNUM <= 20)
589 WHERE rnum >= 1
590
591 This always has an 'extra' column, here called RNUM.
592
593 An alternative and preferred query syntax for Oracle Database 11g uses the
594 analytic ``ROW_NUMBER()`` function. For example to get the 1st to 20th names the
595 query is::
596
597 SELECT last_name FROM
598 (SELECT last_name,
599 ROW_NUMBER() OVER (ORDER BY last_name) AS myr
600 FROM employees)
601 WHERE myr BETWEEN 1 and 20
602
603 Make sure to use :ref:`bind variables <bind>` for the upper and lower limit
604 values.
605
606 .. _crc:
607
608 Client Result Cache
609 -------------------
610
611 Python cx_Oracle applications can use Oracle Database's `Client Result Cache
612 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-35CB2592-7588-4C2D-9075-6F639F25425E>`__
613 The CRC enables client-side caching of SQL query (SELECT statement) results in
614 client memory for immediate use when the same query is re-executed. This is
615 useful for reducing the cost of queries for small, mostly static, lookup tables,
616 such as for postal codes. CRC reduces network :ref:`round-trips <roundtrips>`,
617 and also reduces database server CPU usage.
618
619 The cache is at the application process level. Access and invalidation is
620 managed by the Oracle Client libraries. This removes the need for extra
621 application logic, or external utilities, to implement a cache.
622
623 CRC can be enabled by setting the `database parameters
624 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A9D4A5F5-B939-48FF-80AE-0228E7314C7D>`__
625 ``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then
626 restarting the database. For example, to set the parameters:
627
628 .. code-block:: sql
629
630 SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_LAG = 3000 SCOPE=SPFILE;
631 SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_SIZE = 64K SCOPE=SPFILE;
632
633 CRC can alternatively be configured in an :ref:`oraaccess.xml <optclientfiles>`
634 or :ref:`sqlnet.ora <optnetfiles>` file on the Python host, see `Client
635 Configuration Parameters
636 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-E63D75A1-FCAA-4A54-A3D2-B068442CE766>`__.
637
638 Tables can then be created, or altered, so repeated queries use CRC. This
639 allows existing applications to use CRC without needing modification. For example:
640
641 .. code-block:: sql
642
643 SQL> CREATE TABLE cities (id number, name varchar2(40)) RESULT_CACHE (MODE FORCE);
644 SQL> ALTER TABLE locations RESULT_CACHE (MODE FORCE);
645
646 Alternatively, hints can be used in SQL statements. For example:
647
648 .. code-block:: sql
649
650 SELECT /*+ result_cache */ postal_code FROM locations
651
652 .. _codecerror:
653
654 Querying Corrupt Data
655 ---------------------
656
657 If queries fail with the error "codec can't decode byte" when you select data,
658 then:
659
660 * Check your :ref:`character set <globalization>` is correct. Review the
661 :ref:`client and database character sets <findingcharset>`. Consider using
662 UTF-8, if this is appropriate:
663
664 .. code-block:: python
665
666 connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1",
667 encoding="UTF-8", nencoding="UTF-8")
668
669 * Check for corrupt data in the database.
670
671 If data really is corrupt, you can pass options to the internal `decode()
672 <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__ used by
673 cx_Oracle to allow it to be selected and prevent the whole query failing. Do
674 this by creating an :ref:`outputtypehandler <outputtypehandlers>` and setting
675 ``encodingErrors``. For example to replace corrupt characters in character
676 columns:
677
678 .. code-block:: python
679
680 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
681 if defaultType == cx_Oracle.STRING:
682 return cursor.var(defaultType, size, arraysize=cursor.arraysize,
683 encodingErrors="replace")
684
685 cursor.outputtypehandler = OutputTypeHandler
686
687 cursor.execute("select column1, column2 from SomeTableWithBadData")
688
689 Other codec behaviors can be chosen for ``encodingErrors``, see `Error Handlers
690 <https://docs.python.org/3/library/codecs.html#error-handlers>`__.
691
692 .. _dml:
693
694
695 INSERT and UPDATE Statements
696 ============================
697
698 SQL Data Manipulation Language statements (DML) such as INSERT and UPDATE can
699 easily be executed with cx_Oracle. For example:
700
701 .. code-block:: python
702
703 cur = connection.cursor()
704 cur.execute("insert into MyTable values (:idbv, :nmbv)", [1, "Fredico"])
705
706 Do not concatenate or interpolate user data into SQL statements. See
707 :ref:`bind` instead.
708
709 See :ref:`txnmgmnt` for best practices on committing and rolling back data
710 changes.
711
712 When handling multiple data values, use :meth:`~Cursor.executemany()` for
713 performance. See :ref:`batchstmnt`
714
715
716 Inserting NULLs
717 ---------------
718
719 Oracle requires a type, even for null values. When you pass the value None, then
720 cx_Oracle assumes the type is STRING. If this is not the desired type, you can
721 explicitly set it. For example, to insert a null :ref:`Oracle Spatial
722 SDO_GEOMETRY <spatial>` object:
723
724 .. code-block:: python
725
726 typeObj = connection.gettype("SDO_GEOMETRY")
727 cur = connection.cursor()
728 cur.setinputsizes(typeObj)
729 cur.execute("insert into sometable values (:1)", [None])
0 .. _startup:
1
2 *************************************
3 Starting and Stopping Oracle Database
4 *************************************
5
6 This chapter covers how to start up and shutdown Oracle Database using
7 cx_Oracle.
8
9 ===========================
10 Starting Oracle Database Up
11 ===========================
12
13 cx_Oracle can start up a database instance. A privileged connection is
14 required. This example shows a script that could be run as the 'oracle'
15 operating system user who administers a local database installation on Linux.
16 It assumes that the environment variable ``ORACLE_SID`` has been set to the SID
17 of the database that should be started:
18
19 .. code-block:: python
20
21 # the connection must be in PRELIM_AUTH mode to perform startup
22 connection = cx_Oracle.connect("/",
23 mode = cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
24 connection.startup()
25
26 # the following statements must be issued in normal SYSDBA mode
27 connection = cx_Oracle.connect("/", mode = cx_Oracle.SYSDBA, encoding="UTF-8")
28 cursor = connection.cursor()
29 cursor.execute("alter database mount")
30 cursor.execute("alter database open")
31
32 To start up a remote database, you may need to configure the Oracle Net
33 listener to use `static service registration
34 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
35 id=GUID-0203C8FA-A4BE-44A5-9A25-3D1E578E879F>`_
36 by adding a ``SID_LIST_LISTENER`` entry to the database `listener.ora` file.
37
38
39 =============================
40 Shutting Oracle Database Down
41 =============================
42
43 cx_Oracle has the ability to shutdown the database using a privileged
44 connection. This example also assumes that the environment variable
45 ``ORACLE_SID`` has been set:
46
47 .. code-block:: python
48
49 # need to connect as SYSDBA or SYSOPER
50 connection = cx_Oracle.connect("/", mode = cx_Oracle.SYSDBA)
51
52 # first shutdown() call must specify the mode, if DBSHUTDOWN_ABORT is used,
53 # there is no need for any of the other steps
54 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_IMMEDIATE)
55
56 # now close and dismount the database
57 cursor = connection.cursor()
58 cursor.execute("alter database close normal")
59 cursor.execute("alter database dismount")
60
61 # perform the final shutdown call
62 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_FINAL)
0 .. _tracingsql:
1
2 *********************************
3 Tracing SQL and PL/SQL Statements
4 *********************************
5
6 Subclass Connections
7 ====================
8
9 Subclassing enables applications to add "hooks" for connection and statement
10 execution. This can be used to alter, or log, connection and execution
11 parameters, and to extend cx_Oracle functionality.
12
13 The example below demonstrates subclassing a connection to log SQL execution
14 to a file. This example also shows how connection credentials can be embedded
15 in the custom subclass, so application code does not need to supply them.
16
17 .. code-block:: python
18
19 class Connection(cx_Oracle.Connection):
20 logFileName = "log.txt"
21
22 def __init__(self):
23 connectString = "hr/[email protected]/orclpdb1"
24 self._log("Connect to the database")
25 return super(Connection, self).__init__(connectString)
26
27 def _log(self, message):
28 with open(self.logFileName, "a") as f:
29 print(message, file=f)
30
31 def execute(self, sql, parameters):
32 self._log(sql)
33 cursor = self.cursor()
34 try:
35 return cursor.execute(sql, parameters)
36 except cx_Oracle.Error as e:
37 errorObj, = e.args
38 self._log(errorObj.message)
39
40 connection = Connection()
41 connection.execute("""
42 select department_name
43 from departments
44 where department_id = :id""", dict(id=270))
45
46 The messages logged in ``log.txt`` are::
47
48 Connect to the database
49
50 select department_name
51 from departments
52 where department_id = :id
53
54 If an error occurs, perhaps due to a missing table, the log file would contain
55 instead::
56
57 Connect to the database
58
59 select department_name
60 from departments
61 where department_id = :id
62 ORA-00942: table or view does not exist
63
64 In production applications be careful not to log sensitive information.
65
66 See `Subclassing.py
67 <https://github.com/oracle/python-cx_Oracle/blob/master/
68 samples/Subclassing.py>`__ for an example.
69
70
71 .. _endtoendtracing:
72
73 Oracle Database End-to-End Tracing
74 ==================================
75
76 Oracle Database End-to-end application tracing simplifies diagnosing application
77 code flow and performance problems in multi-tier or multi-user environments.
78
79 The connection attributes, :attr:`~Connection.client_identifier`,
80 :attr:`~Connection.clientinfo`, :attr:`~Connection.dbop`,
81 :attr:`~Connection.module` and :attr:`~Connection.action`, set the metadata for
82 end-to-end tracing. You can use data dictionary and ``V$`` views to monitor
83 tracing or use other application tracing utilities.
84
85 The attributes are sent to the database when the next :ref:`round-trip
86 <roundtrips>` to the database occurs, for example when the next SQL statement is
87 executed.
88
89 The attribute values will remain set in connections released back to connection
90 pools. When the application re-acquires a connection from the pool it should
91 initialize the values to a desired state before using that connection.
92
93 The example below shows setting the action, module and client identifier
94 attributes on the connection object:
95
96 .. code-block:: python
97
98 # Set the tracing metadata
99 connection.client_identifier = "pythonuser"
100 connection.action = "Query Session tracing parameters"
101 connection.module = "End-to-end Demo"
102
103 for row in cursor.execute("""
104 SELECT username, client_identifier, module, action
105 FROM V$SESSION
106 WHERE username = 'SYSTEM'"""):
107 print(row)
108
109 The output will be::
110
111 ('SYSTEM', 'pythonuser', 'End-to-end Demo', 'Query Session tracing parameters')
112
113 The values can also be manually set as shown by calling
114 `DBMS_APPLICATION_INFO procedures
115 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
116 id=GUID-14484F86-44F2-4B34-B34E-0C873D323EAD>`__
117 or `DBMS_SESSION.SET_IDENTIFIER
118 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
119 id=GUID-988EA930-BDFE-4205-A806-E54F05333562>`__. These incur round-trips to
120 the database, however, reducing scalability.
121
122 .. code-block:: sql
123
124 BEGIN
125 DBMS_SESSION.SET_IDENTIFIER('pythonuser');
126 DBMS_APPLICATION_INFO.set_module('End-to-End Demo');
127 DBMS_APPLICATION_INFO.set_action(action_name => 'Query Session tracing parameters');
128 END;
129
130
131 Low Level SQL Tracing in cx_Oracle
132 ==================================
133
134 cx_Oracle is implemented using the `ODPI-C <https://oracle.github.io/odpi>`__
135 wrapper on top of the Oracle Client libraries. The ODPI-C tracing capability
136 can be used to log executed cx_Oracle statements to the standard error stream.
137 Before executing Python, set the environment variable ``DPI_DEBUG_LEVEL`` to
138 16.
139
140 At a Windows command prompt, this could be done with::
141
142 set DPI_DEBUG_LEVEL=16
143
144 On Linux, you might use::
145
146 export DPI_DEBUG_LEVEL=16
147
148 After setting the variable, run the Python Script, for example on Linux::
149
150 python end-to-endtracing.py 2> log.txt
151
152 For an application that does a single query, the log file might contain a
153 tracing line consisting of the prefix 'ODPI', a thread identifier, a timestamp,
154 and the SQL statement executed::
155
156 ODPI [26188] 2019-03-26 09:09:03.909: ODPI-C 3.1.1
157 ODPI [26188] 2019-03-26 09:09:03.909: debugging messages initialized at level 16
158 ODPI [26188] 2019-03-26 09:09:09.917: SQL SELECT * FROM jobss
159 Traceback (most recent call last):
160 File "end-to-endtracing.py", line 14, in <module>
161 cursor.execute("select * from jobss")
162 cx_Oracle.DatabaseError: ORA-00942: table or view does not exist
163
164 See `ODPI-C Debugging
165 <https://oracle.github.io/odpi/doc/user_guide/debugging.html>`__ for
166 documentation on ``DPI_DEBUG_LEVEL``.
0 .. _tuning:
1
2 ****************
3 Tuning cx_Oracle
4 ****************
5
6 Some general tuning tips are:
7
8 * Tune your application architecture.
9
10 A general application goal is to reduce the number of :ref:`round-trips
11 <roundtrips>` between cx_Oracle and the database.
12
13 For multi-user applications, make use of connection pooling. Create the pool
14 once during application initialization. Do not oversize the pool, see
15 :ref:`connpool` . Use a session callback function to set session state, see
16 :ref:`Session CallBacks for Setting Pooled Connection State <sessioncallback>`.
17
18 Make use of efficient cx_Oracle functions. For example, to insert
19 multiple rows use :meth:`Cursor.executemany()` instead of
20 :meth:`Cursor.execute()`.
21
22 * Tune your SQL statements. See the `SQL Tuning Guide
23 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGSQL>`__.
24
25 Use :ref:`bind variables <bind>` to avoid statement reparsing.
26
27 Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query,
28 see :ref:`Tuning Fetch Performance <tuningfetch>`.
29
30 Do simple optimizations like :ref:`limiting the number of rows <rowlimit>` and
31 avoiding selecting columns not used in the application.
32
33 It may be faster to work with simple scalar relational values than to use
34 Oracle Database object types.
35
36 Make good use of PL/SQL to avoid executing many individual statements from
37 cx_Oracle.
38
39 Tune the :ref:`Statement Cache <stmtcache>`.
40
41 Enable :ref:`Client Result Caching <clientresultcache>` for small lookup tables.
42
43 * Tune your database. See the `Database Performance Tuning Guide
44 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGDBA>`__.
45
46 * Tune your network. For example, when inserting or retrieving a large number
47 of rows (or for large data), or when using a slow network, then tune the
48 Oracle Network Session Data Unit (SDU) and socket buffer sizes, see `Oracle
49 Net Services: Best Practices for Database Performance and High Availability
50 <https://static.rainfocus.com/oracle/oow19/sess/1553616880266001WLIh/PF/OOW19_Net_CON4641_1569022126580001esUl.pdf>`__.
51
52 * Do not commit or rollback unnecessarily. Use :attr:`Connection.autocommit` on
53 the last of a sequence of DML statements.
54
55 .. _tuningfetch:
56
57 Tuning Fetch Performance
58 ========================
59
60 To tune queries you can adjust cx_Oracle's internal buffer sizes to improve the
61 speed of fetching rows across the network from the database, and to optimize
62 memory usage. Regardless of which cx_Oracle method is used to get query
63 results, internally all rows are fetched in batches from the database and
64 buffered before being returned to the application. The internal buffer sizes
65 can have a significant performance impact. The sizes do not affect how, or
66 when, rows are returned to your application. They do not affect the minimum or
67 maximum number of rows returned by a query.
68
69 For best performance, tune "array fetching" with :attr:`Cursor.arraysize` and
70 "row prefetching" with :attr:`Cursor.prefetchrows` before calling
71 :meth:`Cursor.execute()`. Queries that return LOBs and similar types will never
72 prefetch rows, so the ``prefetchrows`` value is ignored in those cases.
73
74 The common query tuning scenario is for SELECT statements that return a large
75 number of rows over a slow network. Increasing ``arraysize`` can improve
76 performance by reducing the number of :ref:`round-trips <roundtrips>` to the
77 database. However increasing this value increases the amount of memory
78 required. Adjusting ``prefetchrows`` will also affect performance and memory
79 usage.
80
81 Row prefetching and array fetching are both internal buffering techniques to
82 reduce :ref:`round-trips <roundtrips>` to the database. The difference is the
83 code layer that is doing the buffering, and when the buffering occurs. The
84 Oracle Client libraries used by cx_Oracle have separate "execute SQL statement"
85 and "fetch data" calls. Prefetching allows query results to be returned to the
86 application when the successful statement execution acknowledgment is returned
87 from the database. This means that a subsequent internal "fetch data" operation
88 does not always need to make a round-trip to the database because rows are
89 already buffered in the Oracle Client libraries. Reducing round-trips helps
90 performance and scalability. An overhead of prefetching is the need for an
91 additional data copy from Oracle Client's prefetch buffers.
92
93 Choosing values for ``arraysize`` and ``prefetchrows``
94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
95
96 The best :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` values can be
97 found by experimenting with your application under the expected load of normal
98 application use. This is because the cost of the extra memory copy from the
99 prefetch buffers when fetching a large quantity of rows or very "wide" rows may
100 outweigh the cost of a round-trip for a single cx_Oracle user on a fast network.
101 However under production application load, the reduction of round-trips may help
102 performance and overall system scalability. The documentation in
103 :ref:`round-trips <roundtrips>` shows how to measure round-trips.
104
105 Here are some suggestions for the starting point to begin your tuning:
106
107 * To tune queries that return an unknown number of rows, estimate the number of
108 rows returned and start with an appropriate :attr:`Cursor.arraysize` value.
109 The default is 100. Then set :attr:`Cursor.prefetchrows` to the ``arraysize``
110 value. Do not make the sizes unnecessarily large. For example:
111
112 .. code-block:: python
113
114 cur = connection.cursor()
115
116 cur.prefetchrows = 1000
117 cur.arraysize = 1000
118
119 for row in cur.execute("SELECT * FROM very_big_table"):
120 print(row)
121
122 Adjust the values as needed for performance, memory and round-trip usage. For
123 a large quantity of rows or very "wide" rows on fast networks you may prefer
124 to leave ``prefetchrows`` at its default value of 2. Keep ``arraysize`` as
125 big, or bigger than, ``prefetchrows``.
126
127 * If you are fetching a fixed number of rows, start your tuning by setting
128 ``arraysize`` to the number of expected rows, and set ``prefetchrows`` to one
129 greater than this value. (Adding one removes the need for a round-trip to check
130 for end-of-fetch). For example, if you are querying 20 rows, perhaps to
131 :ref:`display a page <rowlimit>` of data, set ``prefetchrows`` to 21 and
132 ``arraysize`` to 20:
133
134 .. code-block:: python
135
136 cur = connection.cursor()
137
138 cur.prefetchrows = 21
139 cur.arraysize = 20
140
141 for row in cur.execute("""
142 SELECT last_name
143 FROM employees
144 ORDER BY last_name
145 OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY"""):
146 print(row)
147
148 This will return all rows for the query in one round-trip.
149
150 * If you know that a query returns just one row then set :attr:`Cursor.arraysize`
151 to 1 to minimize memory usage. The default prefetch value of 2 allows minimal
152 round-trips for single-row queries:
153
154 .. code-block:: python
155
156 cur = connection.cursor()
157 cur.arraysize = 1
158 cur.execute("select * from MyTable where id = 1"):
159 row = cur.fetchone()
160 print(row)
161
162 In cx_Oracle, the ``arraysize`` and ``prefetchrows`` values are only examined
163 when a statement is executed the first time. To change the values, create a new
164 cursor. For example, to change ``arraysize`` for a repeated statement:
165
166 .. code-block:: python
167
168 array_sizes = (10, 100, 1000)
169 for size in array_sizes:
170 cursor = connection.cursor()
171 cursor.arraysize = size
172 start = time.time()
173 cursor.execute(sql).fetchall()
174 elapsed = time.time() - start
175 print("Time for", size, elapsed, "seconds")
176
177 There are two cases that will benefit from setting :attr:`Cursor.prefetchrows`
178 to 0:
179
180 * When passing REF CURSORS into PL/SQL packages. Setting ``prefetchrows`` to 0
181 can stop rows being prematurely (and silently) fetched into cx_Oracle's
182 internal buffers, making them unavailable to the PL/SQL code that receives the
183 REF CURSOR.
184
185 * When querying a PL/SQL function that uses PIPE ROW to emit rows at
186 intermittent intervals. By default, several rows needs to be emitted by the
187 function before cx_Oracle can return them to the application. Setting
188 ``prefetchrows`` to 0 helps give a consistent flow of data to the application.
189
190 Prefetching can also be enabled in an external :ref:`oraaccess.xml
191 <optclientfiles>` file, which may be useful for tuning an application when
192 modifying its code is not feasible. Setting the size in ``oraaccess.xml`` will
193 affect the whole application, so it should not be the first tuning choice.
194
195 One place where increasing ``arraysize`` is particularly useful is in copying
196 data from one database to another:
197
198 .. code-block:: python
199
200 # setup cursors
201 sourceCursor = sourceConnection.cursor()
202 sourceCursor.arraysize = 1000
203 targetCursor = targetConnection.cursor()
204
205 # perform fetch and bulk insertion
206 sourceCursor.execute("select * from MyTable")
207 while True:
208 rows = sourceCursor.fetchmany()
209 if not rows:
210 break
211 targetCursor.executemany("insert into MyTable values (:1, :2)", rows)
212 targetConnection.commit()
213
214 .. _roundtrips:
215
216 Tuning REF CURSORS
217 ++++++++++++++++++
218
219 In cx_Oracle, REF CURSORS can also be tuned by setting the values of ``arraysize``
220 and ``prefetchrows``. The prefetchrows value must be set before calling the PL/SQL
221 procedure as the REF CURSOR is executed on the server.
222
223 For example:
224
225 .. code-block:: python
226
227 # Set the arraysize and prefetch rows of the REF cursor
228 ref_cursor = connection.cursor()
229 ref_cursor.prefetchrows = 1000
230 ref_cursor.arraysize = 1000
231
232 # Perform the tuned fetch
233 sum_rows = 0
234 cursor.callproc("myrefcursorproc", [ref_cursor])
235 print("Sum of IntCol for", num_rows, "rows:")
236 for row in ref_cursor:
237 sum_rows += row[0]
238 print(sum_rows)
239
240 Database Round-trips
241 ====================
242
243 A round-trip is defined as the trip from the Oracle Client libraries (used by
244 cx_Oracle) to the database and back. Calling each cx_Oracle function, or
245 accessing each attribute, will require zero or more round-trips. Along with
246 tuning an application's architecture and `tuning its SQL statements
247 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGSQL>`__, a general
248 performance and scalability goal is to minimize `round-trips
249 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9B2F05F9-D841-4493-A42D-A7D89694A2D1>`__.
250
251 Some general tips for reducing round-trips are:
252
253 * Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query.
254 * Use :meth:`Cursor.executemany()` for optimal DML execution.
255 * Only commit when necessary. Use :attr:`Connection.autocommit` on the last statement of a transaction.
256 * For connection pools, use a callback to set connection state, see :ref:`Session CallBacks for Setting Pooled Connection State <sessioncallback>`.
257 * Make use of PL/SQL procedures which execute multiple SQL statements instead of executing them individually from cx_Oracle.
258 * Use scalar types instead of Oracle Database object types.
259 * Avoid overuse of :meth:`Connection.ping()`.
260
261 Finding the Number of Round-Trips
262 +++++++++++++++++++++++++++++++++
263
264 Oracle's `Automatic Workload Repository
265 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
266 (AWR) reports show 'SQL*Net roundtrips to/from client' and are useful for
267 finding the overall behavior of a system.
268
269 Sometimes you may wish to find the number of round-trips used for a
270 specific application. Snapshots of the ``V$SESSTAT`` view taken before
271 and after doing some work can be used for this:
272
273 .. code-block:: sql
274
275 SELECT ss.value, sn.display_name
276 FROM v$sesstat ss, v$statname sn
277 WHERE ss.sid = SYS_CONTEXT('USERENV','SID')
278 AND ss.statistic# = sn.statistic#
279 AND sn.name LIKE '%roundtrip%client%';
280
281 .. _stmtcache:
282
283 Statement Caching
284 =================
285
286 cx_Oracle's :meth:`Cursor.execute()` and :meth:`Cursor.executemany()` functions
287 use the `Oracle Call Interface statement cache
288 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-4947CAE8-1F00-4897-BB2B-7F921E495175>`__
289 to make re-execution of statements efficient. Each standalone or pooled
290 connection has its own cache of statements with a default size of 20. Statement
291 caching lets cursors be used without re-parsing the statement. Statement
292 caching also reduces metadata transfer costs between the cx_Oracle and the
293 database. Performance and scalability are improved.
294
295 The statement cache size can be set with :attr:`Connection.stmtcachesize` or
296 :attr:`SessionPool.stmtcachesize`. In general, set the statement cache size to
297 the size of the working set of statements being executed by the application. To
298 manually tune the cache, monitor the general application load and the `Automatic
299 Workload Repository
300 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
301 (AWR) "bytes sent via SQL*Net to client" values. The latter statistic should
302 benefit from not shipping statement metadata to cx_Oracle. Adjust the statement
303 cache size to your satisfaction.
304
305 Statement caching can be disabled by setting the size to 0. Disabling
306 the cache may be beneficial when the quantity or order of statements
307 causes cache entries to be flushed before they get a chance to be
308 reused. For example if there are more distinct statements than cache
309 slots, and the order of statement execution causes older statements to
310 be flushed from the cache before the statements are re-executed.
311
312 With Oracle Database 12c, or later, the statement cache size can be
313 automatically tuned using the :ref:`oraaccess.xml <optclientfiles>` file.
314
315 When it is inconvenient to pass statement text through an application, the
316 :meth:`Cursor.prepare()` call can be used to avoid statement re-parsing.
317 Subsequent ``execute()`` calls use the value ``None`` instead of the SQL text:
318
319 .. code-block:: python
320
321 cur.prepare("select * from dept where deptno = :id order by deptno")
322
323 cur.execute(None, id = 20)
324 res = cur.fetchall()
325 print(res)
326
327 cur.execute(None, id = 10)
328 res = cur.fetchall()
329 print(res)
330
331 Statements passed to :meth:`~Cursor.prepare()` are also stored in the statement
332 cache.
333
334 .. _clientresultcache:
335
336 Client Result Caching
337 =====================
338
339 cx_Oracle applications can use Oracle Database's `Client Result Cache
340 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-35CB2592-7588-4C2D-9075-6F639F25425E>`__.
341 The CRC enables client-side caching of SQL query (SELECT statement) results in
342 client memory for immediate use when the same query is re-executed. This is
343 useful for reducing the cost of queries for small, mostly static, lookup tables,
344 such as for postal codes. CRC reduces network :ref:`round-trips <roundtrips>`,
345 and also reduces database server CPU usage.
346
347 The cache is at the application process level. Access and invalidation is
348 managed by the Oracle Client libraries. This removes the need for extra
349 application logic, or external utilities, to implement a cache.
350
351 CRC can be enabled by setting the `database parameters
352 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A9D4A5F5-B939-48FF-80AE-0228E7314C7D>`__
353 ``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then
354 restarting the database, for example:
355
356 .. code-block:: sql
357
358 SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_LAG = 3000 SCOPE=SPFILE;
359 SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_SIZE = 64K SCOPE=SPFILE;
360 SQL> STARTUP FORCE
361
362 CRC can alternatively be configured in an :ref:`oraaccess.xml <optclientfiles>`
363 or :ref:`sqlnet.ora <optnetfiles>` file on the Python host, see `Client
364 Configuration Parameters
365 <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-E63D75A1-FCAA-4A54-A3D2-B068442CE766>`__.
366
367 Tables can then be created, or altered, so repeated queries use CRC. This
368 allows existing applications to use CRC without needing modification. For example:
369
370 .. code-block:: sql
371
372 SQL> CREATE TABLE cities (id number, name varchar2(40)) RESULT_CACHE (MODE FORCE);
373 SQL> ALTER TABLE locations RESULT_CACHE (MODE FORCE);
374
375 Alternatively, hints can be used in SQL statements. For example:
376
377 .. code-block:: sql
378
379 SELECT /*+ result_cache */ postal_code FROM locations
0 .. _txnmgmnt:
1
2 **********************
3 Transaction Management
4 **********************
5
6 A database transaction is a grouping of SQL statements that make a logical data
7 change to the database.
8
9 When :meth:`Cursor.execute()` executes a SQL statement, a transaction is
10 started or continued. By default, cx_Oracle does not commit this transaction
11 to the database. The methods :meth:`Connection.commit()` and
12 :meth:`Connection.rollback()` methods can be used to explicitly commit
13 or rollback a transaction:
14
15 .. code-block:: python
16
17 cursor.execute("INSERT INTO mytab (name) VALUES ('John')")
18 connection.commit()
19
20 When a database connection is closed, such as with :meth:`Connection.close()`,
21 or when variables referencing the connection go out of scope, any uncommitted
22 transaction will be rolled back.
23
24
25 Autocommitting
26 ==============
27
28 An alternative way to commit is to set the attribute
29 :attr:`~Connection.autocommit` of the connection to ``True``. This ensures all
30 :ref:`DML <dml>` statements (INSERT, UPDATE etc) are committed as they are
31 executed. Unlike :meth:`Connection.commit()`, this does not require an
32 additional :ref:`round-trip <roundtrips>` to the database so it is more
33 efficient when used appropriately.
34
35 Note that irrespective of the autocommit value, Oracle Database will always
36 commit an open transaction when a DDL statement is executed.
37
38 When executing multiple DML statements that constitute a single transaction, it
39 is recommended to use autocommit mode only for the last DML statement in the
40 sequence of operations. Unnecessarily committing causes extra database load,
41 and can destroy transactional consistency.
42
43 The example below shows a new customer being added to the table ``CUST_TABLE``.
44 The corresponding ``SALES`` table is updated with a purchase of 3000 pens from
45 the customer. The final insert uses autocommit mode to commit both new
46 records:
47
48 .. code-block:: python
49
50 # Add a new customer
51 idVar = cursor.var(int)
52 connection.autocommit = False # make sure any previous value is off
53 cursor.execute("""
54 INSERT INTO cust_table (name) VALUES ('John')
55 RETURNING id INTO :bvid""", bvid=idVar)
56
57 # Add sales data for the new customer and commit all new values
58 idVal = idVar.getvalue()[0]
59 connection.autocommit = True
60 cursor.execute("INSERT INTO sales_table VALUES (:bvid, 'pens', 3000)",
61 bvid=idVal)
62
63
64 Explicit Transactions
65 =====================
66
67 The method :meth:`Connection.begin()` can be used to explicitly start a local
68 or global transaction.
69
70 Without parameters, this explicitly begins a local transaction; otherwise, this
71 explicitly begins a distributed (global) transaction with the given parameters.
72 See the Oracle documentation for more details.
73
74 Note that in order to make use of global (distributed) transactions, the
75 attributes :attr:`Connection.internal_name` and
76 :attr:`Connection.external_name` attributes must be set.
0 .. _xmldatatype:
1
2 ********************
3 Working with XMLTYPE
4 ********************
5
6 Oracle XMLType columns are fetched as strings by default. This is currently
7 limited to the maximum length of a ``VARCHAR2`` column. To return longer XML
8 values, they must be queried as LOB values instead.
9
10 The examples below demonstrate using XMLType data with cx_Oracle. The
11 following table will be used in these examples:
12
13 .. code-block:: sql
14
15 CREATE TABLE xml_table (
16 id NUMBER,
17 xml_data SYS.XMLTYPE
18 );
19
20 Inserting into the table can be done by simply binding a string as shown:
21
22 .. code-block:: python
23
24 xmlData = """<?xml version="1.0"?>
25 <customer>
26 <name>John Smith</name>
27 <Age>43</Age>
28 <Designation>Professor</Designation>
29 <Subject>Mathematics</Subject>
30 </customer>"""
31 cursor.execute("insert into xml_table values (:id, :xml)",
32 id=1, xml=xmlData)
33
34 This approach works with XML strings up to 1 GB in size. For longer strings, a
35 temporary CLOB must be created using :meth:`Connection.createlob()` and bound
36 as shown:
37
38 .. code-block:: python
39
40 clob = connection.createlob(cx_Oracle.DB_TYPE_CLOB)
41 clob.write(xmlData)
42 cursor.execute("insert into xml_table values (:id, sys.xmltype(:xml))",
43 id=2, xml=clob)
44
45 Fetching XML data can be done simply for values that are shorter than the
46 length of a VARCHAR2 column, as shown:
47
48 .. code-block:: python
49
50 cursor.execute("select xml_data from xml_table where id = :id", id=1)
51 xmlData, = cursor.fetchone()
52 print(xmlData) # will print the string that was originally stored
53
54 For values that exceed the length of a VARCHAR2 column, a CLOB must be returned
55 instead by using the function ``XMLTYPE.GETCLOBVAL()`` as shown:
56
57 .. code-block:: python
58
59 cursor.execute("""
60 select xmltype.getclobval(xml_data)
61 from xml_table
62 where id = :id""", id=1)
63 clob, = cursor.fetchone()
64 print(clob.read())
65
66 The LOB that is returned can be streamed or a string can be returned instead of
67 a CLOB. See :ref:`lobdata` for more information about processing LOBs.
+0
-84
doc/src/variable.rst less more
0 .. _varobj:
1
2 ****************
3 Variable Objects
4 ****************
5
6 .. note::
7
8 The DB API definition does not define this object.
9
10
11 .. attribute:: Variable.actualElements
12
13 This read-only attribute returns the actual number of elements in the
14 variable. This corresponds to the number of elements in a PL/SQL index-by
15 table for variables that are created using the method
16 :func:`Cursor.arrayvar()`. For all other variables this value will be
17 identical to the attribute :attr:`~Variable.numElements`.
18
19 .. attribute:: Variable.bufferSize
20
21 This read-only attribute returns the size of the buffer allocated for each
22 element in bytes.
23
24
25 .. method:: Variable.getvalue([pos=0])
26
27 Return the value at the given position in the variable. For variables
28 created using the method :func:`Cursor.arrayvar()` the value returned will
29 be a list of each of the values in the PL/SQL index-by table. For variables
30 bound to DML returning statements, the value returned will also be a list
31 corresponding to the returned data for the given execution of the statement
32 (as identified by the pos parameter).
33
34
35 .. attribute:: Variable.inconverter
36
37 This read-write attribute specifies the method used to convert data from
38 Python to the Oracle database. The method signature is converter(value)
39 and the expected return value is the value to bind to the database. If this
40 attribute is None, the value is bound directly without any conversion.
41
42
43 .. attribute:: Variable.numElements
44
45 This read-only attribute returns the number of elements allocated in an
46 array, or the number of scalar items that can be fetched into the variable
47 or bound to the variable.
48
49
50 .. attribute:: Variable.outconverter
51
52 This read-write attribute specifies the method used to convert data from
53 from the Oracle to Python. The method signature is converter(value)
54 and the expected return value is the value to return to Python. If this
55 attribute is None, the value is returned directly without any conversion.
56
57
58 .. method:: Variable.setvalue(pos, value)
59
60 Set the value at the given position in the variable.
61
62
63 .. attribute:: Variable.size
64
65 This read-only attribute returns the size of the variable. For strings this
66 value is the size in characters. For all others, this is same value as the
67 attribute bufferSize.
68
69
70 .. attribute:: Variable.type
71
72 This read-only attribute returns the type of the variable for those
73 variables that bind Oracle objects (it is not present for any other type of
74 variable).
75
76
77 .. attribute:: Variable.values
78
79 This read-only attribute returns a copy of the value of all actual
80 positions in the variable as a list. This is the equivalent of calling
81 :meth:`~Variable.getvalue()` for each valid position and the length will
82 correspond to the value of the :attr:`~Variable.actualElements` attribute.
83
+0
-171
doc/src/whatsnew.rst less more
0 .. _whatsnew:
1
2 **********
3 What's New
4 **********
5
6 .. _whatsnew60:
7
8 cx_Oracle 6.0
9 =============
10
11 This document contains a summary of the changes in cx_Oracle 6 compared to
12 cx_Oracle 5.3. cx_Oracle 6.0 was released on August 14, 2017. See the
13 :ref:`release notes <releasenotes60>` for complete details.
14
15 Highlights
16 ----------
17
18 - Has been re-implemented to use the new
19 `ODPI-C <https://oracle.github.io/odpi>`__ abstraction layer for Oracle
20 Database. The cx_Oracle API is unchanged. The cx_Oracle design, build and
21 linking process has improved because of ODPI-C.
22
23 - Now has Python Wheels available for install. This is made possible by the
24 ODPI-C architecture. Windows installers and Linux RPMs are no longer
25 produced since PyPI no longer supports them.
26
27 - Has less code in Python's Global Interpreter Lock, giving better
28 scalability.
29
30 - Added support for universal rowids.
31
32 - Added support for DML returning of multiple rows.
33
34 - Now associates LOB locators to LOB objects so they are not overwritten on
35 database round trips.
36
37
38 Installation Changes
39 --------------------
40
41 - On Linux, cx_Oracle 6 no longer uses instant client RPMs automatically.
42 You must set LD_LIBRARY_PATH or use ldconfig to locate the Oracle Client
43 library.
44
45 - On platforms other than Windows, if ORACLE_HOME is set (in a database or
46 full client installation), remove requirement to set LD_LIBRARY_PATH in
47 order to locate the Oracle Client library
48 (`issue 20 <https://github.com/oracle/odpi/issues/20>`__).
49
50
51 Connection Management Enhancements
52 ----------------------------------
53
54 - Prevent closing the connection when there are any open statements or LOBs
55 and add new error "DPI-1054: connection cannot be closed when open
56 statements or LOBs exist" when this situation is detected; this is needed
57 to prevent crashes under certain conditions when statements or LOBs are
58 being acted upon while at the same time (in another thread) a connection
59 is being closed; it also prevents leaks of statements and LOBs when a
60 connection is returned to a session pool.
61
62 - Added attribute :attr:`SessionPool.stmtcachesize` to support getting and
63 setting the default statement cache size for connections in the pool.
64
65 - Added attribute :attr:`Connection.dbop` to support setting the database
66 operation that is to be monitored.
67
68 - Added attribute :attr:`Connection.handle` to facilitate testing the
69 creation of a connection using a OCI service context handle.
70
71 - Added parameters tag and matchanytag to the :meth:`cx_Oracle.connect` and
72 :meth:`SessionPool.acquire` methods and added parameters tag and retag to
73 the :meth:`SessionPool.release` method in order to support session
74 tagging.
75
76 - Added parameter edition to the :meth:`cx_Oracle.SessionPool` method.
77
78 - Added parameters region, sharding_key and super_sharding_key to the
79 :meth:`cx_Oracle.makedsn()` method to support connecting to a sharded
80 database (new in Oracle Database 12.2).
81
82 - Removed requirement that encoding and nencoding both be specified when
83 creating a connection or session pool. The missing value is set to its
84 default value if one of the values is set and the other is not
85 (`issue 36 <https://github.com/oracle/python-cx_Oracle/issues/36>`__).
86
87 - Permit use of both string and unicode for Python 2.7 for creating session
88 pools and for changing passwords
89 (`issue 23 <https://github.com/oracle/python-cx_Oracle/issues/23>`__).
90
91
92 Data Type and Data Handling Enhancements
93 ----------------------------------------
94
95 - Added attributes :attr:`Variable.actualElements` and
96 :attr:`Variable.values` to variables.
97
98 - Added support for smallint and float data types in Oracle objects, as
99 requested
100 (`issue 4 <https://github.com/oracle/python-cx_Oracle/issues/4>`__).
101
102 - Added support for getting/setting attributes of objects or element values
103 in collections that contain LOBs, BINARY_FLOAT values, BINARY_DOUBLE
104 values and NCHAR and NVARCHAR2 values. The error message for any types
105 that are not supported has been improved as well.
106
107 - An exception is no longer raised when a collection is empty for methods
108 :meth:`Object.first()` and :meth:`Object.last()`. Instead, the value None
109 is returned to be consistent with the methods :meth:`Object.next()` and
110 :meth:`Object.prev()`.
111
112 - Removed requirement for specifying a maximum size when fetching LONG or
113 LONG raw columns. This also allows CLOB, NCLOB, BLOB and BFILE columns to
114 be fetched as strings or bytes without needing to specify a maximum size.
115 The method :meth:`Cursor.setoutputsize` no longer does anything, since
116 ODPI-C automatically manages buffer sizes of LONG and LONG RAW columns.
117
118 - Enable temporary LOB caching in order to avoid disk I/O as suggested
119 (`issue 10 <https://github.com/oracle/odpi/issues/10>`__).
120
121
122 Error Handling Enhancements
123 ---------------------------
124
125 - Provide improved error message when OCI environment cannot be created,
126 such as when the oraaccess.xml file cannot be processed properly.
127
128 - Define exception classes on the connection object in addition to at
129 module scope in order to simplify error handling in multi-connection
130 environments, as specified in the Python DB API.
131
132
133 Test Enhancements
134 -----------------
135
136 - Reworked test suite and samples so that they are independent of each
137 other and so that the SQL scripts used to create/drop schemas are easily
138 adjusted to use different schema names, if desired.
139
140 - Updated DB API test suite stub to support Python 3.
141
142
143 Removals
144 --------
145
146 - Dropped deprecated parameter twophase from the :meth:`cx_Oracle.connect`
147 method. Applications should set the :attr:`Connection.internal_name` and
148 :attr:`Connection.external_name` attributes instead to a value
149 appropriate to the application.
150
151 - Dropped deprecated parameters action, module and clientinfo from the
152 :meth:`cx_Oracle.connect` method. The appcontext parameter should be used
153 instead as shown in this `sample <https://github.com/oracle/
154 python-cx_Oracle/blob/master/samples/AppContext.py>`__.
155
156 - Dropped deprecated attribute numbersAsString from
157 :ref:`cursor objects <cursorobj>`. Use an output type handler instead as
158 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
159 master/samples/ReturnNumbersAsDecimals.py>`__.
160
161 - Dropped deprecated attributes cqqos and rowids from
162 :ref:`subscription objects <subscrobj>`. Use the qos attribute instead as
163 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
164 master/samples/CQN.py>`__.
165
166 - Dropped deprecated parameters cqqos and rowids from the
167 :meth:`Connection.subscribe()` method. Use the qos parameter instead as
168 shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
169 master/samples/CQN.py>`__.
170
0 [build-system]
1 requires = ["setuptools >= 40.6.0", "wheel"]
2 build-backend = "setuptools.build_meta"
+0
-64
samples/AdvancedQueuing.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 #------------------------------------------------------------------------------
10 # AdvancedQueuing.py
11 # This script demonstrates how to use advanced queuing using cx_Oracle. It
12 # makes use of a simple type and queue created in the sample setup.
13 #
14 # This script requires cx_Oracle 5.3 and higher.
15 #------------------------------------------------------------------------------
16
17 from __future__ import print_function
18
19 BOOK_TYPE_NAME = "UDT_BOOK"
20 QUEUE_NAME = "BOOKS"
21 QUEUE_TABLE_NAME = "BOOK_QUEUE"
22
23 import cx_Oracle
24 import SampleEnv
25 import decimal
26
27 # connect to database
28 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
29 cursor = connection.cursor()
30
31 # dequeue all existing messages to ensure the queue is empty, just so that
32 # the results are consistent
33 booksType = connection.gettype(BOOK_TYPE_NAME)
34 book = booksType.newobject()
35 options = connection.deqoptions()
36 options.wait = cx_Oracle.DEQ_NO_WAIT
37 messageProperties = connection.msgproperties()
38 while connection.deq(QUEUE_NAME, options, messageProperties, book):
39 pass
40
41 # enqueue a few messages
42 book1 = booksType.newobject()
43 book1.TITLE = "The Fellowship of the Ring"
44 book1.AUTHORS = "Tolkien, J.R.R."
45 book1.PRICE = decimal.Decimal("10.99")
46 book2 = booksType.newobject()
47 book2.TITLE = "Harry Potter and the Philosopher's Stone"
48 book2.AUTHORS = "Rowling, J.K."
49 book2.PRICE = decimal.Decimal("7.99")
50 options = connection.enqoptions()
51 for book in (book1, book2):
52 print("Enqueuing book", book.TITLE)
53 connection.enq(QUEUE_NAME, options, messageProperties, book)
54 connection.commit()
55
56 # dequeue the messages
57 options = connection.deqoptions()
58 options.navigation = cx_Oracle.DEQ_FIRST_MSG
59 options.wait = cx_Oracle.DEQ_NO_WAIT
60 while connection.deq(QUEUE_NAME, options, messageProperties, book):
61 print("Dequeued book", book.TITLE)
62 connection.commit()
63
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
55 # AdvancedQueuingNotification.py
66 # This script demonstrates using advanced queuing notification. Once this
77 # script is running, use another session to enqueue a few messages to the
8 # "BOOKS" queue. This is most easily accomplished by running the
9 # AdvancedQueuing sample.
8 # "DEMO_BOOK_QUEUE" queue. This is most easily accomplished by running the
9 # ObjectAQ.py sample.
1010 #
1111 # This script requires cx_Oracle 6.4 and higher.
1212 #------------------------------------------------------------------------------
1313
14 from __future__ import print_function
15
1614 import cx_Oracle
17 import SampleEnv
15 import sample_env
1816 import threading
1917 import time
2018
2119 registered = True
2220
23 def callback(message):
21 def ProcessMessages(message):
2422 global registered
2523 print("Message type:", message.type)
2624 if message.type == cx_Oracle.EVENT_DEREG:
3028 print("Queue name:", message.queueName)
3129 print("Consumer name:", message.consumerName)
3230
33 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(), events = True)
34 sub = connection.subscribe(namespace = cx_Oracle.SUBSCR_NAMESPACE_AQ,
35 name = "BOOKS", callback = callback, timeout = 300)
31 connection = cx_Oracle.connect(sample_env.get_main_connect_string(),
32 events=True)
33 sub = connection.subscribe(namespace=cx_Oracle.SUBSCR_NAMESPACE_AQ,
34 name="DEMO_BOOK_QUEUE", callback=ProcessMessages,
35 timeout=300)
3636 print("Subscription:", sub)
3737 print("--> Connection:", sub.connection)
3838 print("--> Callback:", sub.callback)
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1515 # This script requires cx_Oracle 5.3 and higher.
1616 #------------------------------------------------------------------------------
1717
18 from __future__ import print_function
19
2018 import cx_Oracle
21 import SampleEnv
19 import sample_env
2220
2321 # define constants used throughout the script; adjust as desired
2422 APP_CTX_NAMESPACE = "CLIENTCONTEXT"
2826 ( APP_CTX_NAMESPACE, "ATTR3", "VALUE3" )
2927 ]
3028
31 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(),
32 appcontext = APP_CTX_ENTRIES)
29 connection = cx_Oracle.connect(sample_env.get_main_connect_string(),
30 appcontext=APP_CTX_ENTRIES)
3331 cursor = connection.cursor()
3432 for namespace, name, value in APP_CTX_ENTRIES:
3533 cursor.execute("select sys_context(:1, :2) from dual", (namespace, name))
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
1212 # This script requires cx_Oracle 5.2 and higher.
1313 #------------------------------------------------------------------------------
1414
15 from __future__ import print_function
15 import cx_Oracle
16 import sample_env
1617
17 import cx_Oracle
18 import SampleEnv
19
20 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
18 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2119 cursor = connection.cursor()
2220
2321 # show the number of rows for each parent ID as a means of verifying the
2422 # output from the delete statement
25 for parentId, count in cursor.execute("""
23 for parent_id, count in cursor.execute("""
2624 select ParentId, count(*)
2725 from ChildTable
2826 group by ParentId
2927 order by ParentId"""):
30 print("Parent ID:", parentId, "has", int(count), "rows.")
28 print("Parent ID:", parent_id, "has", int(count), "rows.")
3129 print()
3230
3331 # delete the following parent IDs only
34 parentIdsToDelete = [20, 30, 50]
32 parent_ids_to_delete = [20, 30, 50]
3533
36 print("Deleting Parent IDs:", parentIdsToDelete)
34 print("Deleting Parent IDs:", parent_ids_to_delete)
3735 print()
3836
3937 # enable array DML row counts for each iteration executed in executemany()
4038 cursor.executemany("""
4139 delete from ChildTable
4240 where ParentId = :1""",
43 [(i,) for i in parentIdsToDelete],
41 [(i,) for i in parent_ids_to_delete],
4442 arraydmlrowcounts = True)
4543
4644 # display the number of rows deleted for each parent ID
47 rowCounts = cursor.getarraydmlrowcounts()
48 for parentId, count in zip(parentIdsToDelete, rowCounts):
49 print("Parent ID:", parentId, "deleted", count, "rows.")
45 row_counts = cursor.getarraydmlrowcounts()
46 for parent_id, count in zip(parent_ids_to_delete, row_counts):
47 print("Parent ID:", parent_id, "deleted", count, "rows.")
5048
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
1414 # This script requires cx_Oracle 5.2 and higher.
1515 #------------------------------------------------------------------------------
1616
17 from __future__ import print_function
17 import cx_Oracle
18 import sample_env
1819
19 import cx_Oracle
20 import SampleEnv
21
22 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
20 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2321 cursor = connection.cursor()
2422
2523 # define data to insert
26 dataToInsert = [
24 data_to_insert = [
2725 (1016, 10, 'Child B of Parent 10'),
2826 (1017, 10, 'Child C of Parent 10'),
2927 (1018, 20, 'Child D of Parent 20'),
4038 from ChildTable""")
4139 count, = cursor.fetchone()
4240 print("number of rows in child table:", int(count))
43 print("number of rows to insert:", len(dataToInsert))
41 print("number of rows to insert:", len(data_to_insert))
4442
4543 # old method: executemany() with data errors results in stoppage after the
4644 # first error takes place; the row count is updated to show how many rows
4745 # actually succeeded
4846 try:
4947 cursor.executemany("insert into ChildTable values (:1, :2, :3)",
50 dataToInsert)
48 data_to_insert)
5149 except cx_Oracle.DatabaseError as e:
5250 error, = e.args
5351 print("FAILED with error:", error.message)
6563
6664 # new method: executemany() with batch errors enabled (and array DML row counts
6765 # also enabled) results in no immediate error being raised
68 cursor.executemany("insert into ChildTable values (:1, :2, :3)", dataToInsert,
69 batcherrors = True, arraydmlrowcounts = True)
66 cursor.executemany("insert into ChildTable values (:1, :2, :3)",
67 data_to_insert, batcherrors=True, arraydmlrowcounts=True)
7068
7169 # where errors have taken place, the row count is 0; otherwise it is 1
72 rowCounts = cursor.getarraydmlrowcounts()
73 print("Array DML row counts:", rowCounts)
70 row_counts = cursor.getarraydmlrowcounts()
71 print("Array DML row counts:", row_counts)
7472
7573 # display the errors that have taken place
7674 errors = cursor.getbatcherrors()
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
77 # Demonstrate how to insert a row into a table using bind variables.
88 #------------------------------------------------------------------------------
99
10 from __future__ import print_function
10 import cx_Oracle
11 import sample_env
1112
12 import cx_Oracle
13 import SampleEnv
13 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
1414
15 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
15 #------------------------------------------------------------------------------
16 # "Bind by position"
17 #------------------------------------------------------------------------------
1618
17 rows = [ (1, "First" ),
18 (2, "Second" ),
19 (3, "Third" ),
20 (4, "Fourth" ),
21 (5, "Fifth" ),
22 (6, "Sixth" ),
23 (7, "Seventh" ) ]
19 rows = [
20 (1, "First"),
21 (2, "Second"),
22 (3, "Third"),
23 (4, "Fourth"),
24 (5, None), # Insert a NULL value
25 (6, "Sixth"),
26 (7, "Seventh")
27 ]
2428
2529 cursor = connection.cursor()
30
31 # predefine maximum string size to avoid data scans and memory reallocations;
32 # the None value indicates that the default processing can take place
33 cursor.setinputsizes(None, 20)
34
2635 cursor.executemany("insert into mytab(id, data) values (:1, :2)", rows)
2736
28 # Don't commit - this lets us run the demo multiple times
37 #------------------------------------------------------------------------------
38 # "Bind by name"
39 #------------------------------------------------------------------------------
40
41 rows = [
42 {"d": "Eighth", "i": 8},
43 {"d": "Ninth", "i": 9},
44 {"d": "Tenth", "i": 10}
45 ]
46
47 cursor = connection.cursor()
48
49 # Predefine maximum string size to avoid data scans and memory reallocations
50 cursor.setinputsizes(d=20)
51
52 cursor.executemany("insert into mytab(id, data) values (:i, :d)", rows)
53
54 #------------------------------------------------------------------------------
55 # Inserting a single bind still needs tuples
56 #------------------------------------------------------------------------------
57
58 rows = [
59 ("Eleventh",),
60 ("Twelth",)
61 ]
62
63 cursor = connection.cursor()
64 cursor.executemany("insert into mytab(id, data) values (11, :1)", rows)
65
66 #------------------------------------------------------------------------------
67 # Now query the results back
68 #------------------------------------------------------------------------------
69
70 # Don't commit - this lets the demo be run multiple times
2971 #connection.commit()
30
31 # Now query the results back
3272
3373 for row in cursor.execute('select * from mytab'):
3474 print(row)
35
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
1111 # special characters or SQL injection attacks.
1212 #------------------------------------------------------------------------------
1313
14 from __future__ import print_function
14 import cx_Oracle
15 import sample_env
1516
16 import cx_Oracle
17 import SampleEnv
18
19 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
17 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2018
2119 cursor = connection.cursor()
2220 sql = 'select * from SampleQueryTab where id = :bvid'
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 #------------------------------------------------------------------------------
10 # BulkAQ.py
11 # This script demonstrates how to use bulk enqueuing and dequeuing of
12 # messages with advanced queuing using cx_Oracle. It makes use of a RAW queue
13 # created in the sample setup.
14 #
15 # This script requires cx_Oracle 7.2 and higher.
16 #------------------------------------------------------------------------------
17
18 import cx_Oracle
19 import sample_env
20
21 QUEUE_NAME = "DEMO_RAW_QUEUE"
22 PAYLOAD_DATA = [
23 "The first message",
24 "The second message",
25 "The third message",
26 "The fourth message",
27 "The fifth message",
28 "The sixth message",
29 "The seventh message",
30 "The eighth message",
31 "The ninth message",
32 "The tenth message",
33 "The eleventh message",
34 "The twelfth and final message"
35 ]
36
37 # connect to database
38 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
39 cursor = connection.cursor()
40
41 # create queue
42 queue = connection.queue(QUEUE_NAME)
43 queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
44 queue.deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
45
46 # dequeue all existing messages to ensure the queue is empty, just so that
47 # the results are consistent
48 while queue.deqOne():
49 pass
50
51 # enqueue a few messages
52 print("Enqueuing messages...")
53 batch_size = 6
54 data_to_enqueue = PAYLOAD_DATA
55 while data_to_enqueue:
56 batch_data = data_to_enqueue[:batch_size]
57 data_to_enqueue = data_to_enqueue[batch_size:]
58 messages = [connection.msgproperties(payload=d) for d in batch_data]
59 for data in batch_data:
60 print(data)
61 queue.enqMany(messages)
62 connection.commit()
63
64 # dequeue the messages
65 print("\nDequeuing messages...")
66 batch_size = 8
67 while True:
68 messages = queue.deqMany(batch_size)
69 if not messages:
70 break
71 for props in messages:
72 print(props.payload.decode())
73 connection.commit()
74 print("\nDone.")
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1616 # This script requires cx_Oracle 5.3 and higher.
1717 #------------------------------------------------------------------------------
1818
19 from __future__ import print_function
20
2119 import cx_Oracle
22 import SampleEnv
23 import threading
20 import sample_env
2421 import time
2522
2623 registered = True
4946 print("-" * 60)
5047 print("=" * 60)
5148
52 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(), events = True)
49 connection = cx_Oracle.connect(sample_env.get_main_connect_string(),
50 events=True)
5351 sub = connection.subscribe(callback = callback, timeout = 1800,
5452 qos = cx_Oracle.SUBSCR_QOS_QUERY | cx_Oracle.SUBSCR_QOS_ROWIDS)
5553 print("Subscription:", sub)
6664 while registered:
6765 print("Waiting for notifications....")
6866 time.sleep(5)
69
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # CQN2.py
6 # This script demonstrates using continuous query notification in Python, a
7 # feature that is available in Oracle 11g and later. Once this script is
8 # running, use another session to insert, update or delete rows from the table
9 # cx_Oracle.TestTempTable and you will see the notification of that change.
10 #
11 # This script differs from CQN.py in that it shows how a connection can be
12 # acquired from a session pool and used to query the changes that have been
13 # made.
14 #
15 # This script requires cx_Oracle 7 or higher.
16 #------------------------------------------------------------------------------
17
18 import cx_Oracle
19 import sample_env
20 import time
21
22 registered = True
23
24 def callback(message):
25 global registered
26 if not message.registered:
27 print("Deregistration has taken place...")
28 registered = False
29 return
30 connection = pool.acquire()
31 for query in message.queries:
32 for table in query.tables:
33 if table.rows is None:
34 print("Too many row changes detected in table", table.name)
35 continue
36 num_rows_deleted = 0
37 print(len(table.rows), "row changes detected in table", table.name)
38 for row in table.rows:
39 if row.operation & cx_Oracle.OPCODE_DELETE:
40 num_rows_deleted += 1
41 continue
42 ops = []
43 if row.operation & cx_Oracle.OPCODE_INSERT:
44 ops.append("inserted")
45 if row.operation & cx_Oracle.OPCODE_UPDATE:
46 ops.append("updated")
47 cursor = connection.cursor()
48 cursor.execute("""
49 select IntCol
50 from TestTempTable
51 where rowid = :rid""",
52 rid=row.rowid)
53 int_col, = cursor.fetchone()
54 print(" Row with IntCol", int_col, "was", " and ".join(ops))
55 if num_rows_deleted > 0:
56 print(" ", num_rows_deleted, "rows deleted")
57 print("=" * 60)
58
59 pool = cx_Oracle.SessionPool(user=sample_env.get_main_user(),
60 password=sample_env.get_main_password(),
61 dsn=sample_env.get_connect_string(), min=2,
62 max=5, increment=1, events=True, threaded=True)
63 with pool.acquire() as connection:
64 sub = connection.subscribe(callback=callback, timeout=1800,
65 qos=cx_Oracle.SUBSCR_QOS_QUERY | cx_Oracle.SUBSCR_QOS_ROWIDS)
66 print("Subscription created with ID:", sub.id)
67 query_id = sub.registerquery("select * from TestTempTable")
68 print("Registered query with ID:", query_id)
69
70 while registered:
71 print("Waiting for notifications....")
72 time.sleep(5)
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # CallTimeout.py
6 #
7 # Demonstrate the use of the Oracle Client 18c feature that enables round trips
8 # to the database to time out if a specified amount of time (in milliseconds)
9 # has passed without a response from the database.
10 #
11 # This script requires cx_Oracle 7.0 and higher and Oracle Client 18.1 and
12 # higher.
13 #------------------------------------------------------------------------------
14
15 import cx_Oracle
16 import sample_env
17
18 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
19 connection.callTimeout = 2000
20 print("Call timeout set at", connection.callTimeout, "milliseconds...")
21
22 cursor = connection.cursor()
23 cursor.execute("select sysdate from dual")
24 today, = cursor.fetchone()
25 print("Fetch of current date before timeout:", today)
26
27 # dbms_session.sleep() replaces dbms_lock.sleep() from Oracle Database 18c
28 sleep_proc_name = "dbms_session.sleep" \
29 if int(connection.version.split(".")[0]) >= 18 \
30 else "dbms_lock.sleep"
31
32 print("Sleeping...should time out...")
33 try:
34 cursor.callproc(sleep_proc_name, (3,))
35 except cx_Oracle.DatabaseError as e:
36 print("ERROR:", e)
37
38 cursor.execute("select sysdate from dual")
39 today, = cursor.fetchone()
40 print("Fetch of current date after timeout:", today)
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # ConnectionPool.py
6 # This script demonstrates the use of connection pooling in cx_Oracle. Pools
7 # can significantly reduce connection times for long running applications that
8 # repeatedly open and close connections. Internal features help protect against
9 # dead connections, and also aid use of Oracle Database features such as FAN
10 # and Application Continuity.
11 # The script uses threading to show multiple users of the pool. One thread
12 # performs a database sleep while another performs a query. A more typical
13 # application might be a web service that handles requests from multiple users.
14 # Applications that use connections concurrently in multiple threads should set
15 # the 'threaded' parameter to True. Note only one operation (such as an execute
16 # or fetch) can take place at a time on each connection.
17 #
18 # Also see SessionCallback.py.
19 #
20 #------------------------------------------------------------------------------
21
22 import cx_Oracle
23 import sample_env
24 import threading
25
26 # Create a Connection Pool
27 pool = cx_Oracle.SessionPool(user=sample_env.get_main_user(),
28 password=sample_env.get_main_password(),
29 dsn=sample_env.get_connect_string(), min=2,
30 max=5, increment=1, threaded=True)
31
32 # dbms_session.sleep() replaces dbms_lock.sleep() from Oracle Database 18c
33 with pool.acquire() as conn:
34 sleep_proc_name = "dbms_session.sleep" \
35 if int(conn.version.split(".")[0]) >= 18 \
36 else "dbms_lock.sleep"
37
38 def TheLongQuery():
39 with pool.acquire() as conn:
40 cursor = conn.cursor()
41 cursor.arraysize = 25000
42 print("TheLongQuery(): beginning execute...")
43 cursor.execute("""
44 select *
45 from
46 TestNumbers
47 cross join TestNumbers
48 cross join TestNumbers
49 cross join TestNumbers
50 cross join TestNumbers
51 cross join TestNumbers""")
52 print("TheLongQuery(): done execute...")
53 while True:
54 rows = cursor.fetchmany()
55 if not rows:
56 break
57 print("TheLongQuery(): fetched", len(rows), "rows...")
58 print("TheLongQuery(): all done!")
59
60
61 def DoALock():
62 with pool.acquire() as conn:
63 cursor = conn.cursor()
64 print("DoALock(): beginning execute...")
65 cursor.callproc(sleep_proc_name, (5,))
66 print("DoALock(): done execute...")
67
68
69 thread1 = threading.Thread(None, TheLongQuery)
70 thread1.start()
71
72 thread2 = threading.Thread(None, DoALock)
73 thread2.start()
74
75 thread1.join()
76 thread2.join()
77
78 print("All done!")
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1414 # This script requires cx_Oracle 6.0 and higher.
1515 #------------------------------------------------------------------------------
1616
17 from __future__ import print_function
18
1917 import cx_Oracle
2018 import datetime
21 import SampleEnv
19 import sample_env
2220
2321 # truncate table first so that script can be rerun
24 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
22 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2523 cursor = connection.cursor()
2624 print("Truncating table...")
2725 cursor.execute("truncate table TestTempTable")
3331 cursor.execute("insert into TestTempTable values (:1, :2)", data)
3432
3533 # now delete them and use DML returning to return the data that was inserted
36 intCol = cursor.var(int)
37 stringCol = cursor.var(str)
34 int_col = cursor.var(int)
35 string_col = cursor.var(str)
3836 print("Deleting data with DML returning...")
3937 cursor.execute("""
4038 delete from TestTempTable
41 returning IntCol, StringCol into :intCol, :stringCol""",
42 intCol = intCol,
43 stringCol = stringCol)
39 returning IntCol, StringCol into :int_col, :string_col""",
40 int_col=int_col,
41 string_col=string_col)
4442 print("Data returned:")
45 for intVal, stringVal in zip(intCol.getvalue(), stringCol.getvalue()):
46 print(tuple([intVal, stringVal]))
47
43 for int_val, string_val in zip(int_col.getvalue(), string_col.getvalue()):
44 print(tuple([int_val, string_val]))
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
2424 # There is no difference in how a connection is used once it has been
2525 # established.
2626 #
27 # This script requires cx_Oracle 5.0 and higher.
27 # DRCP has most benefit when used in conjunction with cx_Oracle's local
28 # connection pool, see the cx_Oracle documentation.
29 #
30 # This script requires cx_Oracle 5.0 or higher.
31 #
2832 #------------------------------------------------------------------------------
2933
30 from __future__ import print_function
34 import cx_Oracle
35 import sample_env
3136
32 import cx_Oracle
33 import SampleEnv
34
35 conn = cx_Oracle.connect(SampleEnv.GetDrcpConnectString(), cclass = "PYCLASS",
36 purity = cx_Oracle.ATTR_PURITY_SELF)
37 conn = cx_Oracle.connect(sample_env.get_drcp_connect_string(),
38 cclass="PYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF)
3739 cursor = conn.cursor()
3840 print("Performing query using DRCP...")
3941 for row in cursor.execute("select * from TestNumbers order by IntCol"):
4042 print(row)
41
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1616 # This script requires cx_Oracle 5.3 and higher.
1717 #------------------------------------------------------------------------------
1818
19 from __future__ import print_function
20
2119 import cx_Oracle
22 import SampleEnv
23 import threading
20 import sample_env
2421 import time
2522
2623 registered = True
4643 print("-" * 60)
4744 print("=" * 60)
4845
49 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(), events = True)
46 connection = cx_Oracle.connect(sample_env.get_main_connect_string(),
47 events=True)
5048 sub = connection.subscribe(callback = callback, timeout = 1800,
5149 qos = cx_Oracle.SUBSCR_QOS_ROWIDS)
5250 print("Subscription:", sub)
5351 print("--> Connection:", sub.connection)
52 print("--> ID:", sub.id)
5453 print("--> Callback:", sub.callback)
5554 print("--> Namespace:", sub.namespace)
5655 print("--> Protocol:", sub.protocol)
6261 while registered:
6362 print("Waiting for notifications....")
6463 time.sleep(5)
65
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1818 import cx_Oracle
1919
2020 # need to connect as SYSDBA or SYSOPER
21 connection = cx_Oracle.connect("/", mode = cx_Oracle.SYSDBA)
21 connection = cx_Oracle.connect("/", mode=cx_Oracle.SYSDBA)
2222
2323 # first shutdown() call must specify the mode, if DBSHUTDOWN_ABORT is used,
2424 # there is no need for any of the other steps
3131
3232 # perform the final shutdown call
3333 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_FINAL)
34
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1919
2020 # the connection must be in PRELIM_AUTH mode
2121 connection = cx_Oracle.connect("/",
22 mode = cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
22 mode=cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
2323 connection.startup()
2424
2525 # the following statements must be issued in normal SYSDBA mode
26 connection = cx_Oracle.connect("/", mode = cx_Oracle.SYSDBA)
26 connection = cx_Oracle.connect("/", mode=cx_Oracle.SYSDBA)
2727 cursor = connection.cursor()
2828 cursor.execute("alter database mount")
2929 cursor.execute("alter database open")
30
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # DbmsOutput.py
6 # This script demonstrates one method of fetching the lines produced by
7 # the DBMS_OUTPUT package.
8 #------------------------------------------------------------------------------
9
10 import cx_Oracle
11 import sample_env
12
13 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
14 cursor = connection.cursor()
15
16 # enable DBMS_OUTPUT
17 cursor.callproc("dbms_output.enable")
18
19 # execute some PL/SQL that generates output with DBMS_OUTPUT.PUT_LINE
20 cursor.execute("""
21 begin
22 dbms_output.put_line('This is the cx_Oracle manual');
23 dbms_output.put_line('');
24 dbms_output.put_line('Demonstrating use of DBMS_OUTPUT');
25 end;""")
26
27 # tune this size for your application
28 chunk_size = 10
29
30 # create variables to hold the output
31 lines_var = cursor.arrayvar(str, chunk_size)
32 num_lines_var = cursor.var(int)
33 num_lines_var.setvalue(0, chunk_size)
34
35 # fetch the text that was added by PL/SQL
36 while True:
37 cursor.callproc("dbms_output.get_lines", (lines_var, num_lines_var))
38 num_lines = num_lines_var.getvalue()
39 lines = lines_var.getvalue()[:num_lines]
40 for line in lines:
41 print(line or "")
42 if num_lines < chunk_size:
43 break
77 # Drops the database objects used for the cx_Oracle samples.
88 #------------------------------------------------------------------------------
99
10 from __future__ import print_function
10 import cx_Oracle
11 import sample_env
1112
12 import cx_Oracle
13 import SampleEnv
14
15 def DropSamples(conn):
13 def drop_samples(conn):
1614 print("Dropping sample schemas and edition...")
17 SampleEnv.RunSqlScript(conn, "DropSamples",
18 main_user = SampleEnv.GetMainUser(),
19 edition_user = SampleEnv.GetEditionUser(),
20 edition_name = SampleEnv.GetEditionName())
15 sample_env.run_sql_script(conn, "DropSamples",
16 main_user=sample_env.get_main_user(),
17 edition_user=sample_env.get_edition_user(),
18 edition_name=sample_env.get_edition_name())
2119
2220 if __name__ == "__main__":
23 conn = cx_Oracle.connect(SampleEnv.GetSysdbaConnectString(),
24 mode = cx_Oracle.SYSDBA)
25 DropSamples(conn)
21 conn = cx_Oracle.connect(sample_env.get_admin_connect_string())
22 drop_samples(conn)
2623 print("Done.")
27
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1616 # This script requires cx_Oracle 5.3 and higher.
1717 #------------------------------------------------------------------------------
1818
19 from __future__ import print_function
20
2119 import cx_Oracle
22 import SampleEnv
20 import sample_env
2321 import os
2422
2523 # connect to the editions user and create a procedure
26 editionConnectString = SampleEnv.GetEditionConnectString()
27 connection = cx_Oracle.connect(editionConnectString)
28 print("Edition should be None, actual value is:",
29 repr(connection.edition))
24 edition_connect_string = sample_env.get_edition_connect_string()
25 edition_name = sample_env.get_edition_name()
26 connection = cx_Oracle.connect(edition_connect_string)
27 print("Edition should be None, actual value is:", repr(connection.edition))
3028 cursor = connection.cursor()
3129 cursor.execute("""
3230 create or replace function TestEditions return varchar2 as
3533 end;""")
3634 result = cursor.callfunc("TestEditions", str)
3735 print("Function should return 'Base Procedure', actually returns:",
38 repr(result))
36 repr(result))
3937
4038 # next, change the edition and recreate the procedure in the new edition
41 cursor.execute("alter session set edition = %s" % SampleEnv.GetEditionName())
42 print("Edition should be", repr(SampleEnv.GetEditionName().upper()),
43 "actual value is:", repr(connection.edition))
39 cursor.execute("alter session set edition = %s" % edition_name)
40 print("Edition should be", repr(edition_name.upper()),
41 "actual value is:", repr(connection.edition))
4442 cursor.execute("""
4543 create or replace function TestEditions return varchar2 as
4644 begin
4846 end;""")
4947 result = cursor.callfunc("TestEditions", str)
5048 print("Function should return 'Edition 1 Procedure', actually returns:",
51 repr(result))
49 repr(result))
5250
5351 # next, change the edition back to the base edition and demonstrate that the
5452 # original function is being called
5553 cursor.execute("alter session set edition = ORA$BASE")
5654 result = cursor.callfunc("TestEditions", str)
5755 print("Function should return 'Base Procedure', actually returns:",
58 repr(result))
56 repr(result))
5957
6058 # the edition can be set upon connection
61 connection = cx_Oracle.connect(editionConnectString,
62 edition = SampleEnv.GetEditionName().upper())
59 connection = cx_Oracle.connect(edition_connect_string,
60 edition=edition_name.upper())
6361 cursor = connection.cursor()
6462 result = cursor.callfunc("TestEditions", str)
6563 print("Function should return 'Edition 1 Procedure', actually returns:",
66 repr(result))
64 repr(result))
6765
6866 # it can also be set via the environment variable ORA_EDITION
69 os.environ["ORA_EDITION"] = SampleEnv.GetEditionName().upper()
70 connection = cx_Oracle.connect(editionConnectString)
71 print("Edition should be", repr(SampleEnv.GetEditionName().upper()),
72 "actual value is:", repr(connection.edition))
67 os.environ["ORA_EDITION"] = edition_name.upper()
68 connection = cx_Oracle.connect(edition_connect_string)
69 print("Edition should be", repr(edition_name.upper()),
70 "actual value is:", repr(connection.edition))
7371 cursor = connection.cursor()
7472 result = cursor.callfunc("TestEditions", str)
7573 print("Function should return 'Edition 1 Procedure', actually returns:",
76 repr(result))
77
74 repr(result))
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
88 # subclassed cursor and row factory.
99 #------------------------------------------------------------------------------
1010
11 from __future__ import print_function
12
1311 import collections
1412 import cx_Oracle
15 import SampleEnv
13 import sample_env
1614
1715 class Connection(cx_Oracle.Connection):
1816
2321 class Cursor(cx_Oracle.Cursor):
2422
2523 def execute(self, statement, args = None):
26 prepareNeeded = (self.statement != statement)
24 prepare_needed = (self.statement != statement)
2725 result = super(Cursor, self).execute(statement, args or [])
28 if prepareNeeded:
26 if prepare_needed:
2927 description = self.description
3028 if description:
3129 names = [d[0] for d in description]
3432
3533
3634 # create new subclassed connection and cursor
37 connection = Connection(SampleEnv.GetMainConnectString())
35 connection = Connection(sample_env.get_main_connect_string())
3836 cursor = connection.cursor()
3937
4038 # the names are now available directly for each query executed
4543 for row in cursor.execute("select ChildId, Description from ChildTable"):
4644 print(row.CHILDID, "->", row.DESCRIPTION)
4745 print()
48
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1515 # This script requires cx_Oracle 5.3 and higher.
1616 #------------------------------------------------------------------------------
1717
18 from __future__ import print_function
18 import cx_Oracle
19 import sample_env
1920
20 import cx_Oracle
21 import SampleEnv
22
23 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
21 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2422 cursor = connection.cursor()
2523
2624 # use PL/SQL block to return two cursors
4341 end;""")
4442
4543 # display results
46 for ix, resultSet in enumerate(cursor.getimplicitresults()):
44 for ix, result_set in enumerate(cursor.getimplicitresults()):
4745 print("Result Set #" + str(ix + 1))
48 for row in resultSet:
46 for row in result_set:
4947 print(row)
5048 print()
51
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1414 # This script requires cx_Oracle 5.3 and higher.
1515 #------------------------------------------------------------------------------
1616
17 from __future__ import print_function
18
1917 import cx_Oracle
20 import SampleEnv
18 import sample_env
2119
2220 # create and populate Oracle objects
23 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
24 typeObj = connection.gettype("MDSYS.SDO_GEOMETRY")
25 elementInfoTypeObj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
26 ordinateTypeObj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY")
27 obj = typeObj.newobject()
21 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
22 type_obj = connection.gettype("MDSYS.SDO_GEOMETRY")
23 element_info_type_obj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
24 ordinate_type_obj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY")
25 obj = type_obj.newobject()
2826 obj.SDO_GTYPE = 2003
29 obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
27 obj.SDO_ELEM_INFO = element_info_type_obj.newobject()
3028 obj.SDO_ELEM_INFO.extend([1, 1003, 3])
31 obj.SDO_ORDINATES = ordinateTypeObj.newobject()
29 obj.SDO_ORDINATES = ordinate_type_obj.newobject()
3230 obj.SDO_ORDINATES.extend([1, 1, 5, 7])
3331 print("Created object", obj)
3432
5149 print("Removing any existing rows...")
5250 cursor.execute("delete from TestGeometry")
5351 print("Adding row to table...")
54 cursor.execute("insert into TestGeometry values (1, :obj)", obj = obj)
52 cursor.execute("insert into TestGeometry values (1, :obj)", obj=obj)
5553 connection.commit()
5654 print("Success!")
57
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # JSON.py
6 # Shows some JSON features of Oracle Database 21c.
7 # See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
8 #
9 # For JSON with older databases see JSONBLOB.py
10 #------------------------------------------------------------------------------
11
12 import sys
13 import json
14 import cx_Oracle
15 import sample_env
16
17 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
18
19 client_version = cx_Oracle.clientversion()[0]
20 db_version = int(connection.version.split(".")[0])
21
22 # this script only works with Oracle Database 21
23
24 if db_version < 21:
25 sys.exit("This example requires Oracle Database 21.1 or later. "
26 "Try JSONBLOB.py")
27
28 # Create a table
29
30 cursor = connection.cursor()
31 cursor.execute("""
32 begin
33 execute immediate 'drop table customers';
34 exception when others then
35 if sqlcode <> -942 then
36 raise;
37 end if;
38 end;""")
39 cursor.execute("""
40 create table customers (
41 id integer not null primary key,
42 json_data json
43 )""")
44
45 # Insert JSON data
46
47 data = dict(name="Rod", dept="Sales", location="Germany")
48 inssql = "insert into customers values (:1, :2)"
49 if client_version >= 21:
50 # Take advantage of direct binding
51 cursor.setinputsizes(None, cx_Oracle.DB_TYPE_JSON)
52 cursor.execute(inssql, [1, data])
53 else:
54 # Insert the data as a JSON string
55 cursor.execute(inssql, [1, json.dumps(data)])
56
57 # Select JSON data
58
59 sql = "SELECT c.json_data FROM customers c"
60 if client_version >= 21:
61 for j, in cursor.execute(sql):
62 print(j)
63 else:
64 for j, in cursor.execute(sql):
65 print(json.loads(j.read()))
66
67 # Using JSON_VALUE to extract a value from a JSON column
68
69 sql = """SELECT JSON_VALUE(json_data, '$.location')
70 FROM customers
71 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"""
72 for r in cursor.execute(sql):
73 print(r)
74
75 # Using dot-notation to extract a value from a JSON column
76
77 sql = """SELECT c.json_data.location
78 FROM customers c
79 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"""
80 if client_version >= 21:
81 for j, in cursor.execute(sql):
82 print(j)
83 else:
84 for j, in cursor.execute(sql):
85 print(json.loads(j.read()))
86
87 # Using JSON_OBJECT to extract relational data as JSON
88
89 sql = """SELECT JSON_OBJECT('key' IS d.dummy) dummy
90 FROM dual d"""
91 for r in cursor.execute(sql):
92 print(r)
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # JSONBLOB.py
6 # Shows how to use a BLOB as a JSON column store.
7 #
8 # Note: with Oracle Database 21c using the new JSON type is recommended
9 # instead, see JSON.py
10 #
11 # Documentation:
12 # cx_Oracle: https://cx-oracle.readthedocs.io/en/latest/user_guide/json_data_type.html
13 # Oracle Database: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
14 ##------------------------------------------------------------------------------
15
16 import sys
17 import json
18 import cx_Oracle
19 import sample_env
20
21 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
22
23 client_version = cx_Oracle.clientversion()[0]
24 db_version = int(connection.version.split(".")[0])
25
26 # Minimum database vesion is 12
27 if db_version < 12:
28 sys.exit("This example requires Oracle Database 12.1.0.2 or later")
29
30 # Create a table
31
32 cursor = connection.cursor()
33 cursor.execute("""
34 begin
35 execute immediate 'drop table customers';
36 exception when others then
37 if sqlcode <> -942 then
38 raise;
39 end if;
40 end;""")
41 cursor.execute("""
42 create table customers (
43 id integer not null primary key,
44 json_data blob check (json_data is json)
45 ) lob (json_data) store as (cache)""")
46
47 # Insert JSON data
48
49 data = dict(name="Rod", dept="Sales", location="Germany")
50 inssql = "insert into customers values (:1, :2)"
51 if client_version >= 21 and db_version >= 21:
52 # Take advantage of direct binding
53 cursor.setinputsizes(None, cx_Oracle.DB_TYPE_JSON)
54 cursor.execute(inssql, [1, data])
55 else:
56 # Insert the data as a JSON string
57 cursor.execute(inssql, [1, json.dumps(data)])
58
59 # Select JSON data
60
61 sql = "SELECT c.json_data FROM customers c"
62 for j, in cursor.execute(sql):
63 print(json.loads(j.read()))
64
65 # Using JSON_VALUE to extract a value from a JSON column
66
67 sql = """SELECT JSON_VALUE(json_data, '$.location')
68 FROM customers
69 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"""
70 for r in cursor.execute(sql):
71 print(r)
72
73 # Using dot-notation to extract a value from a JSON (BLOB storage) column
74
75 sql = """SELECT c.json_data.location
76 FROM customers c
77 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"""
78 for j, in cursor.execute(sql):
79 print(j)
80
81 # Using JSON_OBJECT to extract relational data as JSON
82
83 sql = """SELECT JSON_OBJECT('key' IS d.dummy) dummy
84 FROM dual d"""
85 for r in cursor.execute(sql):
86 print(r)
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # LastRowid.py
6 # Demonstrates the use of the cursor.lastrowid attribute.
7 #
8 # This script requires cx_Oracle 7.3 and higher.
9 #------------------------------------------------------------------------------
10
11 import cx_Oracle
12 import sample_env
13
14 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
15
16 row1 = [1, "First"]
17 row2 = [2, "Second"]
18
19 # insert a couple of rows and retain the rowid of each
20 cursor = connection.cursor()
21 cursor.execute("insert into mytab (id, data) values (:1, :2)", row1)
22 rowid1 = cursor.lastrowid
23 print("Row 1:", row1)
24 print("Rowid 1:", rowid1)
25 print()
26
27 cursor.execute("insert into mytab (id, data) values (:1, :2)", row2)
28 rowid2 = cursor.lastrowid
29 print("Row 2:", row2)
30 print("Rowid 2:", rowid2)
31 print()
32
33 # the row can be fetched with the rowid that was retained
34 cursor.execute("select id, data from mytab where rowid = :1", [rowid1])
35 print("Row 1:", cursor.fetchone())
36 cursor.execute("select id, data from mytab where rowid = :1", [rowid2])
37 print("Row 2:", cursor.fetchone())
38 print()
39
40 # updating multiple rows only returns the rowid of the last updated row
41 cursor.execute("update mytab set data = data || ' (Modified)'")
42 cursor.execute("select id, data from mytab where rowid = :1",
43 [cursor.lastrowid])
44 print("Last updated row:", cursor.fetchone())
45
46 # deleting multiple rows only returns the rowid of the last deleted row
47 cursor.execute("delete from mytab")
48 print("Rowid of last deleted row:", cursor.lastrowid)
49
50 # deleting no rows results in a value of None
51 cursor.execute("delete from mytab")
52 print("Rowid when no rows are deleted:", cursor.lastrowid)
53
54 # Don't commit - this lets us run the demo multiple times
55 #connection.commit()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 #------------------------------------------------------------------------------
10 # MultiConsumerAQ.py
11 # This script demonstrates how to use multi-consumer
12 # advanced queuing using cx_Oracle. It makes use of a RAW queue
13 # created in the sample setup.
14 #
15 # This script requires cx_Oracle 7.2 and higher.
16 #------------------------------------------------------------------------------
17
18 import cx_Oracle
19 import sample_env
20
21 QUEUE_NAME = "DEMO_RAW_QUEUE_MULTI"
22 PAYLOAD_DATA = [
23 "The first message",
24 "The second message",
25 "The third message",
26 "The fourth and final message"
27 ]
28
29 # connect to database
30 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
31 cursor = connection.cursor()
32
33 # create queue
34 queue = connection.queue(QUEUE_NAME)
35 queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
36 queue.deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
37
38 # enqueue a few messages
39 print("Enqueuing messages...")
40 for data in PAYLOAD_DATA:
41 print(data)
42 queue.enqOne(connection.msgproperties(payload=data))
43 connection.commit()
44 print()
45
46 # dequeue the messages for consumer A
47 print("Dequeuing the messages for consumer A...")
48 queue.deqOptions.consumername = "SUBSCRIBER_A"
49 while True:
50 props = queue.deqOne()
51 if not props:
52 break
53 print(props.payload.decode())
54 connection.commit()
55 print()
56
57 # dequeue the message for consumer B
58 print("Dequeuing the messages for consumer B...")
59 queue.deqOptions.consumername = "SUBSCRIBER_B"
60 while True:
61 props = queue.deqOne()
62 if not props:
63 break
64 print(props.payload.decode())
65 connection.commit()
66
67 print("\nDone.")
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 #------------------------------------------------------------------------------
10 # ObjectAQ.py
11 # This script demonstrates how to use advanced queuing with objects using
12 # cx_Oracle. It makes use of a simple type and queue created in the sample
13 # setup.
14 #
15 # This script requires cx_Oracle 7.2 and higher.
16 #------------------------------------------------------------------------------
17
18 import cx_Oracle
19 import sample_env
20 import decimal
21
22 BOOK_TYPE_NAME = "UDT_BOOK"
23 QUEUE_NAME = "DEMO_BOOK_QUEUE"
24 BOOK_DATA = [
25 ("The Fellowship of the Ring", "Tolkien, J.R.R.",
26 decimal.Decimal("10.99")),
27 ("Harry Potter and the Philosopher's Stone", "Rowling, J.K.",
28 decimal.Decimal("7.99"))
29 ]
30
31 # connect to database
32 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
33 cursor = connection.cursor()
34
35 # create queue
36 books_type = connection.gettype(BOOK_TYPE_NAME)
37 queue = connection.queue(QUEUE_NAME, books_type)
38 queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
39 queue.deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
40
41 # dequeue all existing messages to ensure the queue is empty, just so that
42 # the results are consistent
43 while queue.deqOne():
44 pass
45
46 # enqueue a few messages
47 print("Enqueuing messages...")
48 for title, authors, price in BOOK_DATA:
49 book = books_type.newobject()
50 book.TITLE = title
51 book.AUTHORS = authors
52 book.PRICE = price
53 print(title)
54 queue.enqOne(connection.msgproperties(payload=book))
55 connection.commit()
56
57 # dequeue the messages
58 print("\nDequeuing messages...")
59 while True:
60 props = queue.deqOne()
61 if not props:
62 break
63 print(props.payload.TITLE)
64 connection.commit()
65 print("\nDone.")
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
1212 # is new in cx_Oracle 7.0.
1313 #------------------------------------------------------------------------------
1414
15 from __future__ import print_function
15 import cx_Oracle
16 import sample_env
1617
17 import cx_Oracle
18 import SampleEnv
19
20 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
18 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2119
2220 # create new empty object of the correct type
2321 # note the use of a PL/SQL type defined in a package
24 typeObj = connection.gettype("PKG_DEMO.UDT_STRINGLIST")
25 obj = typeObj.newobject()
22 type_obj = connection.gettype("PKG_DEMO.UDT_STRINGLIST")
23 obj = type_obj.newobject()
2624
2725 # call the stored procedure which will populate the object
2826 cursor = connection.cursor()
4543 print("Values of collection as dictionary:")
4644 print(obj.asdict())
4745 print()
48
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
77 # Demonstrate how to call a PL/SQL function and get its return value.
88 #------------------------------------------------------------------------------
99
10 from __future__ import print_function
10 import cx_Oracle
11 import sample_env
1112
12 import cx_Oracle
13 import SampleEnv
14
15 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
13 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
1614
1715 cursor = connection.cursor()
1816 res = cursor.callfunc('myfunc', int, ('abc', 2))
1917 print(res)
20
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
88 # OUT variable.
99 #------------------------------------------------------------------------------
1010
11 from __future__ import print_function
11 import cx_Oracle
12 import sample_env
1213
13 import cx_Oracle
14 import SampleEnv
15
16 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
14 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
1715
1816 cursor = connection.cursor()
1917 myvar = cursor.var(int)
2018 cursor.callproc('myproc', (123, myvar))
2119 print(myvar.getvalue())
22
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
1010 # Database 12.1 and higher.
1111 #------------------------------------------------------------------------------
1212
13 from __future__ import print_function
14
1513 import cx_Oracle
16 import SampleEnv
14 import sample_env
1715 import datetime
1816
19 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
17 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2018
2119 # create new object of the correct type
2220 # note the use of a PL/SQL record defined in a package
2321 # a table record identified by TABLE%ROWTYPE can also be used
24 typeObj = connection.gettype("PKG_DEMO.UDT_DEMORECORD")
25 obj = typeObj.newobject()
22 type_obj = connection.gettype("PKG_DEMO.UDT_DEMORECORD")
23 obj = type_obj.newobject()
2624 obj.NUMBERVALUE = 6
2725 obj.STRINGVALUE = "Test String"
2826 obj.DATEVALUE = datetime.datetime(2016, 5, 28)
4543 print("DATEVALUE ->", obj.DATEVALUE)
4644 print("BOOLEANVALUE ->", obj.BOOLEANVALUE)
4745 print()
48
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
77 # Demonstrate how to perform a query in different ways.
88 #------------------------------------------------------------------------------
99
10 from __future__ import print_function
10 import cx_Oracle
11 import sample_env
1112
12 import cx_Oracle
13 import SampleEnv
14
15 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
13 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
1614
1715 sql = """
1816 select * from SampleQueryTab
3735 cursor.execute(sql)
3836 res = cursor.fetchmany(numRows=3)
3937 print(res)
38 print()
4039
40 print("Fetch each row as a Dictionary")
41 cursor.execute(sql)
42 columns = [col[0] for col in cursor.description]
43 cursor.rowfactory = lambda *args: dict(zip(columns, args))
44 for row in cursor:
45 print(row)
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
55 # QueryArraysize.py
66 #
7 # Demonstrate how to alter the array size on a cursor in order to reduce the
8 # number of network round trips and overhead required to fetch all of the rows
9 # from a large table.
7 # Demonstrate how to alter the array size and prefetch rows value on a cursor
8 # in order to reduce the number of network round trips and overhead required to
9 # fetch all of the rows from a large table.
1010 #------------------------------------------------------------------------------
11
12 from __future__ import print_function
1311
1412 import time
1513 import cx_Oracle
16 import SampleEnv
14 import sample_env
1715
18 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
16 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
1917
2018 start = time.time()
2119
2220 cursor = connection.cursor()
21 cursor.prefetchrows = 1000
2322 cursor.arraysize = 1000
2423 cursor.execute('select * from bigtab')
2524 res = cursor.fetchall()
2726
2827 elapsed = (time.time() - start)
2928 print("Retrieved", len(res), "rows in", elapsed, "seconds")
30
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 #------------------------------------------------------------------------------
10 # RawAQ.py
11 # This script demonstrates how to use advanced queuing with RAW data using
12 # cx_Oracle. It makes use of a RAW queue created in the sample setup.
13 #
14 # This script requires cx_Oracle 7.2 and higher.
15 #------------------------------------------------------------------------------
16
17 import cx_Oracle
18 import sample_env
19
20 QUEUE_NAME = "DEMO_RAW_QUEUE"
21 PAYLOAD_DATA = [
22 "The first message",
23 "The second message",
24 "The third message",
25 "The fourth and final message"
26 ]
27
28 # connect to database
29 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
30 cursor = connection.cursor()
31
32 # create queue
33 queue = connection.queue(QUEUE_NAME)
34 queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
35 queue.deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
36
37 # dequeue all existing messages to ensure the queue is empty, just so that
38 # the results are consistent
39 while queue.deqOne():
40 pass
41
42 # enqueue a few messages
43 print("Enqueuing messages...")
44 for data in PAYLOAD_DATA:
45 print(data)
46 queue.enqOne(connection.msgproperties(payload=data))
47 connection.commit()
48
49 # dequeue the messages
50 print("\nDequeuing messages...")
51 while True:
52 props = queue.deqOne()
53 if not props:
54 break
55 print(props.payload.decode())
56 connection.commit()
57 print("\nDone.")
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
66 # Demonstrates the use of REF cursors with cx_Oracle.
77 #------------------------------------------------------------------------------
88
9 from __future__ import print_function
9 import cx_Oracle
10 import sample_env
1011
11 import cx_Oracle
12 import SampleEnv
13
14 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
12 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
1513 cursor = connection.cursor()
1614
17 refCursor = connection.cursor()
18 cursor.callproc("myrefcursorproc", (2, 6, refCursor))
15 ref_cursor = connection.cursor()
16 cursor.callproc("myrefcursorproc", (2, 6, ref_cursor))
1917 print("Rows between 2 and 6:")
20 for row in refCursor:
18 for row in ref_cursor:
2119 print(row)
2220 print()
2321
24 refCursor = connection.cursor()
25 cursor.callproc("myrefcursorproc", (8, 9, refCursor))
22 ref_cursor = connection.cursor()
23 cursor.callproc("myrefcursorproc", (8, 9, ref_cursor))
2624 print("Rows between 8 and 9:")
27 for row in refCursor:
25 for row in ref_cursor:
2826 print(row)
2927 print()
3028
29 #------------------------------------------------------------------------------
30 # Setting prefetchrows and arraysize of a REF cursor can improve performance
31 # when fetching a large number of rows (Tuned Fetch)
32 #------------------------------------------------------------------------------
33
34 # Truncate the table used for this demo
35 cursor.execute("truncate table TestTempTable")
36
37 # Populate the table with a large number of rows
38 num_rows = 50000
39 sql = "insert into TestTempTable (IntCol) values (:1)"
40 data = [(n + 1,) for n in range(num_rows)]
41 cursor.executemany(sql, data)
42
43 # Set the arraysize and prefetch rows of the REF cursor
44 ref_cursor = connection.cursor()
45 ref_cursor.prefetchrows = 1000
46 ref_cursor.arraysize = 1000
47
48 # Perform the tuned fetch
49 sum_rows = 0
50 cursor.callproc("myrefcursorproc2", [ref_cursor])
51 print("Sum of IntCol for", num_rows, "rows:")
52 for row in ref_cursor:
53 sum_rows += row[0]
54 print(sum_rows)
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1717 # This script requires cx_Oracle 5.0 and higher.
1818 #------------------------------------------------------------------------------
1919
20 from __future__ import print_function
20 import cx_Oracle
21 import sample_env
2122
22 import cx_Oracle
23 import SampleEnv
23 def output_type_handler(cursor, name, default_type, size, precision, scale):
24 if default_type == cx_Oracle.CLOB:
25 return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize)
26 if default_type == cx_Oracle.BLOB:
27 return cursor.var(cx_Oracle.LONG_BINARY, arraysize=cursor.arraysize)
2428
25 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
26 if defaultType == cx_Oracle.CLOB:
27 return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize)
28 if defaultType == cx_Oracle.BLOB:
29 return cursor.var(cx_Oracle.LONG_BINARY, arraysize = cursor.arraysize)
30
31 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
32 connection.outputtypehandler = OutputTypeHandler
29 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
30 connection.outputtypehandler = output_type_handler
3331 cursor = connection.cursor()
3432
3533 # add some data to the tables
3634 print("Populating tables with data...")
3735 cursor.execute("truncate table TestClobs")
3836 cursor.execute("truncate table TestBlobs")
39 longString = ""
37 long_string = ""
4038 for i in range(10):
4139 char = chr(ord('A') + i)
42 longString += char * 25000
40 long_string += char * 25000
4341 # uncomment the line below for cx_Oracle 5.3 and earlier
4442 # cursor.setinputsizes(None, cx_Oracle.LONG_STRING)
4543 cursor.execute("insert into TestClobs values (:1, :2)",
46 (i + 1, "STRING " + longString))
44 (i + 1, "STRING " + long_string))
4745 # uncomment the line below for cx_Oracle 5.3 and earlier
4846 # cursor.setinputsizes(None, cx_Oracle.LONG_BINARY)
4947 cursor.execute("insert into TestBlobs values (:1, :2)",
50 (i + 1, longString.encode("ascii")))
48 (i + 1, long_string.encode("ascii")))
5149 connection.commit()
5250
5351 # fetch the data and show the results
5856 ClobCol
5957 from TestClobs
6058 order by IntCol""")
61 for intCol, value in cursor:
62 print("Row:", intCol, "string of length", len(value))
59 for int_col, value in cursor:
60 print("Row:", int_col, "string of length", len(value))
6361 print()
6462 print("BLOBS returned as bytes")
6563 cursor.execute("""
6866 BlobCol
6967 from TestBlobs
7068 order by IntCol""")
71 for intCol, value in cursor:
72 print("Row:", intCol, "string of length", value and len(value) or 0)
73
69 for int_col, value in cursor:
70 print("Row:", int_col, "string of length", value and len(value) or 0)
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
1313 # This script requires cx_Oracle 5.0 and higher.
1414 #------------------------------------------------------------------------------
1515
16 from __future__ import print_function
17
1816 import cx_Oracle
1917 import decimal
20 import SampleEnv
18 import sample_env
2119
22 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
20 def output_type_handler(cursor, name, defaultType, size, precision, scale):
2321 if defaultType == cx_Oracle.NUMBER:
2422 return cursor.var(decimal.Decimal, arraysize = cursor.arraysize)
2523
26 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
27 connection.outputtypehandler = OutputTypeHandler
24 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
25 connection.outputtypehandler = output_type_handler
2826 cursor = connection.cursor()
2927 cursor.execute("select * from TestNumbers")
3028 for row in cursor:
3129 print("Row:", row)
32
+0
-33
samples/ReturnUnicode.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 #------------------------------------------------------------------------------
10 # ReturnUnicode.py
11 # Returns all strings as unicode. This also demonstrates the use of an output
12 # type handler to change the way in which data is returned from a cursor.
13 #
14 # This script requires cx_Oracle 5.0 and higher and will only work in Python 2.
15 #------------------------------------------------------------------------------
16
17 from __future__ import print_function
18
19 import cx_Oracle
20 import SampleEnv
21
22 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
23 if defaultType in (cx_Oracle.STRING, cx_Oracle.FIXED_CHAR):
24 return cursor.var(unicode, size, cursor.arraysize)
25
26 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
27 connection.outputtypehandler = OutputTypeHandler
28 cursor = connection.cursor()
29 cursor.execute("select * from TestStrings")
30 for row in cursor:
31 print("Row:", row)
32
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1515 # This script requires cx_Oracle 4.3 and higher.
1616 #------------------------------------------------------------------------------
1717
18 from __future__ import print_function
19
2018 import cx_Oracle
21 import SampleEnv
19 import sample_env
2220
2321 class Test(object):
2422
2725 self.b = b
2826 self.c = c
2927
30 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
28 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
3129 cursor = connection.cursor()
3230
3331 # change this to False if you want to create the table yourself using SQL*Plus
5654 print("Rows:")
5755 for row in cursor:
5856 print("a = %s, b = %s, c = %s" % (row.a, row.b, row.c))
59
+0
-141
samples/SampleEnv.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Sets the environment used by most Python cx_Oracle samples. Production
6 # applications should consider using External Authentication to
7 # avoid hard coded credentials.
8 #
9 # You can set values in environment variables to bypass the sample requesting
10 # the information it requires.
11 #
12 # CX_ORACLE_SAMPLES_MAIN_USER: user used for most samples
13 # CX_ORACLE_SAMPLES_MAIN_PASSWORD: password of user used for most samples
14 # CX_ORACLE_SAMPLES_EDITION_USER: user for editioning
15 # CX_ORACLE_SAMPLES_EDITION_PASSWORD: password of user for editioning
16 # CX_ORACLE_SAMPLES_EDITION_NAME: name of edition for editioning
17 # CX_ORACLE_SAMPLES_CONNECT_STRING: connect string
18 # CX_ORACLE_SAMPLES_SYSDBA_USER: SYSDBA user for setting up samples
19 # CX_ORACLE_SAMPLES_SYSDBA_PASSWORD: SYSDBA password for setting up samples
20 #
21 # CX_ORACLE_SAMPLES_CONNECT_STRING can be set to an Easy Connect string, or a
22 # Net Service Name from a tnsnames.ora file or external naming service,
23 # or it can be the name of a local Oracle database instance.
24 #
25 # If cx_Oracle is using Instant Client, then an Easy Connect string is
26 # generally appropriate. The syntax is:
27 #
28 # [//]host_name[:port][/service_name][:server_type][/instance_name]
29 #
30 # Commonly just the host_name and service_name are needed
31 # e.g. "localhost/orclpdb" or "localhost/XE"
32 #
33 # If using a tnsnames.ora file, the file can be in a default
34 # location such as $ORACLE_HOME/network/admin/tnsnames.ora or
35 # /etc/tnsnames.ora. Alternatively set the TNS_ADMIN environment
36 # variable and put the file in $TNS_ADMIN/tnsnames.ora.
37 #------------------------------------------------------------------------------
38
39 from __future__ import print_function
40
41 import getpass
42 import os
43 import sys
44
45 # default values
46 DEFAULT_MAIN_USER = "pythondemo"
47 DEFAULT_EDITION_USER = "pythoneditions"
48 DEFAULT_EDITION_NAME = "python_e1"
49 DEFAULT_CONNECT_STRING = "localhost/orclpdb"
50
51 # dictionary containing all parameters; these are acquired as needed by the
52 # methods below (which should be used instead of consulting this dictionary
53 # directly) and then stored so that a value is not requested more than once
54 PARAMETERS = {}
55
56 def GetValue(name, label, defaultValue=""):
57 value = PARAMETERS.get(name)
58 if value is not None:
59 return value
60 envName = "CX_ORACLE_SAMPLES_" + name
61 value = os.environ.get(envName)
62 if value is None:
63 if defaultValue:
64 label += " [%s]" % defaultValue
65 label += ": "
66 if defaultValue:
67 value = input(label).strip()
68 else:
69 value = getpass.getpass(label)
70 if not value:
71 value = defaultValue
72 return value
73
74 def GetMainUser():
75 return GetValue("MAIN_USER", "Main User Name", DEFAULT_MAIN_USER)
76
77 def GetMainPassword():
78 return GetValue("MAIN_PASSWORD", "Password for %s" % GetMainUser())
79
80 def GetEditionUser():
81 return GetValue("EDITION_USER", "Edition User Name", DEFAULT_EDITION_USER)
82
83 def GetEditionPassword():
84 return GetValue("EDITION_PASSWORD", "Password for %s" % GetEditionUser())
85
86 def GetEditionName():
87 return GetValue("EDITION_NAME", "Edition Name", DEFAULT_EDITION_NAME)
88
89 def GetConnectString():
90 return GetValue("CONNECT_STRING", "Connect String", DEFAULT_CONNECT_STRING)
91
92 def GetMainConnectString(password=None):
93 if password is None:
94 password = GetMainPassword()
95 return "%s/%s@%s" % (GetMainUser(), password, GetConnectString())
96
97 def GetDrcpConnectString():
98 return GetMainConnectString() + ":pooled"
99
100 def GetEditionConnectString():
101 return "%s/%s@%s" % \
102 (GetEditionUser(), GetEditionPassword(), GetConnectString())
103
104 def GetSysdbaConnectString():
105 sysdbaUser = GetValue("SYSDBA_USER", "SYSDBA user", "sys")
106 sysdbaPassword = GetValue("SYSDBA_PASSWORD",
107 "Password for %s" % sysdbaUser)
108 return "%s/%s@%s" % (sysdbaUser, sysdbaPassword, GetConnectString())
109
110 def RunSqlScript(conn, scriptName, **kwargs):
111 statementParts = []
112 cursor = conn.cursor()
113 replaceValues = [("&" + k + ".", v) for k, v in kwargs.items()] + \
114 [("&" + k, v) for k, v in kwargs.items()]
115 scriptDir = os.path.dirname(os.path.abspath(sys.argv[0]))
116 fileName = os.path.join(scriptDir, "sql", scriptName + "Exec.sql")
117 for line in open(fileName):
118 if line.strip() == "/":
119 statement = "".join(statementParts).strip()
120 if statement:
121 for searchValue, replaceValue in replaceValues:
122 statement = statement.replace(searchValue, replaceValue)
123 cursor.execute(statement)
124 statementParts = []
125 else:
126 statementParts.append(line)
127 cursor.execute("""
128 select name, type, line, position, text
129 from dba_errors
130 where owner = upper(:owner)
131 order by name, type, line, position""",
132 owner = GetMainUser())
133 prevName = prevObjType = None
134 for name, objType, lineNum, position, text in cursor:
135 if name != prevName or objType != prevObjType:
136 print("%s (%s)" % (name, objType))
137 prevName = name
138 prevObjType = objType
139 print(" %s/%s %s" % (lineNum, position, text))
140
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1515 # This script requires cx_Oracle 5.3 and higher.
1616 #------------------------------------------------------------------------------
1717
18 from __future__ import print_function
18 import cx_Oracle
19 import sample_env
1920
20 import cx_Oracle
21 import SampleEnv
22
23 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
21 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2422
2523 # show all of the rows available in the table
2624 cursor = connection.cursor()
6967 print("SKIP BACK 4 ROWS")
7068 print(cursor.fetchone())
7169 print()
72
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
55 # SessionCallback.py
66 #
7 # Demonstrate how to use a session callback written in Python. The callback is
8 # invoked whenever a newly created session is acquired from the pool, or when
9 # the requested tag does not match the tag that is associated with the
10 # session. It is generally used to set session state, so that the application
11 # can count on known session state, which allows the application to reduce the
12 # number of round trips made to the database.
7 # Demonstrate how to use a connection pool session callback written in
8 # Python. The callback is invoked whenever a newly created session is acquired
9 # from the pool, or when the requested tag does not match the tag that is
10 # associated with the session. It is generally used to set session state, so
11 # that the application can count on known session state, which allows the
12 # application to reduce the number of round trips made to the database.
13 # If all your connections should have the same session state, you can simplify
14 # the session callback by removing the tagging logic.
1315 #
14 # This script requires cx_Oracle 7.1 and higher.
16 # This script requires cx_Oracle 7.1 or higher.
17 #
18 # Also see SessionCallbackPLSQL.py
19 #
1520 #------------------------------------------------------------------------------
1621
17 from __future__ import print_function
18
1922 import cx_Oracle
20 import SampleEnv
23 import sample_env
2124
2225 # define a dictionary of NLS_DATE_FORMAT formats supported by this sample
2326 SUPPORTED_FORMATS = {
3841 }
3942
4043 # define session callback
41 def InitSession(conn, requestedTag):
44 def init_session(conn, requested_tag):
4245
4346 # display the requested and actual tags
44 print("InitSession(): requested tag=%r, actual tag=%r" % \
45 (requestedTag, conn.tag))
47 print("init_session(): requested tag=%r, actual tag=%r" % \
48 (requested_tag, conn.tag))
4649
4750 # tags are expected to be in the form "key1=value1;key2=value2"
4851 # in this example, they are used to set NLS parameters and the tag is
4952 # parsed to validate it
50 if requestedTag is not None:
53 if requested_tag is not None:
5154 stateParts = []
52 for directive in requestedTag.split(";"):
55 for directive in requested_tag.split(";"):
5356 parts = directive.split("=")
5457 if len(parts) != 2:
5558 raise ValueError("Tag must contain key=value pairs")
7073 # assign the requested tag to the connection so that when the connection
7174 # is closed, it will automatically be retagged; note that if the requested
7275 # tag is None (no tag was requested) this has no effect
73 conn.tag = requestedTag
76 conn.tag = requested_tag
7477
7578
7679 # create pool with session callback defined
77 pool = cx_Oracle.SessionPool(SampleEnv.GetMainUser(),
78 SampleEnv.GetMainPassword(), SampleEnv.GetConnectString(), min=2,
79 max=5, increment=1, threaded=True, sessionCallback=InitSession)
80 pool = cx_Oracle.SessionPool(user=sample_env.get_main_user(),
81 password=sample_env.get_main_password(),
82 dsn=sample_env.get_connect_string(), min=2, max=5,
83 increment=1, threaded=True,
84 sessionCallback=init_session)
8085
8186 # acquire session without specifying a tag; since the session returned is
8287 # newly created, the callback will be invoked but since there is no tag
8388 # specified, no session state will be changed
8489 print("(1) acquire session without tag")
85 conn = pool.acquire()
86 cursor = conn.cursor()
87 cursor.execute("select to_char(current_date) from dual")
88 result, = cursor.fetchone()
89 print("main(): result is", repr(result))
90 conn.close()
90 with pool.acquire() as conn:
91 cursor = conn.cursor()
92 cursor.execute("select to_char(current_date) from dual")
93 result, = cursor.fetchone()
94 print("main(): result is", repr(result))
9195
9296 # acquire session, specifying a tag; since the session returned has no tag,
9397 # the callback will be invoked; session state will be changed and the tag will
9498 # be saved when the connection is closed
9599 print("(2) acquire session with tag")
96 conn = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE")
97 cursor = conn.cursor()
98 cursor.execute("select to_char(current_date) from dual")
99 result, = cursor.fetchone()
100 print("main(): result is", repr(result))
101 conn.close()
100 with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn:
101 cursor = conn.cursor()
102 cursor.execute("select to_char(current_date) from dual")
103 result, = cursor.fetchone()
104 print("main(): result is", repr(result))
102105
103106 # acquire session, specifying the same tag; since a session exists in the pool
104107 # with this tag, it will be returned and the callback will not be invoked but
105108 # the connection will still have the session state defined previously
106109 print("(3) acquire session with same tag")
107 conn = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE")
108 cursor = conn.cursor()
109 cursor.execute("select to_char(current_date) from dual")
110 result, = cursor.fetchone()
111 print("main(): result is", repr(result))
112 conn.close()
110 with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn:
111 cursor = conn.cursor()
112 cursor.execute("select to_char(current_date) from dual")
113 result, = cursor.fetchone()
114 print("main(): result is", repr(result))
113115
114116 # acquire session, specifying a different tag; since no session exists in the
115117 # pool with this tag, a new session will be returned and the callback will be
116118 # invoked; session state will be changed and the tag will be saved when the
117119 # connection is closed
118120 print("(4) acquire session with different tag")
119 conn = pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC")
120 cursor = conn.cursor()
121 cursor.execute("select to_char(current_date) from dual")
122 result, = cursor.fetchone()
123 print("main(): result is", repr(result))
124 conn.close()
121 with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC") as conn:
122 cursor = conn.cursor()
123 cursor.execute("select to_char(current_date) from dual")
124 result, = cursor.fetchone()
125 print("main(): result is", repr(result))
125126
126127 # acquire session, specifying a different tag but also specifying that a
127128 # session with any tag can be acquired from the pool; a session with one of the
129130 # session state will be changed and the tag will be saved when the connection
130131 # is closed
131132 print("(4) acquire session with different tag but match any also specified")
132 conn = pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True)
133 cursor = conn.cursor()
134 cursor.execute("select to_char(current_date) from dual")
135 result, = cursor.fetchone()
136 print("main(): result is", repr(result))
137 conn.close()
138
133 with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True) \
134 as conn:
135 cursor = conn.cursor()
136 cursor.execute("select to_char(current_date) from dual")
137 result, = cursor.fetchone()
138 print("main(): result is", repr(result))
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
55 # SessionCallbackPLSQL.py
66 #
7 # Demonstrate how to use a session callback written in PL/SQL. The callback is
8 # invoked whenever the tag requested by the application does not match the tag
9 # associated with the session in the pool. It should be used to set session
10 # state, so that the application can count on known session state, which allows
11 # the application to reduce the number of round trips to the database.
7 # Demonstrate how to use a connection pool session callback written in
8 # PL/SQL. The callback is invoked whenever the tag requested by the application
9 # does not match the tag associated with the session in the pool. It should be
10 # used to set session state, so that the application can count on known session
11 # state, which allows the application to reduce the number of round trips to the
12 # database.
1213 #
1314 # The primary advantage to this approach over the equivalent approach shown in
1415 # SessionCallback.py is when DRCP is used, as the callback is invoked on the
1516 # server and no round trip is required to set state.
1617 #
17 # This script requires cx_Oracle 7.1 and higher.
18 # This script requires cx_Oracle 7.1 or higher.
19 #
20 # Also see SessionCallback.py
21 #
1822 #------------------------------------------------------------------------------
1923
20 from __future__ import print_function
21
2224 import cx_Oracle
23 import SampleEnv
25 import sample_env
2426
2527 # create pool with session callback defined
26 pool = cx_Oracle.SessionPool(SampleEnv.GetMainUser(),
27 SampleEnv.GetMainPassword(), SampleEnv.GetConnectString(), min=2,
28 max=5, increment=1, threaded=True,
29 sessionCallback="pkg_SessionCallback.TheCallback")
28 pool = cx_Oracle.SessionPool(user=sample_env.get_main_user(),
29 password=sample_env.get_main_password(),
30 dsn=sample_env.get_connect_string(), min=2, max=5,
31 increment=1, threaded=True,
32 sessionCallback="pkg_SessionCallback.TheCallback")
3033
3134 # truncate table logging calls to PL/SQL session callback
32 conn = pool.acquire()
33 cursor = conn.cursor()
34 cursor.execute("truncate table PLSQLSessionCallbacks")
35 conn.close()
35 with pool.acquire() as conn:
36 cursor = conn.cursor()
37 cursor.execute("truncate table PLSQLSessionCallbacks")
3638
3739 # acquire session without specifying a tag; the callback will not be invoked as
3840 # a result and no session state will be changed
3941 print("(1) acquire session without tag")
40 conn = pool.acquire()
41 cursor = conn.cursor()
42 cursor.execute("select to_char(current_date) from dual")
43 result, = cursor.fetchone()
44 print("main(): result is", repr(result))
45 conn.close()
42 with pool.acquire() as conn:
43 cursor = conn.cursor()
44 cursor.execute("select to_char(current_date) from dual")
45 result, = cursor.fetchone()
46 print("main(): result is", repr(result))
4647
4748 # acquire session, specifying a tag; since the session returned has no tag,
4849 # the callback will be invoked; session state will be changed and the tag will
4950 # be saved when the connection is closed
5051 print("(2) acquire session with tag")
51 conn = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE")
52 cursor = conn.cursor()
53 cursor.execute("select to_char(current_date) from dual")
54 result, = cursor.fetchone()
55 print("main(): result is", repr(result))
56 conn.close()
52 with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn:
53 cursor = conn.cursor()
54 cursor.execute("select to_char(current_date) from dual")
55 result, = cursor.fetchone()
56 print("main(): result is", repr(result))
5757
5858 # acquire session, specifying the same tag; since a session exists in the pool
5959 # with this tag, it will be returned and the callback will not be invoked but
6060 # the connection will still have the session state defined previously
6161 print("(3) acquire session with same tag")
62 conn = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE")
63 cursor = conn.cursor()
64 cursor.execute("select to_char(current_date) from dual")
65 result, = cursor.fetchone()
66 print("main(): result is", repr(result))
67 conn.close()
62 with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn:
63 cursor = conn.cursor()
64 cursor.execute("select to_char(current_date) from dual")
65 result, = cursor.fetchone()
66 print("main(): result is", repr(result))
6867
6968 # acquire session, specifying a different tag; since no session exists in the
7069 # pool with this tag, a new session will be returned and the callback will be
7170 # invoked; session state will be changed and the tag will be saved when the
7271 # connection is closed
7372 print("(4) acquire session with different tag")
74 conn = pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC")
75 cursor = conn.cursor()
76 cursor.execute("select to_char(current_date) from dual")
77 result, = cursor.fetchone()
78 print("main(): result is", repr(result))
79 conn.close()
73 with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC") as conn:
74 cursor = conn.cursor()
75 cursor.execute("select to_char(current_date) from dual")
76 result, = cursor.fetchone()
77 print("main(): result is", repr(result))
8078
8179 # acquire session, specifying a different tag but also specifying that a
8280 # session with any tag can be acquired from the pool; a session with one of the
8482 # session state will be changed and the tag will be saved when the connection
8583 # is closed
8684 print("(4) acquire session with different tag but match any also specified")
87 conn = pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True)
88 cursor = conn.cursor()
89 cursor.execute("select to_char(current_date) from dual")
90 result, = cursor.fetchone()
91 print("main(): result is", repr(result))
92 conn.close()
85 with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True) \
86 as conn:
87 cursor = conn.cursor()
88 cursor.execute("select to_char(current_date) from dual")
89 result, = cursor.fetchone()
90 print("main(): result is", repr(result))
9391
9492 # acquire session and display results from PL/SQL session logs
95 conn = pool.acquire()
96 cursor = conn.cursor()
97 cursor.execute("""
98 select RequestedTag, ActualTag
99 from PLSQLSessionCallbacks
100 order by FixupTimestamp""")
101 print("(5) PL/SQL session callbacks")
102 for requestedTag, actualTag in cursor:
103 print("Requested:", requestedTag, "Actual:", actualTag)
104
93 with pool.acquire() as conn:
94 cursor = conn.cursor()
95 cursor.execute("""
96 select RequestedTag, ActualTag
97 from PLSQLSessionCallbacks
98 order by FixupTimestamp""")
99 print("(5) PL/SQL session callbacks")
100 for requestedTag, actualTag in cursor:
101 print("Requested:", requestedTag, "Actual:", actualTag)
99 # demonstration of PL/SQL editioning.
1010 #------------------------------------------------------------------------------
1111
12 from __future__ import print_function
13
1412 import cx_Oracle
1513
16 import SampleEnv
14 import sample_env
1715 import DropSamples
1816
19 # connect as SYSDBA
20 conn = cx_Oracle.connect(SampleEnv.GetSysdbaConnectString(),
21 mode = cx_Oracle.SYSDBA)
17 # connect as administrative user (usually SYSTEM or ADMIN)
18 conn = cx_Oracle.connect(sample_env.get_admin_connect_string())
2219
2320 # drop existing users and editions, if applicable
24 DropSamples.DropSamples(conn)
21 DropSamples.drop_samples(conn)
2522
2623 # create sample schema and edition
2724 print("Creating sample schemas and edition...")
28 SampleEnv.RunSqlScript(conn, "SetupSamples",
29 main_user = SampleEnv.GetMainUser(),
30 main_password = SampleEnv.GetMainPassword(),
31 edition_user = SampleEnv.GetEditionUser(),
32 edition_password = SampleEnv.GetEditionPassword(),
33 edition_name = SampleEnv.GetEditionName())
25 sample_env.run_sql_script(conn, "SetupSamples",
26 main_user=sample_env.get_main_user(),
27 main_password=sample_env.get_main_password(),
28 edition_user=sample_env.get_edition_user(),
29 edition_password=sample_env.get_edition_password(),
30 edition_name=sample_env.get_edition_name())
3431 print("Done.")
3532
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # ShardingNumberKey.py
6 # This script demonstrates how to use sharding keys with a sharded database.
7 # The sample schema provided does not include support for running this demo. A
8 # sharded database must first be created. Information on how to create a
9 # sharded database can be found in the documentation:
10 # https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=SHARD
11 #
12 # This script requires cx_Oracle 6.1 and higher but it is recommended to use
13 # cx_Oracle 7.3 and higher in order to avoid a set of known issues when using
14 # sharding capabilities.
15 #------------------------------------------------------------------------------
16
17 import cx_Oracle
18 import sample_env
19
20 pool = cx_Oracle.SessionPool(user=sample_env.get_main_user(),
21 password=sample_env.get_main_password(),
22 dsn=sample_env.get_connect_string(), min=1, max=5,
23 increment=1)
24
25 def connect_and_display(sharding_key):
26 print("Connecting with sharding key:", sharding_key)
27 with pool.acquire(shardingkey=[sharding_key]) as conn:
28 cursor = conn.cursor()
29 cursor.execute("select sys_context('userenv', 'db_name') from dual")
30 name, = cursor.fetchone()
31 print("--> connected to database", name)
32
33 connect_and_display(100)
34 connect_and_display(167)
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
1111 # The user must have been granted the SODA_APP privilege.
1212 #------------------------------------------------------------------------------
1313
14 from __future__ import print_function
14 import cx_Oracle
15 import sample_env
1516
16 import cx_Oracle
17 import SampleEnv
18
19 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
17 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
2018
2119 # The general recommendation for simple SODA usage is to enable autocommit
2220 connection.autocommit = True
2826 # This will open an existing collection, if the name is already in use.
2927 collection = soda.createCollection("mycollection")
3028
31 indexSpec = { 'name': 'CITY_IDX',
32 'fields': [ {
33 'path': 'address.city',
34 'datatype': 'string',
35 'order': 'asc' } ] }
36 collection.createIndex(indexSpec)
29 index_spec = {
30 'name': 'CITY_IDX',
31 'fields': [
32 {
33 'path': 'address.city',
34 'datatype': 'string',
35 'order': 'asc'
36 }
37 ]
38 }
39 collection.createIndex(index_spec)
3740
3841 # Insert a document.
3942 # A system generated key is created by default.
8689 # Drop the collection
8790 if collection.drop():
8891 print('Collection was dropped')
89
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # SodaBulkInsert.py
6 # Demonstrates the use of SODA bulk insert.
7 #
8 # This script requires cx_Oracle 7.2 and higher.
9 # Oracle Client must be at 18.5 or higher.
10 # Oracle Database must be at 18.1 or higher.
11 # The user must have been granted the SODA_APP privilege.
12 #------------------------------------------------------------------------------
13
14 import cx_Oracle
15 import sample_env
16
17 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
18
19 # the general recommendation for simple SODA usage is to enable autocommit
20 connection.autocommit = True
21
22 # create the parent object for all SODA work
23 soda = connection.getSodaDatabase()
24
25 # create a new (or open an existing) SODA collection
26 collection = soda.createCollection("SodaBulkInsert")
27
28 # remove all documents from the collection
29 collection.find().remove()
30
31 # define some documents that will be stored
32 in_docs = [
33 dict(name="Sam", age=8),
34 dict(name="George", age=46),
35 dict(name="Bill", age=35),
36 dict(name="Sally", age=43),
37 dict(name="Jill", age=28),
38 dict(name="Cynthia", age=12)
39 ]
40
41 # perform bulk insert
42 result_docs = collection.insertManyAndGet(in_docs)
43 for doc in result_docs:
44 print("Inserted SODA document with key", doc.key)
45 print()
46
47 # perform search of all persons under the age of 40
48 print("Persons under the age of 40:")
49 for doc in collection.find().filter({'age': {'$lt': 40}}).getDocuments():
50 print(doc.getContent()["name"] + ",", "key", doc.key)
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
2121 # dependencies (see http://geopandas.org/install.html).
2222 #------------------------------------------------------------------------------
2323
24 from __future__ import print_function
25
26 import SampleEnv
24 import sample_env
2725 import cx_Oracle
2826 from shapely.wkb import loads
2927 import geopandas as gpd
3028
3129 # create Oracle connection and cursor objects
32 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
30 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
3331 cursor = connection.cursor()
3432
3533 # enable autocommit to avoid the additional round trip to the database to
3937
4038 # define output type handler to fetch LOBs, avoiding the second round trip to
4139 # the database to read the LOB contents
42 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
43 if defaultType == cx_Oracle.BLOB:
44 return cursor.var(cx_Oracle.LONG_BINARY, arraysize = cursor.arraysize)
45 connection.outputtypehandler = OutputTypeHandler
40 def output_type_handler(cursor, name, default_type, size, precision, scale):
41 if default_type == cx_Oracle.BLOB:
42 return cursor.var(cx_Oracle.LONG_BINARY, arraysize=cursor.arraysize)
43 connection.outputtypehandler = output_type_handler
4644
4745 # drop and create table
4846 print("Dropping and creating table...")
6159 )""")
6260
6361 # acquire types used for creating SDO_GEOMETRY objects
64 typeObj = connection.gettype("MDSYS.SDO_GEOMETRY")
65 elementInfoTypeObj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
66 ordinateTypeObj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY")
62 type_obj = connection.gettype("MDSYS.SDO_GEOMETRY")
63 element_info_type_obj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
64 ordinate_type_obj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY")
6765
6866 # define function for creating an SDO_GEOMETRY object
69 def CreateGeometryObj(*ordinates):
70 geometry = typeObj.newobject()
67 def create_geometry_obj(*ordinates):
68 geometry = type_obj.newobject()
7169 geometry.SDO_GTYPE = 2003
7270 geometry.SDO_SRID = 8307
73 geometry.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
71 geometry.SDO_ELEM_INFO = element_info_type_obj.newobject()
7472 geometry.SDO_ELEM_INFO.extend([1, 1003, 1])
75 geometry.SDO_ORDINATES = ordinateTypeObj.newobject()
73 geometry.SDO_ORDINATES = ordinate_type_obj.newobject()
7674 geometry.SDO_ORDINATES.extend(ordinates)
7775 return geometry
7876
7977 # create SDO_GEOMETRY objects for three adjacent states in the USA
80 geometryNevada = CreateGeometryObj(-114.052025, 37.103989, -114.049797,
78 geometry_nevada = create_geometry_obj(-114.052025, 37.103989, -114.049797,
8179 37.000423, -113.484375, 37, -112.898598, 37.000401,-112.539604,
8280 37.000683, -112, 37.000977, -111.412048, 37.001514, -111.133018,
8381 37.00079,-110.75, 37.003201, -110.5, 37.004265, -110.469505, 36.998001,
9795 -114.049339, 38.572968, -114.049095, 38.14864, -114.0476,
9896 37.80946,-114.05098, 37.746284, -114.051666, 37.604805, -114.052025,
9997 37.103989)
100 geometryWyoming = CreateGeometryObj(-111.045815, 41.251774, -111.045982,
98 geometry_wyoming = create_geometry_obj(-111.045815, 41.251774, -111.045982,
10199 40.998013, -110.5, 40.994801, -110.047768, 40.997696, -110, 40.997398,
102100 -109.534233, 40.998184, -109.2313, 41.002102, -109.0494, 41.000702,
103101 -108.525368, 40.999634, -107.917793, 41.002071, -107.317177, 41.002956,
121119 -111.043846, 43.3158, -111.043381, 43.02013, -111.042786, 42.719578,
122120 -111.045967, 42.513187, -111.045944, 42.001633, -111.045097, 41.579899,
123121 -111.045815, 41.251774)
124 geometryColorado = CreateGeometryObj(-109.045143, 37.375, -109.044571,
122 geometry_colorado = create_geometry_obj(-109.045143, 37.375, -109.044571,
125123 36.999088, -108.378571, 36.999516, -107.481133, 37, -107.420311, 37,
126124 -106.876701, 37.00013, -106.869209, 36.992416, -106.475639, 36.993748,
127125 -106.006058, 36.995327, -105.717834, 36.995823, -105.220055, 36.995144,
152150 # will skip the metadata and indexes.
153151 print("Adding rows to table...")
154152 data = [
155 ('Nevada', geometryNevada),
156 ('Colorado', geometryColorado),
157 ('Wyoming', geometryWyoming)
153 ('Nevada', geometry_nevada),
154 ('Colorado', geometry_colorado),
155 ('Wyoming', geometry_wyoming)
158156 ]
159157 cursor.executemany('insert into TestStates values (:state, :obj)', data)
160158
169167 cursor.execute("""
170168 SELECT state, sdo_util.to_wkbgeometry(geometry)
171169 FROM TestStates""")
172 gdf = gpd.GeoDataFrame(cursor.fetchall(), columns = ['state', 'wkbgeometry'])
170 gdf = gpd.GeoDataFrame(cursor.fetchall(), columns=['state', 'wkbgeometry'])
173171
174172 # create GeoSeries to replace the WKB geometry column
175173 gdf['geometry'] = gpd.GeoSeries(gdf['wkbgeometry'].apply(lambda x: loads(x)))
184182 print()
185183 print("GeoPandas combining the 3 geometries into a single geometry...")
186184 print(gdf.unary_union)
187
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
99 # for paticular applications.
1010 #------------------------------------------------------------------------------
1111
12 from __future__ import print_function
13
1412 import cx_Oracle
15 import SampleEnv
13 import sample_env
1614
1715 # sample subclassed connection which overrides the constructor (so no
1816 # parameters are required) and the cursor() method (so that the subclassed
2018 class Connection(cx_Oracle.Connection):
2119
2220 def __init__(self):
23 connectString = SampleEnv.GetMainConnectString()
21 connect_string = sample_env.get_main_connect_string()
2422 print("CONNECT to database")
25 return super(Connection, self).__init__(connectString)
23 return super(Connection, self).__init__(connect_string)
2624
2725 def cursor(self):
2826 return Cursor(self)
3533 def execute(self, statement, args):
3634 print("EXECUTE", statement)
3735 print("ARGS:")
38 for argIndex, arg in enumerate(args):
39 print(" ", argIndex + 1, "=>", repr(arg))
36 for arg_index, arg in enumerate(args):
37 print(" ", arg_index + 1, "=>", repr(arg))
4038 return super(Cursor, self).execute(statement, args)
4139
4240 def fetchone(self):
5250 cursor.execute("select count(*) from ChildTable where ParentId = :1", (30,))
5351 count, = cursor.fetchone()
5452 print("COUNT:", int(count))
55
+0
-67
samples/Threads.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Threads.py
6 # This script demonstrates the use of threads with cx_Oracle. A session pool
7 # is used so that multiple connections are available to perform work on the
8 # database. Only one operation (such as an execute or fetch) can take place at
9 # a time on a connection. In the below example, one of the threads performs
10 # dbms_lock.sleep while the other performs a query.
11 #
12 # This script requires cx_Oracle 2.5 and higher.
13 #------------------------------------------------------------------------------
14
15 from __future__ import print_function
16
17 import cx_Oracle
18 import SampleEnv
19 import threading
20
21 pool = cx_Oracle.SessionPool(SampleEnv.GetMainUser(),
22 SampleEnv.GetMainPassword(), SampleEnv.GetConnectString(), min=2,
23 max=5, increment=1, threaded=True)
24
25 def TheLongQuery():
26 conn = pool.acquire()
27 cursor = conn.cursor()
28 cursor.arraysize = 25000
29 print("TheLongQuery(): beginning execute...")
30 cursor.execute("""
31 select *
32 from
33 TestNumbers
34 cross join TestNumbers
35 cross join TestNumbers
36 cross join TestNumbers
37 cross join TestNumbers
38 cross join TestNumbers""")
39 print("TheLongQuery(): done execute...")
40 while True:
41 rows = cursor.fetchmany()
42 if not rows:
43 break
44 print("TheLongQuery(): fetched", len(rows), "rows...")
45 print("TheLongQuery(): all done!")
46
47
48 def DoALock():
49 conn = pool.acquire()
50 cursor = conn.cursor()
51 print("DoALock(): beginning execute...")
52 cursor.callproc("dbms_lock.sleep", (5,))
53 print("DoALock(): done execute...")
54
55
56 thread1 = threading.Thread(None, TheLongQuery)
57 thread1.start()
58
59 thread2 = threading.Thread(None, DoALock)
60 thread2.start()
61
62 thread1.join()
63 thread2.join()
64
65 print("All done!")
66
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
3232 # This script requires cx_Oracle 5.3 and higher.
3333 #------------------------------------------------------------------------------
3434
35 from __future__ import print_function
36
3735 import cx_Oracle
38 import SampleEnv
36 import sample_env
3937 import sys
4038
4139 # constants
4240 CONNECT_STRING = "localhost/orcl-tg"
4341
44 # for Python 2.7 we need raw_input
45 try:
46 input = raw_input
47 except NameError:
48 pass
49
5042 # create transaction and generate a recoverable error
51 pool = cx_Oracle.SessionPool(SampleEnv.GetMainUser(),
52 SampleEnv.GetMainPassword(), CONNECT_STRING, min=1,
53 max=9, increment=2)
43 pool = cx_Oracle.SessionPool(user=sample_env.get_main_user(),
44 password=sample_env.get_main_password(),
45 dsn=CONNECT_STRING, min=1, max=9, increment=2)
5446 connection = pool.acquire()
5547 cursor = connection.cursor()
5648 cursor.execute("""
6052 insert into TestTempTable
6153 values (1, null)""")
6254 input("Please kill %s session now. Press ENTER when complete." % \
63 SampleEnv.GetMainUser())
55 sample_env.get_main_user())
6456 try:
6557 connection.commit() # this should fail
6658 sys.exit("Session was not killed. Terminating.")
7668 # check if previous transaction completed
7769 connection = pool.acquire()
7870 cursor = connection.cursor()
71 args = (cx_Oracle.Binary(ltxid), cursor.var(bool), cursor.var(bool))
7972 _, committed, completed = cursor.callproc("dbms_app_cont.get_ltxid_outcome",
80 (cx_Oracle.Binary(ltxid), cursor.var(bool), cursor.var(bool)))
73 args)
8174 print("Failed transaction was committed:", committed)
8275 print("Failed call was completed:", completed)
83
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1616 # This script requires cx_Oracle 5.0 and higher.
1717 #------------------------------------------------------------------------------
1818
19
20 from __future__ import print_function
21
2219 import cx_Oracle
2320 import datetime
24 import SampleEnv
21 import sample_env
2522
26 con = cx_Oracle.connect(SampleEnv.GetMainConnectString())
27 objType = con.gettype("UDT_BUILDING")
23 con = cx_Oracle.connect(sample_env.get_main_connect_string())
24 obj_type = con.gettype("UDT_BUILDING")
2825
2926 class Building(object):
3027
31 def __init__(self, buildingId, description, numFloors, dateBuilt):
32 self.buildingId = buildingId
28 def __init__(self, building_id, description, num_floors, dateBuilt):
29 self.building_id = building_id
3330 self.description = description
34 self.numFloors = numFloors
31 self.num_floors = num_floors
3532 self.dateBuilt = dateBuilt
3633
3734 def __repr__(self):
38 return "<Building %s: %s>" % (self.buildingId, self.description)
35 return "<Building %s: %s>" % (self.building_id, self.description)
3936
4037
41 def BuildingInConverter(value):
42 obj = objType.newobject()
43 obj.BUILDINGID = value.buildingId
38 def building_in_converter(value):
39 obj = obj_type.newobject()
40 obj.BUILDINGID = value.building_id
4441 obj.DESCRIPTION = value.description
45 obj.NUMFLOORS = value.numFloors
42 obj.NUMFLOORS = value.num_floors
4643 obj.DATEBUILT = value.dateBuilt
4744 return obj
4845
4946
50 def BuildingOutConverter(obj):
47 def building_out_converter(obj):
5148 return Building(int(obj.BUILDINGID), obj.DESCRIPTION, int(obj.NUMFLOORS),
5249 obj.DATEBUILT)
5350
5451
55 def InputTypeHandler(cursor, value, numElements):
52 def input_type_handler(cursor, value, numElements):
5653 if isinstance(value, Building):
5754 return cursor.var(cx_Oracle.OBJECT, arraysize = numElements,
58 inconverter = BuildingInConverter, typename = objType.name)
55 inconverter = building_in_converter, typename = obj_type.name)
5956
60 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
61 if defaultType == cx_Oracle.OBJECT:
62 return cursor.var(cx_Oracle.OBJECT, arraysize = cursor.arraysize,
63 outconverter = BuildingOutConverter, typename = objType.name)
57 def output_type_handler(cursor, name, default_type, size, precision, scale):
58 if default_type == cx_Oracle.OBJECT:
59 return cursor.var(cx_Oracle.OBJECT, arraysize=cursor.arraysize,
60 outconverter=building_out_converter,
61 typename=obj_type.name)
6462
6563 buildings = [
6664 Building(1, "The First Building", 5, datetime.date(2007, 5, 18)),
6967 ]
7068
7169 cur = con.cursor()
72 cur.inputtypehandler = InputTypeHandler
70 cur.inputtypehandler = input_type_handler
7371 for building in buildings:
7472 try:
7573 cur.execute("insert into TestBuildings values (:1, :2)",
76 (building.buildingId, building))
74 (building.building_id, building))
7775 except cx_Oracle.DatabaseError as e:
7876 error, = e.args
7977 print("CONTEXT:", error.context)
8684 print()
8785
8886 cur = con.cursor()
89 cur.outputtypehandler = OutputTypeHandler
87 cur.outputtypehandler = output_type_handler
9088 print("WITH OUTPUT TYPE HANDLER:")
9189 for row in cur.execute("select * from TestBuildings order by BuildingId"):
9290 print(row)
9391 print()
94
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
22 #
33 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 #
1414 # This script requires cx_Oracle 6.0 and higher.
1515 #------------------------------------------------------------------------------
1616
17 from __future__ import print_function
18
1917 import cx_Oracle
2018 import datetime
21 import SampleEnv
19 import sample_env
2220
2321 DATA = [
2422 (1, "String #1", datetime.datetime(2017, 4, 4)),
2725 ]
2826
2927 # truncate table so sample can be rerun
30 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
28 connection = cx_Oracle.connect(sample_env.get_main_connect_string())
3129 cursor = connection.cursor()
3230 print("Truncating table...")
3331 cursor.execute("truncate table TestUniversalRowids")
5149 from TestUniversalRowids
5250 where rowid = :rid""",
5351 rid = rowid)
54 intCol, stringCol, dateCol = cursor.fetchone()
55 print("IntCol:", intCol)
56 print("StringCol:", stringCol)
52 int_col, string_col, dateCol = cursor.fetchone()
53 print("IntCol:", int_col)
54 print("StringCol:", string_col)
5755 print("DateCol:", dateCol)
58
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Sets the environment used by most Python cx_Oracle samples. Production
6 # applications should consider using External Authentication to
7 # avoid hard coded credentials.
8 #
9 # You can set values in environment variables to bypass the sample requesting
10 # the information it requires.
11 #
12 # CX_ORACLE_SAMPLES_MAIN_USER: user used for most samples
13 # CX_ORACLE_SAMPLES_MAIN_PASSWORD: password of user used for most samples
14 # CX_ORACLE_SAMPLES_EDITION_USER: user for editioning
15 # CX_ORACLE_SAMPLES_EDITION_PASSWORD: password of user for editioning
16 # CX_ORACLE_SAMPLES_EDITION_NAME: name of edition for editioning
17 # CX_ORACLE_SAMPLES_CONNECT_STRING: connect string
18 # CX_ORACLE_SAMPLES_DRCP_CONNECT_STRING: DRCP connect string
19 # CX_ORACLE_SAMPLES_ADMIN_USER: admin user for setting up samples
20 # CX_ORACLE_SAMPLES_ADMIN_PASSWORD: admin password for setting up samples
21 #
22 # CX_ORACLE_SAMPLES_CONNECT_STRING can be set to an Easy Connect string, or a
23 # Net Service Name from a tnsnames.ora file or external naming service,
24 # or it can be the name of a local Oracle database instance.
25 #
26 # If cx_Oracle is using Instant Client, then an Easy Connect string is
27 # generally appropriate. The syntax is:
28 #
29 # [//]host_name[:port][/service_name][:server_type][/instance_name]
30 #
31 # Commonly just the host_name and service_name are needed
32 # e.g. "localhost/orclpdb1" or "localhost/XEPDB1"
33 #
34 # If using a tnsnames.ora file, the file can be in a default
35 # location such as $ORACLE_HOME/network/admin/tnsnames.ora or
36 # /etc/tnsnames.ora. Alternatively set the TNS_ADMIN environment
37 # variable and put the file in $TNS_ADMIN/tnsnames.ora.
38 #
39 # The administrative user for cloud databases is ADMIN and the administrative
40 # user for on premises databases is SYSTEM.
41 #------------------------------------------------------------------------------
42
43 import getpass
44 import os
45 import sys
46
47 # default values
48 DEFAULT_MAIN_USER = "pythondemo"
49 DEFAULT_EDITION_USER = "pythoneditions"
50 DEFAULT_EDITION_NAME = "python_e1"
51 DEFAULT_CONNECT_STRING = "localhost/orclpdb1"
52 DEFAULT_DRCP_CONNECT_STRING = "localhost/orclpdb1:pooled"
53
54 # dictionary containing all parameters; these are acquired as needed by the
55 # methods below (which should be used instead of consulting this dictionary
56 # directly) and then stored so that a value is not requested more than once
57 PARAMETERS = {}
58
59 def get_value(name, label, default_value=""):
60 value = PARAMETERS.get(name)
61 if value is not None:
62 return value
63 env_name = "CX_ORACLE_SAMPLES_" + name
64 value = os.environ.get(env_name)
65 if value is None:
66 if default_value:
67 label += " [%s]" % default_value
68 label += ": "
69 if default_value:
70 value = input(label).strip()
71 else:
72 value = getpass.getpass(label)
73 if not value:
74 value = default_value
75 PARAMETERS[name] = value
76 return value
77
78 def get_main_user():
79 return get_value("MAIN_USER", "Main User Name", DEFAULT_MAIN_USER)
80
81 def get_main_password():
82 return get_value("MAIN_PASSWORD", "Password for %s" % get_main_user())
83
84 def get_edition_user():
85 return get_value("EDITION_USER", "Edition User Name", DEFAULT_EDITION_USER)
86
87 def get_edition_password():
88 return get_value("EDITION_PASSWORD",
89 "Password for %s" % get_edition_user())
90
91 def get_edition_name():
92 return get_value("EDITION_NAME", "Edition Name", DEFAULT_EDITION_NAME)
93
94 def get_connect_string():
95 return get_value("CONNECT_STRING", "Connect String",
96 DEFAULT_CONNECT_STRING)
97
98 def get_main_connect_string(password=None):
99 if password is None:
100 password = get_main_password()
101 return "%s/%s@%s" % (get_main_user(), password, get_connect_string())
102
103 def get_drcp_connect_string():
104 connectString = get_value("DRCP_CONNECT_STRING", "DRCP Connect String",
105 DEFAULT_DRCP_CONNECT_STRING)
106 return "%s/%s@%s" % (get_main_user(), get_main_password(), connectString)
107
108 def get_edition_connect_string():
109 return "%s/%s@%s" % \
110 (get_edition_user(), get_edition_password(), get_connect_string())
111
112 def get_admin_connect_string():
113 admin_user = get_value("ADMIN_USER", "Administrative user", "admin")
114 admin_password = get_value("ADMIN_PASSWORD", "Password for %s" % admin_user)
115 return "%s/%s@%s" % (admin_user, admin_password, get_connect_string())
116
117 def run_sql_script(conn, script_name, **kwargs):
118 statement_parts = []
119 cursor = conn.cursor()
120 replace_values = [("&" + k + ".", v) for k, v in kwargs.items()] + \
121 [("&" + k, v) for k, v in kwargs.items()]
122 script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
123 file_name = os.path.join(script_dir, "sql", script_name + "Exec.sql")
124 for line in open(file_name):
125 if line.strip() == "/":
126 statement = "".join(statement_parts).strip()
127 if statement:
128 for search_value, replace_value in replace_values:
129 statement = statement.replace(search_value, replace_value)
130 try:
131 cursor.execute(statement)
132 except:
133 print("Failed to execute SQL:", statement)
134 raise
135 statement_parts = []
136 else:
137 statement_parts.append(line)
138 cursor.execute("""
139 select name, type, line, position, text
140 from dba_errors
141 where owner = upper(:owner)
142 order by name, type, line, position""",
143 owner = get_main_user())
144 prev_name = prev_obj_type = None
145 for name, objType, lineNum, position, text in cursor:
146 if name != prev_name or objType != prev_obj_type:
147 print("%s (%s)" % (name, objType))
148 prev_name = name
149 prev_obj_type = objType
150 print(" %s/%s %s" % (lineNum, position, text))
00 /*-----------------------------------------------------------------------------
1 * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved.
1 * Copyright 2017, 2020, Oracle and/or its affiliates. All rights reserved.
22 *---------------------------------------------------------------------------*/
33
44 /*-----------------------------------------------------------------------------
1616 /
1717
1818 create user &main_user identified by &main_password
19 quota unlimited on users
20 default tablespace users
2119 /
2220
2321 grant
2624 create procedure,
2725 create type,
2826 select any dictionary,
29 change notification
27 change notification,
28 unlimited tablespace
3029 to &main_user
3130 /
3231
3332 grant execute on dbms_aqadm to &main_user
3433 /
35 grant execute on dbms_lock to &main_user
34
35 begin
36 execute immediate 'begin dbms_session.sleep(0); end;';
37 exception
38 when others then
39 begin
40 execute immediate 'grant execute on dbms_lock to &main_user';
41 exception
42 when others then
43 raise_application_error(-20000,
44 'Ensure the following grant is made: ' ||
45 'grant execute on dbms_lock to ' || user ||
46 ' with grant option');
47 end;
48 end;
3649 /
3750
3851 begin
189202 )
190203 /
191204
192 -- create queue table and queues for demonstrating advanced queuing
193 begin
194 dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE',
205 -- create queue table, queues and subscribers for demonstrating Advanced Queuing
206 begin
207
208 dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE_TAB',
195209 '&main_user..UDT_BOOK');
196 dbms_aqadm.create_queue('&main_user..BOOKS', '&main_user..BOOK_QUEUE');
197 dbms_aqadm.start_queue('&main_user..BOOKS');
210 dbms_aqadm.create_queue('&main_user..DEMO_BOOK_QUEUE',
211 '&main_user..BOOK_QUEUE_TAB');
212 dbms_aqadm.start_queue('&main_user..DEMO_BOOK_QUEUE');
213
214 dbms_aqadm.create_queue_table('&main_user..RAW_QUEUE_TAB', 'RAW');
215 dbms_aqadm.create_queue('&main_user..DEMO_RAW_QUEUE',
216 '&main_user..RAW_QUEUE_TAB');
217 dbms_aqadm.start_queue('&main_user..DEMO_RAW_QUEUE');
218
219 dbms_aqadm.create_queue_table('&main_user..RAW_QUEUE_MULTI_TAB', 'RAW',
220 multiple_consumers => true);
221 dbms_aqadm.create_queue('&main_user..DEMO_RAW_QUEUE_MULTI',
222 '&main_user..RAW_QUEUE_MULTI_TAB');
223 dbms_aqadm.start_queue('&main_user..DEMO_RAW_QUEUE_MULTI');
224
225 dbms_aqadm.add_subscriber('&main_user..DEMO_RAW_QUEUE_MULTI',
226 sys.aq$_agent('SUBSCRIBER_A', null, null));
227 dbms_aqadm.add_subscriber('&main_user..DEMO_RAW_QUEUE_MULTI',
228 sys.aq$_agent('SUBSCRIBER_B', null, null));
229
198230 end;
199231 /
200232
349381 end;
350382 /
351383
384 create procedure &main_user..myrefcursorproc2 (
385 a_RefCursor out sys_refcursor
386 ) as
387 begin
388 open a_RefCursor for
389 select *
390 from TestTempTable;
391 end;
392 /
352393
353394 --
354395 -- Create package for demoing PL/SQL collections and records.
00 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
11 <html xmlns="http://www.w3.org/1999/xhtml">
22 <head>
3 <title>Python and Oracle Database: Scripting for the Future</title>
3 <title>Python and Oracle Database Tutorial: Scripting for the Future</title>
44 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
55
66 <link rel="stylesheet" href="resources/base.css" type="text/css"/>
88 </head>
99 <body bgcolor="#ffffff" text="#000000">
1010
11 <h1>Python and Oracle Database: Scripting for the Future</h1>
12
13 <img src="resources/community-py-200.png" alt="Python cx_Oracle logo">
11 <h1>Python and Oracle Database Tutorial: Scripting for the Future</h1>
12
13 <img src="resources/community-py-200.png" alt="Python cx_Oracle logo" />
1414
1515 <h2>Contents</h2>
1616
1717 <ul>
18 <li><a href="#preface" >Preface</a></li>
18 <li><a href="#overview" >Overview</a></li>
19 <li><a href="#preface" >Setup</a></li>
1920 <li><a href="#connectioninformation" >Connection Information</a></li>
20 <li><a href="#overview" >Overview</a></li>
21 <li><a href="#lab" >Using Python cx_Oracle 7 with Oracle Database</a></li>
21 <li><a href="#lab" >Using Python cx_Oracle with Oracle Database</a>
2222 <ul>
2323 <li><a href="#connecting">1. Connecting to Oracle</a>
2424 <ul>
3232 </li>
3333 <li><a href="#pooling">2. Connection Pooling</a>
3434 <ul>
35 <li>2.1 Session pooling</li>
36 <li>2.2 Session pool experiments</li>
35 <li>2.1 Connection pooling</li>
36 <li>2.2 Connection pool experiments</li>
3737 <li>2.3 Creating a DRCP Connection</li>
38 <li>2.4 Session pooling and DRCP</li>
38 <li>2.4 Connection pooling and DRCP</li>
3939 <li>2.5 More DRCP investigation</li>
4040 </ul>
4141 </li>
4545 <li>3.2 Using fetchone()</li>
4646 <li>3.3 Using fetchmany()</li>
4747 <li>3.4 Scrollable cursors</li>
48 <li>3.5 Tuning with arraysize</li>
48 <li>3.5 Tuning with arraysize and prefetchrows</li>
4949 </ul>
5050 </li>
5151 <li><a href="#binding">4. Binding Data</a>
9191 <li>10.1 Message passing with Oracle Advanced Queuing</li>
9292 </ul>
9393 </li>
94 <li><a href="#soda" >11. Simple Oracle Document Access (SODA)</a>
95 <ul>
96 <li>11.1 Inserting JSON Documents</li>
97 <li>11.2 Searching SODA Documents</li>
98 </ul>
99 </li>
94100 </ul>
101 </li>
95102 <li><a href="#summary" >Summary</a></li>
96103 <li><a href="#primer" >Appendix: Python Primer</a></li>
97104 <li><a href="#resources" >Resources</a></li>
98105 </ul>
99106
100 <h2><a name="preface">Preface</a></h2>
101
102 <p>If you are running this tutorial in your own environment, install the following required software:</p>
103
104 <ol>
105 <li><a target="_blank" href="https://www.python.org/">Python</a> (3.6 preferred but 2.7 should work)</li>
106 <li>cx_Oracle (version 7 preferred but 6.3 or later should work) and Oracle Instant Client Package - Basic (version 18.3 preferred but 12.2 should work)
107 <ul>
108 <li><a target="_blank" href="http://cx-oracle.readthedocs.io/en/latest/installation.html#installing-cx-oracle-on-linux">Linux</a></li>
109 <li><a target="_blank" href="http://cx-oracle.readthedocs.io/en/latest/installation.html#installing-cx-oracle-on-macos">macOS</a> - please note the special instructions for macOS in the link.</li>
110 <li><a target="_blank" href="http://cx-oracle.readthedocs.io/en/latest/installation.html#installing-cx-oracle-on-windows">Windows</a></li>
111 </ul>
112 </li>
113 <li>Oracle <a target="_blank" href="http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html">Instant Client Package - SQL*Plus</a>.</li>
114 </ol>
115
116 <p>To create the schema run:</p>
117
118 <pre>
119 sqlplus sys/yoursyspassword@localhost/orclpdb as sysdba @sql/SetupSamples
120 </pre>
121
122 <h2><a name="connectioninformation">Connection Information</a></h2>
123
124 <p>The database connection information is set in two files:
125 <ul>
126 <li>db_config.py which is imported by the other Python modules.</li>
127 <li>db_config.sql which is used by the other SQL scripts.</li>
128 </ul>
129 </p>
130
131 <p>The username is "pythonhol" with
132 the password "welcome". The connect string is "localhost/orclpdb".
133 See <code>sql/SampleEnv.sql</code>.</p>
134
135 <p>It is easist to have a local pluggable database with the service
136 'orclpdb' configured. If your database is not local, or has a
137 different service, you will need to modify the connection information in db_config.py and db_config.sql.</p>
138
139 <p>The following sections may need adjusting, depending on how you
140 have set up your environment.</p>
141
142107 <h2><a name="overview">Overview</a></h2>
143108
144109 <p>This tutorial is an introduction to using Python with Oracle
151116 <code>tutorial/solutions</code> directory has scripts with the
152117 suggested code changes.</p>
153118
154 <p>Use the Desktop icons to start editors and terminal windows.</p>
155
156119 <p>If you are new to Python review the <a href="#primer">Appendix:
157120 Python Primer</a> to gain an understanding of the language. </p>
158121
159 <h2><a name="lab">Using Python cx_Oracle 7 with Oracle Database</a></h2>
122 <p>When you have finished this tutorial, we recommend reviewing the <a
123 href="http://cx-oracle.readthedocs.org/en/latest/index.html" >cx_Oracle
124 documention</a>. </p>
125
126 <h2><a name="preface">Setup</a></h2>
127
128 <p>These cx_Oracle tutorial instructions can be found <a href="https://oracle.github.io/python-cx_Oracle/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html" >here</a>. The files used in them can be found in the <a href="https://github.com/oracle/python-cx_Oracle/tree/master/samples/tutorial" >cx_Oracle GitHub repository</a>.</p>
129
130 <p>If you are running this tutorial in your own environment, install the required software:</p>
131
132 <ol>
133 <li><p><a target="_blank" href="https://www.python.org/">Python</a>. Version 3.6 (or later) is preferred.</p></li>
134 <li><p>cx_Oracle version 7.3, or version 8, or later.</p></li>
135 <li><p>Oracle Client libraries.</p>
136 <ul>
137 <li><a target="_blank" href="https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html#installing-cx-oracle-on-linux">Linux</a></li>
138 <li><a target="_blank" href="https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html#installing-cx-oracle-on-macos">macOS</a> - please note the special instructions for macOS in the link.</li>
139 <li><a target="_blank" href="https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html#installing-cx-oracle-on-windows">Windows</a></li>
140 </ul>
141 </li>
142
143 <li><p>SQL*Plus such as from the Oracle <a target="_blank" href="https://www.oracle.com/database/technologies/instant-client.html">Instant Client SQL*Plus Package</a>.</p></li>
144 </ol>
145
146 <p>The Advanced Queuing section requires Oracle client 12.2 or later. The SODA section requires Oracle client 18.5, or later, and Oracle Database 18 or later.</p>
147
148 <p>To create the schema run:</p>
149
150 <pre>
151 sqlplus sys/yoursyspassword@localhost/orclpdb1 as sysdba @sql/SetupSamples
152 </pre>
153
154 <p>If DRCP is not already running, connect to the SYS user again in
155 SQL*Plus and execute the command:</p>
156
157 <pre>
158 execute dbms_connection_pool.start_pool()
159 </pre>
160
161
162 <h2><a name="connectioninformation">Connection Information</a></h2>
163
164 <p>Database credentials and the connection string are set in two files:</p>
165
166 <ul>
167 <li><p>db_config.py which is imported by the other Python modules.</p></li>
168 <li><p>db_config.sql which is used by the other SQL scripts.</p></li>
169 </ul>
170
171 <p>The username is "pythonhol" with
172 the password "welcome". The connect string is "localhost/orclpdb1".</p>
173
174 <p>If your database is not local, or has a different service, you will need to
175 modify the connection information in these two files. If you are installing
176 your own schema, you can also modify the default username and password in
177 <code>sql/SampleEnv.sql</code>.</p>
178
179 <p>The following sections may need adjusting, depending on how you
180 have set up your environment.</p>
181
182 <h2><a name="lab">Using Python cx_Oracle with Oracle Database</a></h2>
160183
161184 <p>Python is a popular general purpose dynamic scripting language.
162185 The cx_Oracle interface provides Python API to access Oracle
165188 <ul>
166189 <li>
167190 <h3><a name="connecting">1. Connecting to Oracle</a></h3>
191
192 <p>You can connect from Python to a local or remote database. <i>Documentation
193 link for further reading: <a
194 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/connection_handling.html"
195 >Connecting to Oracle Database</a></i>.</p>
196
168197 <ul>
169198 <li>
170199 <h4>1.1 Review the connection credentials</h4>
173202 <pre>
174203 user = "pythonhol"
175204 pw = "welcome"
176 dsn = "localhost/orclpdb"
205 dsn = "localhost/orclpdb1"
177206 </pre>
178207 <code>db_config.sql</code>
179208 <pre>
180209 def user = "pythonhol"
181210 def pw = "welcome"
182 def connect_string = "localhost/orclpdb"
211 def connect_string = "localhost/orclpdb1"
183212 </pre>
184213
185 <p>By default they connect to the 'orclpdb' database service on the same machine as Python. You can modify the values in both files to match the connection information for your environment.</p>
214 <p>By default they connect to the 'orclpdb1' database service on the same machine as Python. You can modify the values in both files to match the connection information for your environment.</p>
186215
187216 </li>
188217
206235 the db_config.py module. In this case, Oracle's Easy Connect connection
207236 string syntax is used. It consists of the hostname of your
208237 machine, <code>localhost</code>, and the database service name
209 <code>orclpdb</code>. </p>
238 <code>orclpdb1</code>. </p>
210239
211240 <p>Open a command terminal and change to the <code>tutorial</code> directory:</p>
212241
396425
397426 <li><h3><a name="pooling">2. Connection Pooling</a></h3>
398427
428 <p>Connection pooling is important for performance when applications
429 frequently connect and disconnect from the database. Pooling also gives
430 the best support for Oracle high availability features. <i>Documentation
431 link for further reading: <a
432 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/connection_handling.html#connection-pooling"
433 >Connection Pooling</a></i>.</p>
434
435
399436 <ul>
400 <li> <h4>2.1 Session pooling</h4>
437 <li> <h4>2.1 Connection pooling</h4>
401438
402439 <p>Review the code contained in <code>connect_pool.py</code>:</p>
403440 <pre>
429466 </pre>
430467
431468 <p>The <code>SessionPool()</code> function creates a pool of
432 Oracle "sessions" for the user. Sessions in the pool
433 can be used by cx_Oracle connections by calling
434 <code>pool.acquire()</code>. The initial pool size is 2 sessions.
435 The maximum size is 5 sessions. When the pool needs to grow, 1 new
436 session will be created at a time. The pool can shrink back to the
437 minimum size of 2 when sessions are no longer in use.</p>
469 Oracle connections for the user. Connections in the pool can
470 be used by cx_Oracle by calling <code>pool.acquire()</code>.
471 The initial pool size is 2 connections. The maximum size is 5
472 connections. When the pool needs to grow, 1 new connection
473 will be created at a time. The pool can shrink back to the
474 minimum size of 2 when connections are no longer in use.</p>
438475
439476 <p>The <code>def Query():</code> line creates a method that
440477 is called by each thread. </p>
441478
442479 <p>In the method, the <code>pool.acquire()</code> call gets
443 one session from the pool (as long as less than 5 are
444 already in use). This session is used in a loop of 4
480 one connection from the pool (as long as less than 5 are
481 already in use). This connection is used in a loop of 4
445482 iterations to query the sequence <code>myseq</code>. At the
446483 end of the method, cx_Oracle will automatically close the
447 cursor and release the session back to the pool for
484 cursor and release the connection back to the pool for
448485 reuse.</p>
449486
450487 <p>The <code>seqval, = cur.fetchone()</code> line fetches a
467504 </li>
468505
469506 <li>
470 <h4>2.2 Session pool experiments</h4>
507 <h4>2.2 Connection pool experiments</h4>
471508
472509
473510 <p>Review <code>connect_pool2.py</code>, which has a loop for the number
509546
510547 <p>Experiment with different values of the pool parameters and
511548 <code>numberOfThreads</code>. Larger initial pool sizes will make the
512 pool creation slower, but the sessions will be available immediately
513 when needed. When <code>numberOfThreads</code> exceeds the maximum
514 size of the pool, the <code>acquire()</code> call will generate an
515 error. Adding the additional argument <code>getmode =
549 pool creation slower, but the connections will be available immediately
550 when needed. When <code>numberOfThreads</code> exceeds the maximum size
551 of the pool, the <code>acquire()</code> call will generate an error such
552 as "ORA-24459: OCISessionGet() timed out waiting for pool to create new
553 connections". Adding the additional argument <code>getmode =
516554 cx_Oracle.SPOOL_ATTRVAL_WAIT</code> to the
517555 <code>cx_Oracle.SessionPool()</code> call will prevent the exception
518 from taking place, but will cause the thread to wait until a session
556 from taking place, but will cause the thread to wait until a connection
519557 is available.</p>
520558
521559 <p>Pool configurations where <code>min</code> is the same as
522560 <code>max</code> (and <code>increment = 0</code>) are often
523 recommended as a way to avoid connection storms on the database
524 server.</p>
561 recommended as a best practice. This avoids connection storms on the
562 database server.</p>
525563
526564 </li>
527565
533571 server processes.</p>
534572
535573 <p>Below left is a diagram without DRCP. Every application
536 connection or session has its own 'dedicated' database server
574 connection has its own 'dedicated' database server
537575 process. Application connect and close calls require the expensive
538576 create and destroy of those database server processes. To avoid these
539577 costs, scripts may hold connections open even when not doing
545583 <table cellspacing="0" cellpadding="30" border="0" >
546584 <tr>
547585 <td>
548 <img width="400" src="resources/python_nopool.png" >
549 <p><center><strong>Without DRCP</strong></center></p>
586 <img width="400" src="resources/python_nopool.png" alt="Picture of 3-tier application architecture without DRCP showing connections from multiple application processes each going to a server process in the database tier" />
587 <div align="center"><p><strong>Without DRCP</strong></p></div>
550588 </td>
551589 <td>
552 <img width="400" src="resources/python_pool.png" >
553 <p><center><strong>With DRCP</strong></center></p>
590 <img width="400" src="resources/python_pool.png" alt="Picture of 3-tier application architecture with DRCP showing connections from multiple application processes going to a pool of server process in the database tier" />
591 <div align="center"><p><strong>With DRCP</strong></p></div>
554592 </td>
555593 </tr>
556594 </table>
560598 required. However, if database host memory is large enough, then
561599 the default, 'dedicated' server process model is generally
562600 recommended. If DRCP is enabled, it is best used in conjunction
563 with cx_Oracle session pooling.</p>
601 with cx_Oracle's middle-tier connection pooling.</p>
564602
565603 <p>Batch scripts doing long running jobs should generally use
566604 dedicated connections. Both dedicated and DRCP servers can be used
605643 </li>
606644
607645 <li>
608 <h4>2.4 Session pooling and DRCP</h4>
609
610 <p>DRCP works well with session pooling.</p>
646 <h4>2.4 Connection pooling and DRCP</h4>
647
648 <p>DRCP works well with cx_Oracle's connection pooling.</p>
611649
612650 <p>Edit <code>connect_pool2.py</code>, reset any changed pool options, and modify it to use DRCP:</p>
613651 <pre>
646684
647685 <pre><strong>python connect_pool2.py</strong></pre>
648686
649 <p>If you get the error "ORA-24418: Cannot open further
687 <p>If you get an error "ORA-24459: OCISessionGet() timed out waiting for
688 pool to create new connections" or "ORA-24418: Cannot open further
650689 sessions", it is because connection requests are being made
651690 while the pool is starting or growing. Add the argument
652691 <code>getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT</code> to the
653692 <code>cx_Oracle.SessionPool()</code> call so connection
654 requests wait for pooled sessions to be available.</p>
693 requests wait for pooled connections to be available.</p>
655694
656695 <p>Open a new a terminal window and invoke SQL*Plus:</p>
657696
669708 <li>
670709 <h4>2.5 More DRCP investigation</h4>
671710
672 <p>To explore the behaviors of session and DRCP pooling futher,
711 <p>To explore the behaviors of cx_Oracle connection pooling and DRCP pooling futher,
673712 you could try changing the purity to
674713 <code>cx_Oracle.ATTR_PURITY_NEW</code> to see the effect on the
675714 DRCP NUM_MISSES statistic.</p>
687726
688727 </li>
689728 </ul>
729 </li>
690730
691731 <li><h3><a name="fetching">3. Fetching Data</a> </h3>
732
733
734 <p>Executing SELECT queries is the primary way to get data from Oracle
735 Database. <i>Documentation link for further reading: <a
736 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/sql_execution.html"
737 >SQL Queries</a></i>.</p>
692738
693739 <ul>
694740 <li><h4>3.1 A simple query</h4>
696742 <p>There are a number of functions you can use to query an Oracle
697743 database, but the basics of querying are always the same:</p>
698744
699 <p>1. Parse the statement for execution.<br />
745 <p>1. Execute the statement.<br />
700746 2. Bind data values (optional).<br />
701 3. Execute the statement.<br />
702 4. Fetch the results from the database.</p>
747 3. Fetch the results from the database.</p>
703748
704749 <p>Review the code contained in <code>query2.py</code>:</p>
705750
731776
732777 </li>
733778
734 <li><h4>3.2 Using fetchone()</h3>
779 <li><h4>3.2 Using fetchone()</h4>
735780
736781 <p>When the number of rows is large, the <code>fetchall()</code>
737782 call may use too much memory.</p>
846891
847892 </li>
848893
849 <li><h4>3.5 Tuning with arraysize</h4>
850
851 <p>This section demonstrates a way to improve query performance by
852 increasing the number of rows returned in each batch from Oracle to
853 the Python program.</p>
894 <li><h4>3.5 Tuning with arraysize and prefetchrows</h4>
895
896 <p>This section demonstrates a way to improve query performance by increasing
897 the number of rows returned in each batch from Oracle to the Python
898 program.</p>
899
900 <p>Row prefetching and array fetching are both internal buffering techniques
901 to reduce round-trips to the database. The difference is the code layer that
902 is doing the buffering, and when the buffering occurs.</p>
854903
855904 <p>First, create a table with a large number of rows.
856905 Review <code>query_arraysize.sql</code>:</p>
873922
874923 <pre><strong>sqlplus /nolog @query_arraysize.sql</strong></pre>
875924
876
877925 <p>Review the code contained in <code>query_arraysize.py</code>:</p>
878926
879927 <pre>
886934 start = time.time()
887935
888936 cur = con.cursor()
889 cur.arraysize = 10
937 cur.prefetchrows = 100
938 cur.arraysize = 100
890939 cur.execute("select * from bigtab")
891940 res = cur.fetchall()
892941 # print(res) # uncomment to display the query results
895944 print(elapsed, "seconds")
896945 </pre>
897946
898 <p>This uses the 'time' module to measure elapsed time of the
899 query. The arraysize is set to 10. This causes batches of 10
900 records at a time to be returned from the database to a cache in
901 Python. This reduces the number of &quot;roundtrips&quot; made to
902 the database, often reducing network load and reducing the number
903 of context switches on the database server. The
904 <code>fetchone()</code>, <code>fetchmany()</code> and
905 <code>fetchall()</code> methods will read from the cache before
906 requesting more data from the database.</p>
947 <p>This uses the 'time' module to measure elapsed time of the query. The
948 prefetchrows and arraysize values are 100. This causes batches of 100
949 records at a time to be returned from the database to a cache in Python.
950 These values can be tuned to reduce the number of &quot;round-trips&quot;
951 made to the database, often reducing network load and reducing the number of
952 context switches on the database server. The <code>fetchone()</code>,
953 <code>fetchmany()</code> and <code>fetchall()</code> methods will read from
954 the cache before requesting more data from the database.</p>
907955
908956 <p>In a terminal window, run:</p>
909957
910958 <pre><strong>python query_arraysize.py</strong></pre>
911959
912 <p>Reload a few times to see the average times.</p>
913
914 <p>Experiment with different arraysize values. For example, edit
915 <code>query_arraysize.py</code> and change the arraysize to:</p>
960 <p>Rerun a few times to see the average times.</p>
961
962 <p>Experiment with different prefetchrows and arraysize values. For
963 example, edit <code>query_arraysize.py</code> and change the arraysize
964 to:</p>
916965
917966 <pre>cur.arraysize = <strong>2000</strong></pre>
918967
919968 <p>Rerun the script to compare the performance of different
920969 arraysize settings.</p>
921970
922 <p>In general, larger array sizes improve
923 performance. Depending on how fast your system is, you may need
924 to use different arraysizes than those given here to see a
925 meaningful time difference.</p>
926
927 <p>The default arraysize used by cx_Oracle 7 is 100. There is a
928 time/space tradeoff for increasing the arraysize. Larger
929 arraysizes will require more memory in Python for buffering the
930 records.</p>
971 <p>In general, larger array sizes improve performance. Depending on how
972 fast your system is, you may need to use different values than those
973 given here to see a meaningful time difference.</p>
974
975 <p>There is a time/space tradeoff for increasing the values. Larger values
976 will require more memory in Python for buffering the records.</p>
977
978 <p>If you know the query returns a fixed number of rows, for example 20
979 rows, then set arraysize to 20 and prefetchrows to 21. The addition of one
980 for prefetchrows prevents a round-trip to check for end-of-fetch. The
981 statement execution and fetch will take a total of one round-trip. This
982 minimizes load on the database.</p>
931983
932984 <p>If you know a query only returns a few records,
933985 decrease the arraysize from the default to reduce memory
934986 usage.</p>
935
936 </ul>
987 </li>
988 </ul>
937989
938990 </li>
939991
940992 <li><h3><a name="binding">4. Binding Data</a></h3>
941993
942 <p>Bind variables enable you to re-execute statements with new data
943 values, without the overhead of reparsing the statement. Bind
944 variables improve code reusability, and can reduce the risk of SQL
945 injection attacks.</p>
994 <p>Bind variables enable you to re-execute statements with new data values
995 without the overhead of re-parsing the statement. Binding improves code
996 reusability, improves application scalability, and can reduce the risk of SQL
997 injection attacks. Using bind variables is strongly recommended.
998 <i>Documentation link for further reading: <a
999 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/bind.html" >Using
1000 Bind Variables</a></i>.</p>
9461001
9471002 <ul>
9481003
949 <li><h4>4.1 Binding in queries</h3>
1004 <li><h4>4.1 Binding in queries</h4>
9501005
9511006 <p>Review the code contained in <code>bind_query.py</code>:</p>
9521007
9571012 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
9581013 cur = con.cursor()
9591014
960 cur.prepare("select * from dept where deptno = :id order by deptno")
961
962 cur.execute(None, id = 20)
1015 sql = "select * from dept where deptno = :id order by deptno"
1016
1017 cur.execute(sql, id = 20)
9631018 res = cur.fetchall()
9641019 print(res)
9651020
966 cur.execute(None, id = 10)
1021 cur.execute(sql, id = 10)
9671022 res = cur.fetchall()
9681023 print(res)
9691024 </pre>
9701025
971 <p>The statement contains a bind variable "<code>:id</code>"
972 placeholder. The statement is only prepared once but executed
973 twice with different values for the <code>WHERE</code>
974 clause.</p>
975
976 <p> The special symbol "<code>None</code>" is used in place of
977 the statement text argument to <code>execute()</code> because
978 the <code>prepare()</code> method has already set the
979 statement. The second argument to the <code>execute()</code>
980 call can be a sequence (binding by position) or a dictionary (binding
981 by name) or an arbitrary number of named arguments (also binding by
982 name), which is what has been done in this example. In the first execute
983 call, this dictionary has the value 20 for the key of "id". The second
984 execute uses the value 10. </p>
1026 <p>The statement contains a bind variable "<code>:id</code>" placeholder.
1027 The statement is executed twice with different values for the
1028 <code>WHERE</code> clause.</p>
9851029
9861030 <p>From a terminal window, run:</p>
9871031
9891033
9901034 <p>The output shows the details for the two departments.</p>
9911035
1036 <p>An arbitrary number of named arguments can be used in an
1037 <code>execute()</code> call. Each argument name must match a bind
1038 variable name. Alternatively, instead of passing multiple arguments you
1039 could pass a second argument to <code>execute()</code> that is a sequence
1040 or a dictionary. Later examples show these syntaxes.</p>
1041
1042 <p>cx_Oracle uses Oracle Database's Statement Cache. As long as the
1043 statement you pass to <code>execute()</code> is in that cache, you can use
1044 different bind values and still avoid a full statement parse. The
1045 statement cache size is configurable for each connection. To see the
1046 default statement cache size, edit <code>bind_query.py</code> and add a
1047 line at the end:</p>
1048
1049 <pre>
1050 print(con.stmtcachesize)
1051 </pre>
1052
1053 <p>Re-run the file.</p>
1054
1055 <p>In your applications you would set the statement cache size to the
1056 number of unique statements commonly executed.</p>
1057
9921058 </li>
993 <li><h4>4.2 Binding in inserts</h3>
1059
1060 <li><h4>4.2 Binding in inserts</h4>
9941061
9951062 <p>Review the code in <code>bind_insert.sql </code> creating a table
9961063 for inserting data:</p>
10301097 <p>The '<code>rows</code>' array contains the data to be inserted.</p>
10311098
10321099 <p>The <code>executemany()</code> call inserts all rows. This
1033 calls allows "array binding", which is an efficient way to
1100 call uses "array binding", which is an efficient way to
10341101 insert multiple records.</p>
10351102
10361103 <p>The final part of the script queries the results back and displays them as a list of tuples.</p>
10451112
10461113 </li>
10471114
1048 <li><h4>4.3 Batcherrors</h3>
1115 <li><h4>4.3 Batcherrors</h4>
10491116
10501117 <p>The Batcherrors features allows invalid data to be identified
10511118 while allowing valid data to be inserted.</p>
11091176
11101177 <p>The other data gets inserted and is queried back.</p>
11111178
1112 <p>At the end of the script, cx_Oracle will rollback an uncommitted transaction. If you want to commit results, you can use:</p>
1179 <p>At the end of the script, cx_Oracle will roll back an uncommitted transaction. If you want to commit results, you can use:</p>
11131180
11141181 <pre>con.commit()</pre>
11151182
1116 <p>To force a rollback in cx_Oracle, use:</p>
1183 <p>To force cx_Oracle to roll back, use:</p>
11171184
11181185 <pre>con.rollback()</pre>
11191186
11201187 </li>
11211188
1122 <li><h4>4.4 Binding named objects</h3>
1189 <li><h4>4.4 Binding named objects</h4>
11231190
11241191 <p>cx_Oracle can fetch and bind named object types such as Oracle's
11251192 Spatial Data Objects (SDO).</p>
11271194 <p>In a terminal window, start SQL*Plus using the lab credentials and connection string, such as:</p>
11281195
11291196 <pre>
1130 sqlplus pythonhol/welcome@localhost/orclpdb
1197 sqlplus pythonhol/welcome@localhost/orclpdb1
11311198 </pre>
11321199
11331200 <p>Use the SQL*Plus DESCRIBE command to look at the SDO definition:</p>
11611228 cur.execute("""begin
11621229 execute immediate 'drop table testgeometry';
11631230 exception when others then
1164 if sqlcode <> -942 then
1231 if sqlcode &lt;&gt; -942 then
11651232 raise;
11661233 end if;
11671234 end;""")
12781345 <p>The <code>gettype()</code> and <code>newobject()</code> methods can
12791346 also be used to bind PL/SQL Records and Collections.</p>
12801347
1348 <p>Before deciding to use objects, review your performance goals because
1349 working with scalar values can be faster.</p>
1350
12811351 </li>
12821352 </ul>
12831353
12901360 PL/SQL lets all database applications reuse logic, no matter how the
12911361 application accesses the database. Many data-related operations can
12921362 be performed in PL/SQL faster than extracting the data into a
1293 program (for example, Python) and then processing it.</p>
1363 program (for example, Python) and then processing it. <i>Documentation link
1364 for further reading: <a
1365 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/plsql_execution.html"
1366 >PL/SQL Execution</a></i>.</p>
12941367
12951368 <ul>
1296 <li><h4>5.1 PL/SQL functions</h3>
1369 <li><h4>5.1 PL/SQL functions</h4>
12971370
12981371 <p>Review <code>plsql_func.sql</code> which creates a PL/SQL
12991372 stored function <code>myfunc()</code> to insert a row into a new
13431416
13441417 </li>
13451418
1346 <li><h4>5.2 PL/SQL procedures</h3>
1419 <li><h4>5.2 PL/SQL procedures</h4>
13471420
13481421 <p>Review <code>plsql_proc.sql</code> which creates a PL/SQL procedure
13491422 <code>myproc()</code> to accept two parameters. The second parameter
13951468
13961469 <li><h3><a name="handlers">6. Type Handlers</a></h3>
13971470
1471 <p>Type handlers enable applications to alter data that is fetched from, or sent
1472 to, the database. <i>Documentation links for further reading: <a
1473 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/sql_execution.html#changing-fetched-data-types-with-output-type-handlers"
1474 >Changing Fetched Data Types with Output Type Handlers</a> and <a
1475 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/bind.html#changing-bind-data-types-using-an-input-type-handler"
1476 >Changing Bind Data Types using an Input Type Handler</a></i>.</p>
1477
13981478 <ul>
13991479 <li>
14001480 <h4>6.1 Basic output type handler</h4>
15571637 cur.execute("""begin
15581638 execute immediate 'drop table testgeometry';
15591639 exception when others then
1560 if sqlcode <> -942 then
1640 if sqlcode &lt;&gt; -942 then
15611641 raise;
15621642 end if;
15631643 end;""")
16601740
16611741 <li><h3><a name="lobs">7. LOBs</a></h3>
16621742
1743 <p>Oracle Database "LOB" long objects can be streamed using a LOB
1744 locator, or worked with directly as strings or bytes. <i>Documentation link
1745 for further reading: <a
1746 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/lob_data.html"
1747 >Using CLOB and BLOB Data</a></i>.</p>
1748
16631749 <ul>
16641750 <li>
16651751 <h4>7.1 Fetching a CLOB using a locator</h4>
16841770 con.commit()
16851771
16861772 print("Querying data...")
1687 cur.prepare("select * from testclobs where id = :id")
1688 cur.execute(None, {'id': 1})
1773 cur.execute("select * from testclobs where id = :id", {'id': 1})
16891774 (id, clob) = cur.fetchone()
16901775 print("CLOB length:", clob.size())
16911776 clobdata = clob.read()
16931778 </pre>
16941779
16951780 <p>This inserts some test string data and then fetches one
1696 record into <code>clob</code>, which is a cx_Oracle LOB Object.
1697 Methods on LOB include <code>size()</code> and
1781 record into <code>clob</code>, which is a cx_Oracle character
1782 LOB Object. Methods on LOB include <code>size()</code> and
16981783 <code>read()</code>.</p>
16991784
17001785 <p>To see the output, run the file:</p>
17401825 con.outputtypehandler = OutputTypeHandler</strong>
17411826
17421827 print("Querying data...")
1743 cur.prepare("select * from testclobs where id = :id")
1744 cur.execute(None, {'id': 1})
1828 cur.execute("select * from testclobs where id = :id", {'id': 1})
17451829 <strong>(id, clobdata) = cur.fetchone()
17461830 print("CLOB length:", len(clobdata))
17471831 print("CLOB data:", clobdata)</strong>
18401924
18411925 <p>Subclassing enables application to "hook" connection and cursor
18421926 creation. This can be used to alter or log connection and execution
1843 parameters, and to extend cx_Oracle functionality. </p>
1927 parameters, and to extend cx_Oracle functionality. <i>Documentation link for
1928 further reading: <a
1929 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/tracing_sql.html"
1930 >Tracing SQL and PL/SQL Statements</a></i>.</p>
18441931
18451932 <ul>
18461933 <li><h4>9.1 Subclassing connections</h4>
19452032 </li>
19462033
19472034 <li><h3><a name="aq">10. Advanced Queuing</a></h3>
2035
2036 <p>Oracle Advanced Queuing (AQ) allows messages to be passed between
2037 applications. <i>Documentation link for further reading: <a
2038 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/aq.html" >Oracle
2039 Advanced Queuing (AQ)</a></i>.</p>
2040
19482041 <ul>
1949 <li><h4>10.1 Message passing with Oracle Advanced Queuing</h4></li>
2042 <li><h4>10.1 Message passing with Oracle Advanced Queuing</h4>
19502043
19512044 <p>Review <code>aq.py</code>:</p>
19522045
19702063 dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """');
19712064 execute immediate 'drop type """ + BOOK_TYPE_NAME + """';
19722065 exception when others then
1973 if sqlcode <> -24010 then
2066 if sqlcode &lt;&gt; -24010 then
19742067 raise;
19752068 end if;
19762069 end;""")
19772070
1978 # Create type
2071 # Create a type
19792072 print("Creating books type UDT_BOOK...")
19802073 cur.execute("""
19812074 create type %s as object (
19872080 # Create queue table and queue and start the queue
19882081 print("Creating queue table...")
19892082 cur.callproc("dbms_aqadm.create_queue_table",
1990 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
2083 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
19912084 cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME))
19922085 cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,))
19932086
2087 booksType = con.gettype(BOOK_TYPE_NAME)
2088 queue = con.queue(QUEUE_NAME, booksType)
2089
19942090 # Enqueue a few messages
1995 booksType = con.gettype(BOOK_TYPE_NAME)
1996 book1 = booksType.newobject()
1997 book1.TITLE = "The Fellowship of the Ring"
1998 book1.AUTHORS = "Tolkien, J.R.R."
1999 book1.PRICE = decimal.Decimal("10.99")
2000 book2 = booksType.newobject()
2001 book2.TITLE = "Harry Potter and the Philosopher's Stone"
2002 book2.AUTHORS = "Rowling, J.K."
2003 book2.PRICE = decimal.Decimal("7.99")
2004 options = con.enqoptions()
2005 messageProperties = con.msgproperties()
2006 for book in (book1, book2):
2007 print("Enqueuing book", book.TITLE)
2008 con.enq(QUEUE_NAME, options, messageProperties, book)
2009 con.commit()
2091 print("Enqueuing messages...")
2092
2093 BOOK_DATA = [
2094 ("The Fellowship of the Ring", "Tolkien, J.R.R.", decimal.Decimal("10.99")),
2095 ("Harry Potter and the Philosopher's Stone", "Rowling, J.K.",
2096 decimal.Decimal("7.99"))
2097 ]
2098
2099 for title, authors, price in BOOK_DATA:
2100 book = booksType.newobject()
2101 book.TITLE = title
2102 book.AUTHORS = authors
2103 book.PRICE = price
2104 print(title)
2105 queue.enqOne(con.msgproperties(payload=book))
2106 con.commit()
20102107
20112108 # Dequeue the messages
2012 options = con.deqoptions()
2013 options.navigation = cx_Oracle.DEQ_FIRST_MSG
2014 options.wait = cx_Oracle.DEQ_NO_WAIT
2015 while con.deq(QUEUE_NAME, options, messageProperties, book):
2016 print("Dequeued book", book.TITLE)
2017 con.commit()
2109 print("\nDequeuing messages...")
2110 queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
2111 while True:
2112 props = queue.deqOne()
2113 if not props:
2114 break
2115 print(props.payload.TITLE)
2116 con.commit()
2117
2118 print("\nDone.")
20182119 </pre>
20192120
20202121 <p>This file sets up Advanced Queuing using Oracle's DBMS_AQADM
2021 package. The queue is used for passing Oracle UDT_BOOK objects.</p>
2122 package. The queue is used for passing Oracle UDT_BOOK objects. The
2123 file uses AQ interface features enhanced in cx_Oracle 7.2.</p>
20222124
20232125 <p>Run the file:</p>
20242126
20292131 <p>To experiment, split the code into three files: one to create and
20302132 start the queue, and two other files to queue and dequeue messages.
20312133 Experiment running the queue and dequeue files concurrently in
2032 separate terminal windows. If you are stuck, look in the
2033 <code>solutions</code> directory at the <code>aq-dequeue.py</code>,
2034 <code>aq-enqueue.py</code> and <code>aq-queuestart.py</code>
2035 files.</p>
2036
2037 <p>Try changing the dequeue options and mode. For example change the
2038 dequeue <code>options.wait</code> value to
2039 <code>cx_Oracle.DEQ_WAIT_FOREVER</code>.</p>
2040
2134 separate terminal windows.</p>
2135
2136 <p>Try removing the <code>commit()</code> call in
2137 <code>aq-dequeue.py</code>. Now run <code>aq-enqueue.py</code> once
2138 and then <code>aq-dequeue.py</code> several times. The same messages
2139 will be available each time you try to dequeue them.</p>
2140
2141 <p>Change <code>aq-dequeue.py</code> to commit in a separate
2142 transaction by changing the "visibility" setting:</p>
2143
2144 <pre>
2145 queue.deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
2146 </pre>
2147
2148 <p>This gives the same behavior as the original code.</p>
2149
2150 <p>Now change the options of enqueued messages so that they expire from the
2151 queue if they have not been dequeued after four seconds:</p>
2152
2153 <pre>
2154 queue.enqOne(con.msgproperties(payload=book, expiration=4))
2155 </pre>
2156
2157 <p>Now run <code>aq-enqueue.py</code> and wait four seconds before you
2158 run <code>aq-dequeue.py</code>. There should be no messages to
2159 dequeue. </p>
2160
2161 <p>If you are stuck, look in the <code>solutions</code> directory at
2162 the <code>aq-dequeue.py</code>, <code>aq-enqueue.py</code> and
2163 <code>aq-queuestart.py</code> files.</p>
2164
2165 </li>
20412166 </ul>
20422167 </li>
20432168
2044 </ol>
2169 <li><h3><a name="soda">11. Simple Oracle Document Access (SODA)</a></h3>
2170
2171 <p>Simple Oracle Document Access (SODA) is a set of NoSQL-style APIs.
2172 Documents can be inserted, queried, and retrieved from Oracle
2173 Database. By default, documents are JSON strings. SODA APIs
2174 exist in many languages. <i>Documentation link for further reading: <a
2175 href="https://cx-oracle.readthedocs.io/en/latest/user_guide/soda.html" >Simple
2176 Oracle Document Access (SODA)</a></i>.</p>
2177
2178 <ul>
2179
2180 <li><h4>11.1 Inserting JSON Documents</h4>
2181
2182 <p>Review <code>soda.py</code>:</p>
2183
2184 <pre>
2185 import cx_Oracle
2186 import db_config
2187
2188 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
2189
2190 soda = con.getSodaDatabase()
2191
2192 collection = soda.createCollection("friends")
2193
2194 content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}}
2195
2196 doc = collection.insertOneAndGet(content)
2197 key = doc.key
2198
2199 doc = collection.find().key(key).getOne()
2200 content = doc.getContent()
2201 print('Retrieved SODA document dictionary is:')
2202 print(content)
2203 </pre>
2204
2205 <p><code>soda.createCollection()</code> will create a new
2206 collection, or open an existing collection, if the name is
2207 already in use.</p>
2208
2209 <p><code>insertOneAndGet()</code> inserts the content of a
2210 document into the database and returns a SODA Document Object.
2211 This allows access to meta data such as the document key. By
2212 default, document keys are automatically generated.</p>
2213
2214 <p>The <code>find()</code> method is used to begin an operation
2215 that will act upon documents in the collection.</p>
2216
2217 <p><code>content</code> is a dictionary. You can also get a JSON string
2218 by calling <code>doc.getContentAsString()</code>.</p>
2219
2220 <p>Run the file:</p>
2221
2222 <pre><strong>python soda.py</strong></pre>
2223
2224 <p>The output shows the content of the new document.</p>
2225
2226 </li>
2227
2228 <li><h4>11.2 Searching SODA Documents</h4>
2229
2230 <p>Extend <code>soda.py</code> to insert some more documents and
2231 perform a find filter operation:</p>
2232
2233 <pre>
2234 myDocs = [
2235 {'name': 'Gerald', 'age': 21, 'address': {'city': 'London'}},
2236 {'name': 'David', 'age': 28, 'address': {'city': 'Melbourne'}},
2237 {'name': 'Shawn', 'age': 20, 'address': {'city': 'San Francisco'}}
2238 ]
2239 collection.insertMany(myDocs)
2240
2241 filterSpec = { "address.city": "Melbourne" }
2242 myDocuments = collection.find().filter(filterSpec).getDocuments()
2243
2244 print('Melbourne people:')
2245 for doc in myDocuments:
2246 print(doc.getContent()["name"])
2247 </pre>
2248
2249 <p>Run the script again:</p>
2250
2251 <pre><strong>python soda.py</strong></pre>
2252
2253 <p>The find operation filters the collection and returns
2254 documents where the city is Melbourne. Note the
2255 <code>insertMany()</code> method is currently in preview.</p>
2256
2257 <p>SODA supports query by example (QBE) with an extensive set of
2258 operators. Extend <code>soda.py</code> with a QBE to find
2259 documents where the age is less than 25:</p>
2260
2261 <pre>
2262 filterSpec = {'age': {'$lt': 25}}
2263 myDocuments = collection.find().filter(filterSpec).getDocuments()
2264
2265 print('Young people:')
2266 for doc in myDocuments:
2267 print(doc.getContent()["name"])
2268 </pre>
2269
2270 <p>Running the script displays the names.</p>
2271
2272 </li>
2273 </ul>
2274
2275 </li>
2276
2277 </ul>
20452278
20462279 <h2><a name="summary">Summary</a></h2>
20472280 <p>In this tutorial, you have learned how to: </p>
20482281 <ul>
20492282 <li>Create connections</li>
2050 <li>Use sessions pooling and Database Resident Connection Pooling</li>
2283 <li>Use cx_Oracle connection pooling and Database Resident Connection Pooling</li>
20512284 <li>Execute queries and fetch data</li>
20522285 <li>Use bind variables</li>
20532286 <li>Use PL/SQL stored functions and procedures</li>
20542287 <li>Extend cx_Oracle classes</li>
20552288 <li>Use Oracle Advanced Queuing</li>
2289 <li>Use the "SODA" document store API</li>
20562290 </ul>
20572291
2292 <p>For further reading see the <a
2293 href="https://cx-oracle.readthedocs.io/en/latest/index.html" >cx_Oracle
2294 documentation</a>.</p>
20582295
20592296 <h2><a name="primer">Appendix: Python Primer</a></h2>
20602297
20612298 <p>Python is a dynamically typed scripting language. It is most
2062 often used to run command-line scripts but is also used in Web
2063 applications.</p>
2299 often used to run command-line scripts but is also used for web
2300 applications and web services.</p>
20642301
20652302 <h4>Running Python</h4>
20662303
21422379 <p> Strings and variables can be displayed with a <code>print()</code> function:</p>
21432380 <pre>print('Hello, World!')
21442381 print('Value:', count)</pre>
2145
2146 <P>Note the <a
2147 href="https://docs.python.org/3.0/whatsnew/3.0.html#print-is-a-function"
2148 ><code>print</code></a> syntax and output is different in Python
2149 2. Examples in this lab use <code>from __future__ import print_function
2150 </code> so that they run with Python 2 and Python 3.</p>
21512382
21522383 <h4>Data Structures</h4>
21532384
22012432 like:</p>
22022433
22032434 <pre>
2204 if sal &gt; 900000:
2205 print('Salary is way too big')
2206 elif sal &gt; 500000:
2207 print('Salary is huge')
2435 if v == 2 or v == 4:
2436 print('Even')
2437 elif v == 1 or v == 3:
2438 print('Odd')
22082439 else:
2209 print('Salary might be OK')
2440 print('Unknown number')
22102441 </pre>
22112442
22122443 <p>This also shows how the clauses are delimited with colons, and each
22742505 <div class="footer"></div>
22752506 <table border="0" cellpadding="10" cellspacing="0" width="100%">
22762507 <tbody><tr>
2277 <td align="right" width="54%">Copyright &copy; 2017, Oracle and/or its affiliates. All rights reserved</td>
2508 <td align="right" width="54%">Copyright &copy; 2017, 2020, Oracle and/or its affiliates. All rights reserved</td>
22782509 </tr>
22792510 <tr><td colspan="2"></td></tr>
22802511 </tbody>
22812512 </table>
22822513
2283
2284 </div>
22852514 </body>
22862515 </html>
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import decimal
3129 end if;
3230 end;""")
3331
34 # Create type
32 # Create a type
3533 print("Creating books type UDT_BOOK...")
3634 cur.execute("""
3735 create type %s as object (
4341 # Create queue table and queue and start the queue
4442 print("Creating queue table...")
4543 cur.callproc("dbms_aqadm.create_queue_table",
46 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
44 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
4745 cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME))
4846 cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,))
4947
48 booksType = con.gettype(BOOK_TYPE_NAME)
49 queue = con.queue(QUEUE_NAME, booksType)
50
5051 # Enqueue a few messages
51 booksType = con.gettype(BOOK_TYPE_NAME)
52 book1 = booksType.newobject()
53 book1.TITLE = "The Fellowship of the Ring"
54 book1.AUTHORS = "Tolkien, J.R.R."
55 book1.PRICE = decimal.Decimal("10.99")
56 book2 = booksType.newobject()
57 book2.TITLE = "Harry Potter and the Philosopher's Stone"
58 book2.AUTHORS = "Rowling, J.K."
59 book2.PRICE = decimal.Decimal("7.99")
60 options = con.enqoptions()
61 messageProperties = con.msgproperties()
62 for book in (book1, book2):
63 print("Enqueuing book", book.TITLE)
64 con.enq(QUEUE_NAME, options, messageProperties, book)
65 con.commit()
52 print("Enqueuing messages...")
53
54 BOOK_DATA = [
55 ("The Fellowship of the Ring", "Tolkien, J.R.R.", decimal.Decimal("10.99")),
56 ("Harry Potter and the Philosopher's Stone", "Rowling, J.K.",
57 decimal.Decimal("7.99"))
58 ]
59
60 for title, authors, price in BOOK_DATA:
61 book = booksType.newobject()
62 book.TITLE = title
63 book.AUTHORS = authors
64 book.PRICE = price
65 print(title)
66 queue.enqOne(con.msgproperties(payload=book))
67 con.commit()
6668
6769 # Dequeue the messages
68 options = con.deqoptions()
69 options.navigation = cx_Oracle.DEQ_FIRST_MSG
70 options.wait = cx_Oracle.DEQ_NO_WAIT
71 while con.deq(QUEUE_NAME, options, messageProperties, book):
72 print("Dequeued book", book.TITLE)
73 con.commit()
70 print("\nDequeuing messages...")
71 queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
72 while True:
73 props = queue.deqOne()
74 if not props:
75 break
76 print(props.payload.TITLE)
77 con.commit()
78
79 print("\nDone.")
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
1311 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1412 cur = con.cursor()
1513
16 cur.prepare("select * from dept where deptno = :id order by deptno")
14 sql = "select * from dept where deptno = :id order by deptno"
1715
18 cur.execute(None, id = 20)
16 cur.execute(sql, id = 20)
1917 res = cur.fetchall()
2018 print(res)
2119
22 cur.execute(None, id = 10)
20 cur.execute(sql, id = 10)
2321 res = cur.fetchall()
2422 print(res)
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
2018 char = chr(ord('A') + i)
2119 longString += char * 250
2220 cur.execute("insert into testclobs values (:1, :2)",
23 (i + 1, "String data " + longString + ' End of string'))
21 (i + 1, "String data " + longString + ' End of string'))
2422 con.commit()
2523
2624 print("Querying data...")
27 cur.prepare("select * from testclobs where id = :id")
28 cur.execute(None, {'id': 1})
25 cur.execute("select * from testclobs where id = :id", {'id': 1})
2926 (id, clob) = cur.fetchone()
3027 print("CLOB length:", clob.size())
3128 clobdata = clob.read()
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
3028 con.outputtypehandler = OutputTypeHandler
3129
3230 print("Querying data...")
33 cur.prepare("select * from testclobs where id = :id")
34 cur.execute(None, {'id': 1})
31 cur.execute("select * from testclobs where id = :id", {'id': 1})
3532 (id, clobdata) = cur.fetchone()
3633 print("CLOB length:", len(clobdata))
3734 print("CLOB data:", clobdata)
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import threading
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import threading
00 user = "pythonhol"
11 pw = "welcome"
2 dsn = "localhost/orclpdb"
2 dsn = "localhost/orclpdb1"
00 def user = "pythonhol"
11 def pw = "welcome"
2 def connect_string = "localhost/orclpdb"
2 def connect_string = "localhost/orclpdb1"
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import time
1614 start = time.time()
1715
1816 cur = con.cursor()
19 cur.arraysize = 10
17 cur.prefetchrows = 100
18 cur.arraysize = 100
2019 cur.execute("select * from bigtab")
2120 res = cur.fetchall()
2221 # print(res) # uncomment to display the query results
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import collections
119 import cx_Oracle
0 #------------------------------------------------------------------------------
1 # soda.py (Section 11.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 import cx_Oracle
9 import db_config
10
11 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
12
13 soda = con.getSodaDatabase()
14
15 collection = soda.createCollection("friends")
16
17 content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}}
18
19 doc = collection.insertOneAndGet(content)
20 key = doc.key
21
22 doc = collection.find().key(key).getOne()
23 content = doc.getContent()
24 print('Retrieved SODA document dictionary is:')
25 print(content)
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import decimal
1917 QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
2018
2119 # Dequeue the messages
22 options = con.deqoptions()
23 options.navigation = cx_Oracle.DEQ_FIRST_MSG
24 options.wait = cx_Oracle.DEQ_NO_WAIT
25 messageProperties = con.msgproperties()
2620 booksType = con.gettype(BOOK_TYPE_NAME)
27 book = booksType.newobject()
28 while con.deq(QUEUE_NAME, options, messageProperties, book):
29 print("Dequeued book", book.TITLE)
30 con.commit()
21 queue = con.queue(QUEUE_NAME, booksType)
22 queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
23 queue.deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
24
25 print("\nDequeuing messages...")
26 while True:
27 props = queue.deqOne()
28 if not props:
29 break
30 print(props.payload.TITLE)
31
32 print("\nDone.")
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import decimal
1917 QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
2018
2119 # Enqueue a few messages
20 print("Enqueuing messages...")
21
22 BOOK_DATA = [
23 ("The Fellowship of the Ring", "Tolkien, J.R.R.", decimal.Decimal("10.99")),
24 ("Harry Potter and the Philosopher's Stone", "Rowling, J.K.", decimal.Decimal("7.99"))
25 ]
26
2227 booksType = con.gettype(BOOK_TYPE_NAME)
23 book1 = booksType.newobject()
24 book1.TITLE = "The Fellowship of the Ring"
25 book1.AUTHORS = "Tolkien, J.R.R."
26 book1.PRICE = decimal.Decimal("10.99")
27 book2 = booksType.newobject()
28 book2.TITLE = "Harry Potter and the Philosopher's Stone"
29 book2.AUTHORS = "Rowling, J.K."
30 book2.PRICE = decimal.Decimal("7.99")
31 options = con.enqoptions()
32 messageProperties = con.msgproperties()
33 for book in (book1, book2):
34 print("Enqueuing book", book.TITLE)
35 con.enq(QUEUE_NAME, options, messageProperties, book)
36 con.commit()
28 queue = con.queue(QUEUE_NAME, booksType)
29
30 for title, authors, price in BOOK_DATA:
31 book = booksType.newobject()
32 book.TITLE = title
33 book.AUTHORS = authors
34 book.PRICE = price
35 print(title)
36 queue.enqOne(con.msgproperties(payload=book, expiration=4))
37 con.commit()
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import decimal
3129 end if;
3230 end;""")
3331
34 # Create type
32 # Create a type
3533 print("Creating books type UDT_BOOK...")
3634 cur.execute("""
3735 create type %s as object (
4341 # Create queue table and queue and start the queue
4442 print("Creating queue table...")
4543 cur.callproc("dbms_aqadm.create_queue_table",
46 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
44 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
4745 cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME))
4846 cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,))
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import threading
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import collections
119 import cx_Oracle
0 #------------------------------------------------------------------------------
1 # soda.py (Section 11.2)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 import cx_Oracle
9 import db_config
10
11 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
12
13 soda = con.getSodaDatabase()
14
15 collection = soda.createCollection("friends")
16
17 content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}}
18
19 doc = collection.insertOneAndGet(content)
20 key = doc.key
21
22 doc = collection.find().key(key).getOne()
23 content = doc.getContent()
24 print('Retrieved SODA document dictionary is:')
25 print(content)
26
27 myDocs = [
28 {'name': 'Gerald', 'age': 21, 'address': {'city': 'London'}},
29 {'name': 'David', 'age': 28, 'address': {'city': 'Melbourne'}},
30 {'name': 'Shawn', 'age': 20, 'address': {'city': 'San Francisco'}}
31 ]
32 collection.insertMany(myDocs)
33
34 filterSpec = { "address.city": "Melbourne" }
35 myDocuments = collection.find().filter(filterSpec).getDocuments()
36
37 print('Melbourne people:')
38 for doc in myDocuments:
39 print(doc.getContent()["name"])
40
41 filterSpec = {'age': {'$lt': 25}}
42 myDocuments = collection.find().filter(filterSpec).getDocuments()
43
44 print('Young people:')
45 for doc in myDocuments:
46 print(doc.getContent()["name"])
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import decimal
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
3636
3737 grant execute on dbms_aqadm to &main_user;
3838 grant execute on dbms_lock to &main_user;
39
40 begin
41
42 for r in
43 ( select role
44 from dba_roles
45 where role in ('SODA_APP')
46 ) loop
47 execute immediate 'grant ' || r.role || ' to &main_user';
48 end loop;
49
50 end;
51 /
3952
4053 create table &main_user..testclobs (
4154 id number not null,
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
5 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
66 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
97
108 import cx_Oracle
119 import db_config
0 [metadata]
1 name = cx_Oracle
2 description = Python interface to Oracle
3 long_description = file: README.md
4 long_description_content_type = text/markdown
5 keywords = Oracle, database
6 author = "Anthony Tuininga",
7 author_email = "[email protected]",
8 license = BSD License
9 url = https://oracle.github.io/python-cx_Oracle
10 project_urls =
11 Installation = https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html
12 Samples = https://github.com/oracle/python-cx_Oracle/tree/master/samples
13 Documentation = http://cx-oracle.readthedocs.io
14 Release Notes = https://cx-oracle.readthedocs.io/en/latest/release_notes.html#releasenotes
15 Issues = https://github.com/oracle/python-cx_Oracle/issues
16 Source = https://github.com/oracle/python-cx_Oracle
17 python_requires = >=3.6
18 classifiers =
19 Development Status :: 6 - Mature
20 Intended Audience :: Developers
21 License :: OSI Approved :: BSD License
22 Natural Language :: English
23 Operating System :: OS Independent
24 Programming Language :: C
25 Programming Language :: Python :: 3 :: Only
26 Topic :: Database
0 """Distutils script for cx_Oracle.
0 """Setup script for cx_Oracle.
11
22 Windows platforms:
33 python setup.py build --compiler=mingw32 install
77
88 """
99
10 import distutils.core
1110 import os
11 import pkg_resources
12 import setuptools
1213 import sys
1314
14 # if setuptools is detected, use it to add support for eggs
15 try:
16 from setuptools import setup, Extension
17 except:
18 from distutils.core import setup
19 from distutils.extension import Extension
15 # check minimum supported Python version
16 if sys.version_info[:2] < (3, 6):
17 raise Exception("Python 3.6 or higher is required. " +
18 "For python 2, use 'pip install cx_Oracle==7.3'")
19
20 # check minimum supported version of setuptools
21 pkg_resources.require("setuptools>=40.6.0")
2022
2123 # define build constants
22 BUILD_VERSION = "7.1.0"
24 BUILD_VERSION = "8.1.0"
2325
2426 # setup extra link and compile args
2527 extraLinkArgs = []
3335 elif sys.platform == "darwin":
3436 extraLinkArgs.append("-shared-libgcc")
3537
36 class test(distutils.core.Command):
37 description = "run the test suite for the extension"
38 user_options = []
39
40 def finalize_options(self):
41 pass
42
43 def initialize_options(self):
44 pass
45
46 def run(self):
47 self.run_command("build")
48 buildCommand = self.distribution.get_command_obj("build")
49 sys.path.insert(0, os.path.abspath("test"))
50 sys.path.insert(0, os.path.abspath(buildCommand.build_lib))
51 fileName = os.path.join("test", "test.py")
52 exec(open(fileName).read())
53
54 # define classifiers for the package index
55 classifiers = [
56 "Development Status :: 6 - Mature",
57 "Intended Audience :: Developers",
58 "License :: OSI Approved :: BSD License",
59 "Natural Language :: English",
60 "Operating System :: OS Independent",
61 "Programming Language :: C",
62 "Programming Language :: Python",
63 "Programming Language :: Python :: 2",
64 "Programming Language :: Python :: 3",
65 "Topic :: Database"
66 ]
67
6838 # define cx_Oracle sources
6939 sourceDir = "src"
7040 sources = [os.path.join(sourceDir, n) \
7141 for n in sorted(os.listdir(sourceDir)) if n.endswith(".c")]
7242 depends = ["src/cxoModule.h"]
73
7443
7544 # define ODPI-C sources, libraries and include directories; if the environment
7645 # variables ODPIC_INC_DIR and ODPIC_LIB_DIR are both set, assume these
9463 libraryDirs = []
9564
9665 # setup the extension
97 extension = Extension(
66 extension = setuptools.Extension(
9867 name = "cx_Oracle",
9968 include_dirs = includeDirs,
10069 extra_compile_args = extraCompileArgs,
10675 library_dirs = libraryDirs)
10776
10877 # perform the setup
109 setup(
110 name = "cx_Oracle",
78 setuptools.setup(
11179 version = BUILD_VERSION,
112 description = "Python interface to Oracle",
113 cmdclass = dict(test = test),
11480 data_files = [ ("cx_Oracle-doc", ["LICENSE.txt", "README.txt"]) ],
115 long_description = \
116 "Python interface to Oracle Database conforming to the Python DB "
117 "API 2.0 specification.\n"
118 "See http://www.python.org/topics/database/DatabaseAPI-2.0.html.",
119 author = "Anthony Tuininga",
120 author_email = "[email protected]",
121 url = "https://oracle.github.io/python-cx_Oracle",
122 ext_modules = [extension],
123 keywords = "Oracle",
124 license = "BSD License",
125 classifiers = classifiers)
126
81 ext_modules = [extension])
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoApiType.c
6 // Defines the objects used for identifying types defined by the Python
7 // Database API.
8 //-----------------------------------------------------------------------------
9
10 #include "cxoModule.h"
11
12 //-----------------------------------------------------------------------------
13 // cxoApiType_free()
14 // Free the API type object.
15 //-----------------------------------------------------------------------------
16 static void cxoApiType_free(cxoApiType *apiType)
17 {
18 Py_CLEAR(apiType->dbTypes);
19 Py_TYPE(apiType)->tp_free((PyObject*) apiType);
20 }
21
22
23 //-----------------------------------------------------------------------------
24 // cxoApiType_repr()
25 // Return a string representation of a queue.
26 //-----------------------------------------------------------------------------
27 static PyObject *cxoApiType_repr(cxoApiType *apiType)
28 {
29 PyObject *module, *name, *apiTypeName, *result;
30
31 apiTypeName = PyUnicode_DecodeASCII(apiType->name, strlen(apiType->name),
32 NULL);
33 if (!apiTypeName)
34 return NULL;
35 if (cxoUtils_getModuleAndName(Py_TYPE(apiType), &module, &name) < 0) {
36 Py_DECREF(apiTypeName);
37 return NULL;
38 }
39 result = cxoUtils_formatString("<%s.%s %s>",
40 PyTuple_Pack(3, module, name, apiTypeName));
41 Py_DECREF(module);
42 Py_DECREF(name);
43 Py_DECREF(apiTypeName);
44 return result;
45 }
46
47
48 //-----------------------------------------------------------------------------
49 // cxoApiType_reduce()
50 // Method provided for pickling/unpickling of API types.
51 //-----------------------------------------------------------------------------
52 static PyObject *cxoApiType_reduce(cxoApiType *apiType)
53 {
54 return PyUnicode_DecodeASCII(apiType->name, strlen(apiType->name), NULL);
55 }
56
57
58 //-----------------------------------------------------------------------------
59 // declaration of methods
60 //-----------------------------------------------------------------------------
61 static PyMethodDef cxoMethods[] = {
62 { "__reduce__", (PyCFunction) cxoApiType_reduce, METH_NOARGS },
63 { NULL, NULL }
64 };
65
66
67 //-----------------------------------------------------------------------------
68 // declaration of members
69 //-----------------------------------------------------------------------------
70 static PyMemberDef cxoMembers[] = {
71 { "name", T_STRING, offsetof(cxoApiType, name), READONLY },
72 { NULL }
73 };
74
75
76 //-----------------------------------------------------------------------------
77 // Python type declaration
78 //-----------------------------------------------------------------------------
79 PyTypeObject cxoPyTypeApiType = {
80 PyVarObject_HEAD_INIT(NULL, 0)
81 .tp_name = "cx_Oracle.ApiType",
82 .tp_basicsize = sizeof(cxoApiType),
83 .tp_dealloc = (destructor) cxoApiType_free,
84 .tp_repr = (reprfunc) cxoApiType_repr,
85 .tp_flags = Py_TPFLAGS_DEFAULT,
86 .tp_members = cxoMembers,
87 .tp_methods = cxoMethods
88 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
3030 return -1;
3131 buf->ptr = PyBytes_AS_STRING(buf->obj);
3232 buf->size = (uint32_t) PyBytes_GET_SIZE(buf->obj);
33 #if PY_MAJOR_VERSION < 3
34 buf->numCharacters = (uint32_t) PyUnicode_GET_SIZE(obj);
35 #else
3633 buf->numCharacters = (uint32_t) PyUnicode_GET_LENGTH(obj);
37 #endif
3834 } else if (PyBytes_Check(obj)) {
3935 Py_INCREF(obj);
4036 buf->obj = obj;
4137 buf->ptr = PyBytes_AS_STRING(buf->obj);
4238 buf->size = buf->numCharacters = (uint32_t) PyBytes_GET_SIZE(buf->obj);
43 #if PY_MAJOR_VERSION < 3
44 } else if (PyBuffer_Check(obj)) {
45 Py_ssize_t temp;
46 if (PyObject_AsReadBuffer(obj, (void*) &buf->ptr, &temp) < 0)
47 return -1;
48 Py_INCREF(obj);
49 buf->obj = obj;
50 buf->numCharacters = buf->size = (uint32_t) temp;
51 #endif
5239 } else {
53 #if PY_MAJOR_VERSION >= 3
5440 PyErr_SetString(PyExc_TypeError, "expecting string or bytes object");
55 #else
56 PyErr_SetString(PyExc_TypeError,
57 "expecting string, unicode or buffer object");
58 #endif
5941 return -1;
6042 }
6143 return 0;
7557 buf->obj = NULL;
7658 return 0;
7759 }
78
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // functions for the Python type "Connection"
18 //-----------------------------------------------------------------------------
19 static void cxoConnection_free(cxoConnection*);
20 static PyObject *cxoConnection_new(PyTypeObject*, PyObject*, PyObject*);
21 static int cxoConnection_init(cxoConnection*, PyObject*, PyObject*);
22 static PyObject *cxoConnection_repr(cxoConnection*);
23 static PyObject *cxoConnection_close(cxoConnection*, PyObject*);
24 static PyObject *cxoConnection_commit(cxoConnection*, PyObject*);
25 static PyObject *cxoConnection_begin(cxoConnection*, PyObject*);
26 static PyObject *cxoConnection_prepare(cxoConnection*, PyObject*);
27 static PyObject *cxoConnection_rollback(cxoConnection*, PyObject*);
28 static PyObject *cxoConnection_newCursor(cxoConnection*, PyObject*, PyObject*);
29 static PyObject *cxoConnection_cancel(cxoConnection*, PyObject*);
30 static PyObject *cxoConnection_getCallTimeout(cxoConnection*, void*);
31 static PyObject *cxoConnection_getVersion(cxoConnection*, void*);
32 static PyObject *cxoConnection_getEncoding(cxoConnection*, void*);
33 static PyObject *cxoConnection_getNationalEncoding(cxoConnection*, void*);
34 static PyObject *cxoConnection_getMaxBytesPerCharacter(cxoConnection*, void*);
35 static PyObject *cxoConnection_contextManagerEnter(cxoConnection*, PyObject*);
36 static PyObject *cxoConnection_contextManagerExit(cxoConnection*, PyObject*);
37 static PyObject *cxoConnection_changePassword(cxoConnection*, PyObject*);
38 static PyObject *cxoConnection_getType(cxoConnection*, PyObject*);
39 static PyObject *cxoConnection_createLob(cxoConnection*, PyObject*);
40 static PyObject *cxoConnection_getStmtCacheSize(cxoConnection*, void*);
41 static PyObject *cxoConnection_newEnqueueOptions(cxoConnection*, PyObject*);
42 static PyObject *cxoConnection_newDequeueOptions(cxoConnection*, PyObject*);
43 static PyObject *cxoConnection_newMessageProperties(cxoConnection*, PyObject*);
44 static PyObject *cxoConnection_dequeue(cxoConnection*, PyObject*, PyObject*);
45 static PyObject *cxoConnection_enqueue(cxoConnection*, PyObject*, PyObject*);
46 static PyObject *cxoConnection_ping(cxoConnection*, PyObject*);
47 static PyObject *cxoConnection_shutdown(cxoConnection*, PyObject*, PyObject*);
48 static PyObject *cxoConnection_startup(cxoConnection*, PyObject*, PyObject*);
49 static PyObject *cxoConnection_subscribe(cxoConnection*, PyObject*, PyObject*);
50 static PyObject *cxoConnection_unsubscribe(cxoConnection*, PyObject*,
51 PyObject*);
52 static PyObject *cxoConnection_getSodaDatabase(cxoConnection*, PyObject*);
53 static PyObject *cxoConnection_getLTXID(cxoConnection*, void*);
54 static PyObject *cxoConnection_getHandle(cxoConnection*, void*);
55 static PyObject *cxoConnection_getCurrentSchema(cxoConnection*, void*);
56 static PyObject *cxoConnection_getEdition(cxoConnection*, void*);
57 static PyObject *cxoConnection_getExternalName(cxoConnection*, void*);
58 static PyObject *cxoConnection_getInternalName(cxoConnection*, void*);
59 static PyObject *cxoConnection_getException(cxoConnection*, void*);
60 static int cxoConnection_setCallTimeout(cxoConnection*, PyObject*, void*);
61 static int cxoConnection_setStmtCacheSize(cxoConnection*, PyObject*, void*);
62 static int cxoConnection_setAction(cxoConnection*, PyObject*, void*);
63 static int cxoConnection_setClientIdentifier(cxoConnection*, PyObject*, void*);
64 static int cxoConnection_setClientInfo(cxoConnection*, PyObject*, void*);
65 static int cxoConnection_setCurrentSchema(cxoConnection*, PyObject*, void*);
66 static int cxoConnection_setDbOp(cxoConnection*, PyObject*, void*);
67 static int cxoConnection_setExternalName(cxoConnection*, PyObject*, void*);
68 static int cxoConnection_setInternalName(cxoConnection*, PyObject*, void*);
69 static int cxoConnection_setModule(cxoConnection*, PyObject*, void*);
70
71
72 //-----------------------------------------------------------------------------
73 // declaration of methods for Python type "Connection"
74 //-----------------------------------------------------------------------------
75 static PyMethodDef cxoConnectionMethods[] = {
17 // structure used to help in establishing a connection
18 //-----------------------------------------------------------------------------
19 typedef struct {
20 const char *encoding;
21 const char *nencoding;
22 cxoBuffer userNameBuffer;
23 cxoBuffer passwordBuffer;
24 cxoBuffer newPasswordBuffer;
25 cxoBuffer dsnBuffer;
26 cxoBuffer connectionClassBuffer;
27 cxoBuffer editionBuffer;
28 cxoBuffer tagBuffer;
29 uint32_t numAppContext;
30 dpiAppContext *appContext;
31 cxoBuffer *ctxNamespaceBuffers;
32 cxoBuffer *ctxNameBuffers;
33 cxoBuffer *ctxValueBuffers;
34 dpiShardingKeyColumn *shardingKeyColumns;
35 cxoBuffer *shardingKeyBuffers;
36 uint32_t numShardingKeyColumns;
37 dpiShardingKeyColumn *superShardingKeyColumns;
38 uint32_t numSuperShardingKeyColumns;
39 cxoBuffer *superShardingKeyBuffers;
40 } cxoConnectionParams;
41
42
43 //-----------------------------------------------------------------------------
44 // cxoConnectionParams_initialize()
45 // Initialize the parameters to default values.
46 //-----------------------------------------------------------------------------
47 static void cxoConnectionParams_initialize(cxoConnectionParams *params)
48 {
49 cxoBuffer_init(&params->userNameBuffer);
50 cxoBuffer_init(&params->passwordBuffer);
51 cxoBuffer_init(&params->newPasswordBuffer);
52 cxoBuffer_init(&params->dsnBuffer);
53 cxoBuffer_init(&params->connectionClassBuffer);
54 cxoBuffer_init(&params->editionBuffer);
55 cxoBuffer_init(&params->tagBuffer);
56 params->numAppContext = 0;
57 params->appContext = NULL;
58 params->ctxNamespaceBuffers = NULL;
59 params->ctxNameBuffers = NULL;
60 params->ctxValueBuffers = NULL;
61 params->numShardingKeyColumns = 0;
62 params->shardingKeyColumns = NULL;
63 params->shardingKeyBuffers = NULL;
64 params->numSuperShardingKeyColumns = 0;
65 params->superShardingKeyColumns = NULL;
66 params->superShardingKeyBuffers = NULL;
67 }
68
69
70 //-----------------------------------------------------------------------------
71 // cxoConnectionParams_ProcessContext()
72 // Process context for the connection parameters. This validates that the
73 // context passed in is a list of 3-tuples (namespace, name, value) and
74 // populates the parametrs with buffers for each of these.
75 //-----------------------------------------------------------------------------
76 static int cxoConnectionParams_processContext(cxoConnectionParams *params,
77 PyObject *context)
78 {
79 uint32_t numEntries, i;
80 dpiAppContext *entry;
81 PyObject *entryObj;
82 size_t memorySize;
83
84 // validate context is a list with at least one entry in it
85 if (!context)
86 return 0;
87 if (!PyList_Check(context)) {
88 PyErr_SetString(PyExc_TypeError,
89 "appcontext should be a list of 3-tuples");
90 return -1;
91 }
92 numEntries = (uint32_t) PyList_GET_SIZE(context);
93 if (numEntries == 0)
94 return 0;
95
96 // allocate memory for the buffers used to communicate with DPI
97 params->appContext = PyMem_Malloc(numEntries * sizeof(dpiAppContext));
98 memorySize = numEntries * sizeof(cxoBuffer);
99 params->ctxNamespaceBuffers = PyMem_Malloc(memorySize);
100 params->ctxNameBuffers = PyMem_Malloc(memorySize);
101 params->ctxValueBuffers = PyMem_Malloc(memorySize);
102 if (!params->appContext || !params->ctxNamespaceBuffers ||
103 !params->ctxNameBuffers || !params->ctxValueBuffers) {
104 PyErr_NoMemory();
105 return -1;
106 }
107
108 // initialize buffers
109 for (i = 0; i < numEntries; i++) {
110 cxoBuffer_init(&params->ctxNamespaceBuffers[i]);
111 cxoBuffer_init(&params->ctxNameBuffers[i]);
112 cxoBuffer_init(&params->ctxValueBuffers[i]);
113 }
114 params->numAppContext = numEntries;
115
116 // process each entry
117 for (i = 0; i < numEntries; i++) {
118 entryObj = PyList_GET_ITEM(context, i);
119 if (!PyTuple_Check(entryObj) || PyTuple_GET_SIZE(entryObj) != 3) {
120 PyErr_SetString(PyExc_TypeError,
121 "appcontext should be a list of 3-tuples");
122 return -1;
123 }
124 if (cxoBuffer_fromObject(&params->ctxNamespaceBuffers[i],
125 PyTuple_GET_ITEM(entryObj, 0), params->encoding) < 0)
126 return -1;
127 if (cxoBuffer_fromObject(&params->ctxNameBuffers[i],
128 PyTuple_GET_ITEM(entryObj, 1), params->encoding) < 0)
129 return -1;
130 if (cxoBuffer_fromObject(&params->ctxValueBuffers[i],
131 PyTuple_GET_ITEM(entryObj, 2), params->encoding) < 0)
132 return -1;
133 entry = &params->appContext[i];
134 entry->namespaceName = params->ctxNamespaceBuffers[i].ptr;
135 entry->namespaceNameLength = params->ctxNamespaceBuffers[i].size;
136 entry->name = params->ctxNameBuffers[i].ptr;
137 entry->nameLength = params->ctxNameBuffers[i].size;
138 entry->value = params->ctxValueBuffers[i].ptr;
139 entry->valueLength = params->ctxValueBuffers[i].size;
140 }
141
142 return 0;
143 }
144
145
146 //-----------------------------------------------------------------------------
147 // cxoConnectionParams_processShardingKeyValue()
148 // Process a single sharding key value.
149 //-----------------------------------------------------------------------------
150 static int cxoConnectionParams_processShardingKeyValue(
151 cxoConnectionParams *params, PyObject *value,
152 dpiShardingKeyColumn *column, cxoBuffer *buffer)
153 {
154 dpiNativeTypeNum nativeTypeNum;
155 cxoTransformNum transformNum;
156
157 transformNum = cxoTransform_getNumFromPythonValue(value, 0);
158 if (cxoTransform_fromPython(transformNum, &nativeTypeNum, value,
159 &column->value, buffer, params->encoding, params->nencoding, NULL,
160 0) < 0)
161 return -1;
162 cxoTransform_getTypeInfo(transformNum, &column->oracleTypeNum,
163 &column->nativeTypeNum);
164 return 0;
165 }
166
167
168 //-----------------------------------------------------------------------------
169 // cxoConnectionParams_processShardingKey()
170 // Process either the sharding key or the super sharding key. A sharding key
171 // is expected to be a sequence of values. A null value or a sequence of size
172 // 0 is ignored.
173 //-----------------------------------------------------------------------------
174 static int cxoConnectionParams_processShardingKey(cxoConnectionParams *params,
175 PyObject *shardingKeyObj, int isSuperShardingKey)
176 {
177 dpiShardingKeyColumn *columns;
178 uint32_t i, numColumns;
179 cxoBuffer *buffers;
180 PyObject *value;
181
182 // validate sharding key
183 if (!shardingKeyObj || shardingKeyObj == Py_None)
184 return 0;
185 if (!PySequence_Check(shardingKeyObj)) {
186 PyErr_SetString(PyExc_TypeError, "expecting a sequence");
187 return -1;
188 }
189 numColumns = (uint32_t) PySequence_Size(shardingKeyObj);
190 if (numColumns == 0)
191 return 0;
192
193 // allocate memory for the sharding key values
194 columns = PyMem_Calloc(numColumns, sizeof(dpiShardingKeyColumn));
195 buffers = PyMem_Calloc(numColumns, sizeof(cxoBuffer));
196 if (!columns || !buffers) {
197 PyErr_NoMemory();
198 return -1;
199 }
200 if (isSuperShardingKey) {
201 params->superShardingKeyColumns = columns;
202 params->superShardingKeyBuffers = buffers;
203 params->numSuperShardingKeyColumns = numColumns;
204 } else {
205 params->shardingKeyColumns = columns;
206 params->shardingKeyBuffers = buffers;
207 params->numShardingKeyColumns = numColumns;
208 }
209
210 // process each value
211 for (i = 0; i < numColumns; i++) {
212 value = PySequence_GetItem(shardingKeyObj, i);
213 if (!value)
214 return -1;
215 if (cxoConnectionParams_processShardingKeyValue(params, value,
216 &columns[i], &buffers[i]) < 0)
217 return -1;
218 Py_DECREF(value);
219 }
220
221 return 0;
222 }
223
224
225 //-----------------------------------------------------------------------------
226 // cxoConnectionParams_finalize()
227 // Finalize the parameters, freeing any resources that were allocated. The
228 // return value is a convenience to the caller.
229 //-----------------------------------------------------------------------------
230 static int cxoConnectionParams_finalize(cxoConnectionParams *params)
231 {
232 uint32_t i;
233
234 cxoBuffer_clear(&params->userNameBuffer);
235 cxoBuffer_clear(&params->passwordBuffer);
236 cxoBuffer_clear(&params->newPasswordBuffer);
237 cxoBuffer_clear(&params->dsnBuffer);
238 cxoBuffer_clear(&params->connectionClassBuffer);
239 cxoBuffer_clear(&params->editionBuffer);
240 cxoBuffer_clear(&params->tagBuffer);
241 for (i = 0; i < params->numAppContext; i++) {
242 cxoBuffer_clear(&params->ctxNamespaceBuffers[i]);
243 cxoBuffer_clear(&params->ctxNameBuffers[i]);
244 cxoBuffer_clear(&params->ctxValueBuffers[i]);
245 }
246 params->numAppContext = 0;
247 if (params->appContext) {
248 PyMem_Free(params->appContext);
249 params->appContext = NULL;
250 }
251 if (params->ctxNamespaceBuffers) {
252 PyMem_Free(params->ctxNamespaceBuffers);
253 params->ctxNamespaceBuffers = NULL;
254 }
255 if (params->ctxNameBuffers) {
256 PyMem_Free(params->ctxNameBuffers);
257 params->ctxNameBuffers = NULL;
258 }
259 if (params->ctxValueBuffers) {
260 PyMem_Free(params->ctxValueBuffers);
261 params->ctxValueBuffers = NULL;
262 }
263 for (i = 0; i < params->numShardingKeyColumns; i++)
264 cxoBuffer_clear(&params->shardingKeyBuffers[i]);
265 if (params->shardingKeyColumns) {
266 PyMem_Free(params->shardingKeyColumns);
267 params->shardingKeyColumns = NULL;
268 }
269 if (params->shardingKeyBuffers) {
270 PyMem_Free(params->shardingKeyBuffers);
271 params->shardingKeyBuffers = NULL;
272 }
273 for (i = 0; i < params->numSuperShardingKeyColumns; i++)
274 cxoBuffer_clear(&params->superShardingKeyBuffers[i]);
275 if (params->superShardingKeyColumns) {
276 PyMem_Free(params->superShardingKeyColumns);
277 params->superShardingKeyColumns = NULL;
278 }
279 if (params->superShardingKeyBuffers) {
280 PyMem_Free(params->superShardingKeyBuffers);
281 params->superShardingKeyBuffers = NULL;
282 }
283 return -1;
284 }
285
286
287 //-----------------------------------------------------------------------------
288 // cxoConnection_getSodaFlags()
289 // Get the flags to use for SODA. This checks the autocommit flag and enables
290 // atomic commit if set to a true value. It also checks to ensure that the
291 // connection is valid.
292 //-----------------------------------------------------------------------------
293 int cxoConnection_getSodaFlags(cxoConnection *conn, uint32_t *flags)
294 {
295 if (cxoConnection_isConnected(conn) < 0)
296 return -1;
297 *flags = (conn->autocommit) ? DPI_SODA_FLAGS_ATOMIC_COMMIT :
298 DPI_SODA_FLAGS_DEFAULT;
299 return 0;
300 }
301
302
303 //-----------------------------------------------------------------------------
304 // cxoConnection_isConnected()
305 // Determines if the connection object is connected to the database. If not,
306 // a Python exception is raised.
307 //-----------------------------------------------------------------------------
308 int cxoConnection_isConnected(cxoConnection *conn)
309 {
310 if (!conn->handle) {
311 cxoError_raiseFromString(cxoInterfaceErrorException, "not connected");
312 return -1;
313 }
314 return 0;
315 }
316
317
318 //-----------------------------------------------------------------------------
319 // cxoConnection_getAttrText()
320 // Get the value of the attribute returned from the given function. The value
321 // is assumed to be a text value.
322 //-----------------------------------------------------------------------------
323 static PyObject *cxoConnection_getAttrText(cxoConnection *conn,
324 int (*func)(dpiConn *conn, const char **value, uint32_t *valueLength))
325 {
326 uint32_t valueLength;
327 const char *value;
328
329 if (cxoConnection_isConnected(conn) < 0)
330 return NULL;
331 if ((*func)(conn->handle, &value, &valueLength) < 0)
332 return cxoError_raiseAndReturnNull();
333 if (!value)
334 Py_RETURN_NONE;
335 return PyUnicode_Decode(value, valueLength, conn->encodingInfo.encoding,
336 NULL);
337 }
338
339
340 //-----------------------------------------------------------------------------
341 // cxoConnection_setAttrText()
342 // Set the value of the attribute using the given function. The value is
343 // assumed to be a text value.
344 //-----------------------------------------------------------------------------
345 static int cxoConnection_setAttrText(cxoConnection *conn, PyObject *value,
346 int (*func)(dpiConn *conn, const char *value, uint32_t valueLength))
347 {
348 cxoBuffer buffer;
349 int status;
350
351 if (cxoConnection_isConnected(conn) < 0)
352 return -1;
353 if (cxoBuffer_fromObject(&buffer, value, conn->encodingInfo.encoding))
354 return -1;
355 status = (*func)(conn->handle, buffer.ptr, buffer.size);
356 cxoBuffer_clear(&buffer);
357 if (status < 0)
358 return cxoError_raiseAndReturnInt();
359 return 0;
360 }
361
362
363 //-----------------------------------------------------------------------------
364 // cxoConnection_changePassword()
365 // Change the password for the given connection.
366 //-----------------------------------------------------------------------------
367 static PyObject *cxoConnection_changePassword(cxoConnection *conn,
368 PyObject *args)
369 {
370 cxoBuffer usernameBuffer, oldPasswordBuffer, newPasswordBuffer;
371 PyObject *oldPasswordObj, *newPasswordObj;
372 int status;
373
374 // parse the arguments
375 if (cxoConnection_isConnected(conn) < 0)
376 return NULL;
377 if (!PyArg_ParseTuple(args, "OO", &oldPasswordObj, &newPasswordObj))
378 return NULL;
379
380 // populate buffers
381 cxoBuffer_init(&usernameBuffer);
382 cxoBuffer_init(&oldPasswordBuffer);
383 cxoBuffer_init(&newPasswordBuffer);
384 if (cxoBuffer_fromObject(&usernameBuffer, conn->username,
385 conn->encodingInfo.encoding) < 0 ||
386 cxoBuffer_fromObject(&oldPasswordBuffer, oldPasswordObj,
387 conn->encodingInfo.encoding) < 0 ||
388 cxoBuffer_fromObject(&newPasswordBuffer, newPasswordObj,
389 conn->encodingInfo.encoding) < 0) {
390 cxoBuffer_clear(&usernameBuffer);
391 cxoBuffer_clear(&oldPasswordBuffer);
392 cxoBuffer_clear(&newPasswordBuffer);
393 return NULL;
394 }
395
396 // change the password
397 Py_BEGIN_ALLOW_THREADS
398 status = dpiConn_changePassword(conn->handle, usernameBuffer.ptr,
399 usernameBuffer.size, oldPasswordBuffer.ptr, oldPasswordBuffer.size,
400 newPasswordBuffer.ptr, newPasswordBuffer.size);
401 Py_END_ALLOW_THREADS
402 cxoBuffer_clear(&usernameBuffer);
403 cxoBuffer_clear(&oldPasswordBuffer);
404 cxoBuffer_clear(&newPasswordBuffer);
405 if (status < 0)
406 return cxoError_raiseAndReturnNull();
407
408 Py_RETURN_NONE;
409 }
410
411
412 //-----------------------------------------------------------------------------
413 // cxoConnection_new()
414 // Create a new connection object and return it.
415 //-----------------------------------------------------------------------------
416 static PyObject *cxoConnection_new(PyTypeObject *type, PyObject *args,
417 PyObject *keywordArgs)
418 {
419 return type->tp_alloc(type, 0);
420 }
421
422
423 //-----------------------------------------------------------------------------
424 // cxoConnection_splitComponent()
425 // Split the component out of the source if the split string is found and
426 // return the "before" and "after" parts.
427 //-----------------------------------------------------------------------------
428 static int cxoConnection_splitComponent(PyObject *sourceObj,
429 const char *splitString, const char *methodName,
430 PyObject **beforePartObj, PyObject **afterPartObj)
431 {
432 Py_ssize_t size, pos;
433 PyObject *posObj;
434
435 posObj = PyObject_CallMethod(sourceObj, methodName, "s", splitString);
436 if (!posObj)
437 return -1;
438 pos = PyLong_AsLong(posObj);
439 Py_DECREF(posObj);
440 if (PyErr_Occurred())
441 return -1;
442 if (pos < 0) {
443 *beforePartObj = *afterPartObj = NULL;
444 } else {
445 size = PySequence_Size(sourceObj);
446 if (PyErr_Occurred())
447 return -1;
448 *afterPartObj = PySequence_GetSlice(sourceObj, pos + 1, size);
449 if (!*afterPartObj)
450 return -1;
451 *beforePartObj = PySequence_GetSlice(sourceObj, 0, pos);
452 if (!*beforePartObj) {
453 Py_DECREF(*afterPartObj);
454 *afterPartObj = NULL;
455 return -1;
456 }
457 }
458 return 0;
459 }
460
461
462 //-----------------------------------------------------------------------------
463 // cxoConnection_init()
464 // Initialize the connection members.
465 //-----------------------------------------------------------------------------
466 static int cxoConnection_init(cxoConnection *conn, PyObject *args,
467 PyObject *keywordArgs)
468 {
469 PyObject *tagObj, *matchAnyTagObj, *threadedObj, *eventsObj, *contextObj;
470 PyObject *usernameObj, *passwordObj, *dsnObj, *cclassObj, *editionObj;
471 PyObject *shardingKeyObj, *superShardingKeyObj, *tempObj;
472 int status, temp, invokeSessionCallback;
473 PyObject *beforePartObj, *afterPartObj;
474 dpiCommonCreateParams dpiCommonParams;
475 dpiConnCreateParams dpiCreateParams;
476 unsigned long long externalHandle;
477 cxoConnectionParams params;
478 PyObject *newPasswordObj;
479 cxoSessionPool *pool;
480
481 // define keyword arguments
482 static char *keywordList[] = { "user", "password", "dsn", "mode",
483 "handle", "pool", "threaded", "events", "cclass", "purity",
484 "newpassword", "encoding", "nencoding", "edition", "appcontext",
485 "tag", "matchanytag", "shardingkey", "supershardingkey", NULL };
486
487 // parse arguments
488 pool = NULL;
489 tagObj = Py_None;
490 externalHandle = 0;
491 passwordObj = dsnObj = cclassObj = editionObj = NULL;
492 threadedObj = eventsObj = newPasswordObj = usernameObj = NULL;
493 matchAnyTagObj = contextObj = shardingKeyObj = superShardingKeyObj = NULL;
494 if (cxoUtils_initializeDPI(NULL) < 0)
495 return -1;
496 if (dpiContext_initCommonCreateParams(cxoDpiContext, &dpiCommonParams) < 0)
497 return cxoError_raiseAndReturnInt();
498 if (dpiContext_initConnCreateParams(cxoDpiContext, &dpiCreateParams) < 0)
499 return cxoError_raiseAndReturnInt();
500 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs,
501 "|OOOiKO!OOOiOssOOOOOO", keywordList, &usernameObj, &passwordObj,
502 &dsnObj, &dpiCreateParams.authMode, &externalHandle,
503 &cxoPyTypeSessionPool, &pool, &threadedObj, &eventsObj, &cclassObj,
504 &dpiCreateParams.purity, &newPasswordObj,
505 &dpiCommonParams.encoding, &dpiCommonParams.nencoding, &editionObj,
506 &contextObj, &tagObj, &matchAnyTagObj, &shardingKeyObj,
507 &superShardingKeyObj))
508 return -1;
509 dpiCreateParams.externalHandle = (void*) externalHandle;
510 if (cxoUtils_getBooleanValue(threadedObj, 0, &temp) < 0)
511 return -1;
512 if (temp)
513 dpiCommonParams.createMode |= DPI_MODE_CREATE_THREADED;
514 if (cxoUtils_getBooleanValue(eventsObj, 0, &temp) < 0)
515 return -1;
516 if (temp)
517 dpiCommonParams.createMode |= DPI_MODE_CREATE_EVENTS;
518 if (cxoUtils_getBooleanValue(matchAnyTagObj, 0,
519 &dpiCreateParams.matchAnyTag) < 0)
520 return -1;
521
522 // keep a copy of the user name and connect string (DSN)
523 Py_XINCREF(usernameObj);
524 conn->username = usernameObj;
525 Py_XINCREF(dsnObj);
526 conn->dsn = dsnObj;
527 Py_XINCREF(passwordObj);
528
529 // perform some parsing, if no password and DSN are provided but the user
530 // name is provided
531 if (conn->username && !passwordObj && !dsnObj) {
532 if (cxoConnection_splitComponent(conn->username, "/", "find",
533 &beforePartObj, &afterPartObj) < 0)
534 return -1;
535 if (beforePartObj) {
536 Py_DECREF(conn->username);
537 conn->username = beforePartObj;
538 passwordObj = afterPartObj;
539 if (cxoConnection_splitComponent(passwordObj, "@", "rfind",
540 &beforePartObj, &afterPartObj) < 0)
541 return -1;
542 if (beforePartObj) {
543 Py_DECREF(passwordObj);
544 passwordObj = beforePartObj;
545 conn->dsn = afterPartObj;
546 }
547 }
548 }
549
550 // setup parameters
551 cxoConnectionParams_initialize(&params);
552 if (pool) {
553 dpiCreateParams.pool = pool->handle;
554 params.encoding = pool->encodingInfo.encoding;
555 params.nencoding = pool->encodingInfo.nencoding;
556 } else {
557 params.encoding =
558 cxoUtils_getAdjustedEncoding(dpiCommonParams.encoding);
559 params.nencoding =
560 cxoUtils_getAdjustedEncoding(dpiCommonParams.nencoding);
561 }
562 if (cxoConnectionParams_processContext(&params, contextObj) < 0)
563 return cxoConnectionParams_finalize(&params);
564 if (cxoConnectionParams_processShardingKey(&params, shardingKeyObj, 0) < 0)
565 return cxoConnectionParams_finalize(&params);
566 if (cxoConnectionParams_processShardingKey(&params, superShardingKeyObj,
567 1) < 0)
568 return cxoConnectionParams_finalize(&params);
569 if (cxoBuffer_fromObject(&params.userNameBuffer, conn->username,
570 params.encoding) < 0 ||
571 cxoBuffer_fromObject(&params.passwordBuffer, passwordObj,
572 params.encoding) < 0 ||
573 cxoBuffer_fromObject(&params.dsnBuffer, conn->dsn,
574 params.encoding) < 0 ||
575 cxoBuffer_fromObject(&params.connectionClassBuffer, cclassObj,
576 params.encoding) < 0 ||
577 cxoBuffer_fromObject(&params.newPasswordBuffer, newPasswordObj,
578 params.encoding) < 0 ||
579 cxoBuffer_fromObject(&params.editionBuffer, editionObj,
580 params.encoding) < 0 ||
581 cxoBuffer_fromObject(&params.tagBuffer, tagObj,
582 params.encoding) < 0) {
583 Py_XDECREF(passwordObj);
584 return cxoConnectionParams_finalize(&params);
585 }
586 Py_XDECREF(passwordObj);
587 if (params.userNameBuffer.size == 0 && params.passwordBuffer.size == 0)
588 dpiCreateParams.externalAuth = 1;
589 dpiCreateParams.connectionClass = params.connectionClassBuffer.ptr;
590 dpiCreateParams.connectionClassLength = params.connectionClassBuffer.size;
591 dpiCreateParams.newPassword = params.newPasswordBuffer.ptr;
592 dpiCreateParams.newPasswordLength = params.newPasswordBuffer.size;
593 dpiCommonParams.edition = params.editionBuffer.ptr;
594 dpiCommonParams.editionLength = params.editionBuffer.size;
595 dpiCreateParams.tag = params.tagBuffer.ptr;
596 dpiCreateParams.tagLength = params.tagBuffer.size;
597 dpiCreateParams.appContext = params.appContext;
598 dpiCreateParams.numAppContext = params.numAppContext;
599 dpiCreateParams.shardingKeyColumns = params.shardingKeyColumns;
600 dpiCreateParams.numShardingKeyColumns = params.numShardingKeyColumns;
601 dpiCreateParams.superShardingKeyColumns = params.superShardingKeyColumns;
602 dpiCreateParams.numSuperShardingKeyColumns =
603 params.numSuperShardingKeyColumns;
604 if (pool && !pool->homogeneous && pool->username && conn->username) {
605 temp = PyObject_RichCompareBool(conn->username, pool->username, Py_EQ);
606 if (temp < 0)
607 return cxoConnectionParams_finalize(&params);
608 if (temp)
609 params.userNameBuffer.size = 0;
610 }
611
612 // create connection
613 Py_BEGIN_ALLOW_THREADS
614 status = dpiConn_create(cxoDpiContext, params.userNameBuffer.ptr,
615 params.userNameBuffer.size, params.passwordBuffer.ptr,
616 params.passwordBuffer.size, params.dsnBuffer.ptr,
617 params.dsnBuffer.size, &dpiCommonParams, &dpiCreateParams,
618 &conn->handle);
619 Py_END_ALLOW_THREADS
620 if (status < 0) {
621 cxoConnectionParams_finalize(&params);
622 return cxoError_raiseAndReturnInt();
623 }
624
625 // determine if session callback should be invoked; this takes place if
626 // the connection is newly created by the pool or if the requested tag
627 // does not match the actal tag
628 invokeSessionCallback = 0;
629 if (dpiCreateParams.outNewSession ||
630 dpiCreateParams.outTagLength != params.tagBuffer.size ||
631 (dpiCreateParams.outTagLength > 0 &&
632 strncmp(dpiCreateParams.outTag, params.tagBuffer.ptr,
633 dpiCreateParams.outTagLength) != 0))
634 invokeSessionCallback = 1;
635 cxoConnectionParams_finalize(&params);
636
637 // determine encodings to use
638 if (pool)
639 conn->encodingInfo = pool->encodingInfo;
640 else {
641 if (dpiConn_getEncodingInfo(conn->handle, &conn->encodingInfo) < 0)
642 return cxoError_raiseAndReturnInt();
643 conn->encodingInfo.encoding =
644 cxoUtils_getAdjustedEncoding(conn->encodingInfo.encoding);
645 conn->encodingInfo.nencoding =
646 cxoUtils_getAdjustedEncoding(conn->encodingInfo.nencoding);
647 }
648
649 // set tag property
650 if (dpiCreateParams.outTagLength > 0) {
651 conn->tag = PyUnicode_Decode(dpiCreateParams.outTag,
652 dpiCreateParams.outTagLength, conn->encodingInfo.encoding,
653 NULL);
654 if (!conn->tag)
655 return -1;
656 }
657
658 // invoke the session callback if applicable
659 if (invokeSessionCallback && pool && pool->sessionCallback &&
660 PyCallable_Check(pool->sessionCallback)) {
661 tempObj = PyObject_CallFunctionObjArgs(pool->sessionCallback,
662 (PyObject*) conn, tagObj, NULL);
663 if (!tempObj)
664 return -1;
665 Py_DECREF(tempObj);
666 }
667
668 return 0;
669 }
670
671
672 //-----------------------------------------------------------------------------
673 // cxoConnection_free()
674 // Deallocate the connection, disconnecting from the database if necessary.
675 //-----------------------------------------------------------------------------
676 static void cxoConnection_free(cxoConnection *conn)
677 {
678 if (conn->handle) {
679 Py_BEGIN_ALLOW_THREADS
680 dpiConn_release(conn->handle);
681 Py_END_ALLOW_THREADS
682 conn->handle = NULL;
683 }
684 Py_CLEAR(conn->sessionPool);
685 Py_CLEAR(conn->username);
686 Py_CLEAR(conn->dsn);
687 Py_CLEAR(conn->version);
688 Py_CLEAR(conn->inputTypeHandler);
689 Py_CLEAR(conn->outputTypeHandler);
690 Py_CLEAR(conn->tag);
691 Py_TYPE(conn)->tp_free((PyObject*) conn);
692 }
693
694
695 //-----------------------------------------------------------------------------
696 // cxoConnection_repr()
697 // Return a string representation of the connection.
698 //-----------------------------------------------------------------------------
699 static PyObject *cxoConnection_repr(cxoConnection *connection)
700 {
701 PyObject *module, *name, *result;
702
703 if (cxoUtils_getModuleAndName(Py_TYPE(connection), &module, &name) < 0)
704 return NULL;
705 if (connection->username && connection->username != Py_None &&
706 connection->dsn && connection->dsn != Py_None) {
707 result = cxoUtils_formatString("<%s.%s to %s@%s>",
708 PyTuple_Pack(4, module, name, connection->username,
709 connection->dsn));
710 } else if (connection->username && connection->username != Py_None) {
711 result = cxoUtils_formatString("<%s.%s to user %s@local>",
712 PyTuple_Pack(3, module, name, connection->username));
713 } else {
714 result = cxoUtils_formatString("<%s.%s to externally identified user>",
715 PyTuple_Pack(2, module, name));
716 }
717 Py_DECREF(module);
718 Py_DECREF(name);
719 return result;
720 }
721
722
723 //-----------------------------------------------------------------------------
724 // cxoConnection_getStmtCacheSize()
725 // Return the Oracle statement cache size.
726 //-----------------------------------------------------------------------------
727 static PyObject *cxoConnection_getStmtCacheSize(cxoConnection* conn, void* arg)
728 {
729 uint32_t cacheSize;
730
731 if (cxoConnection_isConnected(conn) < 0)
732 return NULL;
733 if (dpiConn_getStmtCacheSize(conn->handle, &cacheSize) < 0)
734 return cxoError_raiseAndReturnNull();
735 return PyLong_FromLong(cacheSize);
736 }
737
738
739 //-----------------------------------------------------------------------------
740 // cxoConnection_setStmtCacheSize()
741 // Set the Oracle statement cache size.
742 //-----------------------------------------------------------------------------
743 static int cxoConnection_setStmtCacheSize(cxoConnection* conn, PyObject *value,
744 void* arg)
745 {
746 uint32_t cacheSize;
747
748 if (cxoConnection_isConnected(conn) < 0)
749 return -1;
750 if (!PyLong_Check(value)) {
751 PyErr_SetString(PyExc_TypeError, "value must be an integer");
752 return -1;
753 }
754 cacheSize = (uint32_t) PyLong_AsLong(value);
755 if (dpiConn_setStmtCacheSize(conn->handle, cacheSize) < 0)
756 return cxoError_raiseAndReturnInt();
757 return 0;
758 }
759
760
761 //-----------------------------------------------------------------------------
762 // cxoConnection_getCallTimeout()
763 // Return the call timeout (in milliseconds) for round-trips performed with
764 // this connection.
765 //-----------------------------------------------------------------------------
766 static PyObject *cxoConnection_getCallTimeout(cxoConnection* conn, void* arg)
767 {
768 uint32_t callTimeout;
769
770 if (cxoConnection_isConnected(conn) < 0)
771 return NULL;
772 if (dpiConn_getCallTimeout(conn->handle, &callTimeout) < 0)
773 return cxoError_raiseAndReturnNull();
774 return PyLong_FromLong(callTimeout);
775 }
776
777
778 //-----------------------------------------------------------------------------
779 // cxoConnection_setCallTimeout()
780 // Set the call timeout (in milliseconds) for round-trips performed with this
781 // connection.
782 //-----------------------------------------------------------------------------
783 static int cxoConnection_setCallTimeout(cxoConnection* conn, PyObject *value,
784 void* arg)
785 {
786 uint32_t callTimeout;
787
788 if (cxoConnection_isConnected(conn) < 0)
789 return -1;
790 callTimeout = (uint32_t) PyLong_AsLong(value);
791 if (PyErr_Occurred())
792 return -1;
793 if (dpiConn_setCallTimeout(conn->handle, callTimeout) < 0)
794 return cxoError_raiseAndReturnInt();
795 return 0;
796 }
797
798
799 //-----------------------------------------------------------------------------
800 // cxoConnection_getType()
801 // Return a type object given its name.
802 //-----------------------------------------------------------------------------
803 static PyObject *cxoConnection_getType(cxoConnection *conn, PyObject *nameObj)
804 {
805 if (cxoConnection_isConnected(conn) < 0)
806 return NULL;
807 return (PyObject*) cxoObjectType_newByName(conn, nameObj);
808 }
809
810
811 //-----------------------------------------------------------------------------
812 // cxoConnection_createLob()
813 // Create a new temporary LOB and return it.
814 //-----------------------------------------------------------------------------
815 static PyObject *cxoConnection_createLob(cxoConnection *conn,
816 PyObject *lobType)
817 {
818 cxoDbType *dbType;
819 dpiLob *handle;
820 PyObject *lob;
821
822 // verify connection is open
823 if (cxoConnection_isConnected(conn) < 0)
824 return NULL;
825
826 // verify the LOB type
827 if (lobType != (PyObject*) cxoDbTypeClob &&
828 lobType != (PyObject*) cxoDbTypeBlob &&
829 lobType != (PyObject*) cxoDbTypeNclob) {
830 PyErr_SetString(PyExc_TypeError,
831 "parameter should be one of cx_Oracle.DB_TYPE_CLOB, "
832 "cx_Oracle.DB_TYPE_BLOB or cx_Oracle.DB_TYPE_NCLOB");
833 return NULL;
834 }
835
836 // create a temporary LOB
837 dbType = (cxoDbType*) lobType;
838 if (dpiConn_newTempLob(conn->handle, dbType->num, &handle) < 0)
839 return cxoError_raiseAndReturnNull();
840 lob = cxoLob_new(conn, dbType, handle);
841 if (!lob)
842 dpiLob_release(handle);
843 return lob;
844 }
845
846
847 //-----------------------------------------------------------------------------
848 // cxoConnection_getVersion()
849 // Retrieve the version of the database and return it. Note that this
850 // function also places the result in the associated dictionary so it is only
851 // calculated once.
852 //-----------------------------------------------------------------------------
853 static PyObject *cxoConnection_getVersion(cxoConnection *conn, void *unused)
854 {
855 dpiVersionInfo versionInfo;
856 char buffer[25];
857 int status, len;
858
859 if (cxoConnection_isConnected(conn) < 0)
860 return NULL;
861 Py_BEGIN_ALLOW_THREADS
862 status = dpiConn_getServerVersion(conn->handle, NULL, NULL, &versionInfo);
863 Py_END_ALLOW_THREADS
864 if (status < 0)
865 return cxoError_raiseAndReturnNull();
866 len = snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.%d",
867 versionInfo.versionNum, versionInfo.releaseNum,
868 versionInfo.updateNum, versionInfo.portReleaseNum,
869 versionInfo.portUpdateNum);
870 return PyUnicode_DecodeASCII(buffer, len, NULL);
871 }
872
873
874 //-----------------------------------------------------------------------------
875 // cxoConnection_getEncoding()
876 // Return the encoding associated with the environment of the connection.
877 //-----------------------------------------------------------------------------
878 static PyObject *cxoConnection_getEncoding(cxoConnection *conn, void *unused)
879 {
880 return PyUnicode_DecodeASCII(conn->encodingInfo.encoding,
881 strlen(conn->encodingInfo.encoding), NULL);
882 }
883
884
885 //-----------------------------------------------------------------------------
886 // cxoConnection_getLTXID()
887 // Return the logical transaction id used with Transaction Guard.
888 //-----------------------------------------------------------------------------
889 static PyObject *cxoConnection_getLTXID(cxoConnection *conn, void *unused)
890 {
891 uint32_t ltxidLength;
892 const char *ltxid;
893
894 if (cxoConnection_isConnected(conn) < 0)
895 return NULL;
896 if (dpiConn_getLTXID(conn->handle, &ltxid, &ltxidLength) < 0)
897 return cxoError_raiseAndReturnNull();
898 return PyBytes_FromStringAndSize(ltxid, ltxidLength);
899 }
900
901
902 //-----------------------------------------------------------------------------
903 // cxoConnection_getHandle()
904 // Return the OCI handle used by the connection.
905 //-----------------------------------------------------------------------------
906 static PyObject *cxoConnection_getHandle(cxoConnection *conn, void *unused)
907 {
908 void *handle;
909
910 if (cxoConnection_isConnected(conn) < 0)
911 return NULL;
912 if (dpiConn_getHandle(conn->handle, &handle) < 0)
913 return cxoError_raiseAndReturnNull();
914 return PyLong_FromUnsignedLongLong((unsigned long long) handle);
915 }
916
917
918 //-----------------------------------------------------------------------------
919 // cxoConnection_getNationalEncoding()
920 // Return the national encoding associated with the environment of the
921 // connection.
922 //-----------------------------------------------------------------------------
923 static PyObject *cxoConnection_getNationalEncoding(cxoConnection *conn,
924 void *unused)
925 {
926 return PyUnicode_DecodeASCII(conn->encodingInfo.nencoding,
927 strlen(conn->encodingInfo.nencoding), NULL);
928 }
929
930
931 //-----------------------------------------------------------------------------
932 // cxoConnection_getMaxBytesPerCharacter()
933 // Return the maximum number of bytes per character.
934 //-----------------------------------------------------------------------------
935 static PyObject *cxoConnection_getMaxBytesPerCharacter(cxoConnection *conn,
936 void *unused)
937 {
938 return PyLong_FromLong(conn->encodingInfo.maxBytesPerCharacter);
939 }
940
941
942 //-----------------------------------------------------------------------------
943 // cxoConnection_close()
944 // Close the connection, disconnecting from the database.
945 //-----------------------------------------------------------------------------
946 static PyObject *cxoConnection_close(cxoConnection *conn, PyObject *args)
947 {
948 cxoBuffer tagBuffer;
949 uint32_t mode;
950 int status;
951
952 if (cxoConnection_isConnected(conn) < 0)
953 return NULL;
954 if (cxoBuffer_fromObject(&tagBuffer, conn->tag,
955 conn->encodingInfo.encoding) < 0)
956 return NULL;
957 mode = DPI_MODE_CONN_CLOSE_DEFAULT;
958 if (conn->tag && conn->tag != Py_None)
959 mode |= DPI_MODE_CONN_CLOSE_RETAG;
960 Py_BEGIN_ALLOW_THREADS
961 status = dpiConn_close(conn->handle, mode, (char*) tagBuffer.ptr,
962 tagBuffer.size);
963 if (status == DPI_SUCCESS)
964 dpiConn_release(conn->handle);
965 Py_END_ALLOW_THREADS
966 cxoBuffer_clear(&tagBuffer);
967 if (status < 0)
968 return cxoError_raiseAndReturnNull();
969 conn->handle = NULL;
970
971 Py_RETURN_NONE;
972 }
973
974
975 //-----------------------------------------------------------------------------
976 // cxoConnection_commit()
977 // Commit the transaction on the connection.
978 //-----------------------------------------------------------------------------
979 static PyObject *cxoConnection_commit(cxoConnection *conn, PyObject *args)
980 {
981 int status;
982
983 if (cxoConnection_isConnected(conn) < 0)
984 return NULL;
985 Py_BEGIN_ALLOW_THREADS
986 status = dpiConn_commit(conn->handle);
987 Py_END_ALLOW_THREADS
988 if (status < 0)
989 return cxoError_raiseAndReturnNull();
990
991 Py_RETURN_NONE;
992 }
993
994
995 //-----------------------------------------------------------------------------
996 // cxoConnection_begin()
997 // Begin a new transaction on the connection.
998 //-----------------------------------------------------------------------------
999 static PyObject *cxoConnection_begin(cxoConnection *conn, PyObject *args)
1000 {
1001 Py_ssize_t transactionIdLength, branchIdLength;
1002 const char *transactionId, *branchId;
1003 int formatId, status;
1004
1005 // parse the arguments
1006 formatId = -1;
1007 transactionId = branchId = NULL;
1008 transactionIdLength = branchIdLength = 0;
1009 if (!PyArg_ParseTuple(args, "|is#s#", &formatId, &transactionId,
1010 &transactionIdLength, &branchId, &branchIdLength))
1011 return NULL;
1012
1013 // make sure we are actually connected
1014 if (cxoConnection_isConnected(conn) < 0)
1015 return NULL;
1016
1017 // begin the distributed transaction
1018 Py_BEGIN_ALLOW_THREADS
1019 status = dpiConn_beginDistribTrans(conn->handle, formatId, transactionId,
1020 transactionIdLength, branchId, branchIdLength);
1021 Py_END_ALLOW_THREADS
1022 if (status < 0)
1023 return cxoError_raiseAndReturnNull();
1024
1025 Py_RETURN_NONE;
1026 }
1027
1028
1029 //-----------------------------------------------------------------------------
1030 // cxoConnection_prepare()
1031 // Commit the transaction on the connection.
1032 //-----------------------------------------------------------------------------
1033 static PyObject *cxoConnection_prepare(cxoConnection *conn, PyObject *args)
1034 {
1035 int status, commitNeeded;
1036
1037 // make sure we are actually connected
1038 if (cxoConnection_isConnected(conn) < 0)
1039 return NULL;
1040
1041 // perform the prepare
1042 Py_BEGIN_ALLOW_THREADS
1043 status = dpiConn_prepareDistribTrans(conn->handle, &commitNeeded);
1044 Py_END_ALLOW_THREADS
1045 if (status < 0)
1046 return cxoError_raiseAndReturnNull();
1047
1048 // return whether a commit is needed in order to allow for avoiding the
1049 // call to commit() which will fail with ORA-24756 (transaction does not
1050 // exist)
1051 return PyBool_FromLong(commitNeeded);
1052 }
1053
1054
1055 //-----------------------------------------------------------------------------
1056 // cxoConnection_rollback()
1057 // Rollback the transaction on the connection.
1058 //-----------------------------------------------------------------------------
1059 static PyObject *cxoConnection_rollback(cxoConnection *conn, PyObject *args)
1060 {
1061 int status;
1062
1063 if (cxoConnection_isConnected(conn) < 0)
1064 return NULL;
1065 Py_BEGIN_ALLOW_THREADS
1066 status = dpiConn_rollback(conn->handle);
1067 Py_END_ALLOW_THREADS
1068 if (status < 0)
1069 return cxoError_raiseAndReturnNull();
1070
1071 Py_RETURN_NONE;
1072 }
1073
1074
1075 //-----------------------------------------------------------------------------
1076 // cxoConnection_newCursor()
1077 // Create a new cursor (statement) referencing the connection.
1078 //-----------------------------------------------------------------------------
1079 static PyObject *cxoConnection_newCursor(cxoConnection *conn, PyObject *args,
1080 PyObject *keywordArgs)
1081 {
1082 PyObject *createArgs, *result, *arg;
1083 Py_ssize_t numArgs = 0, i;
1084
1085 if (cxoConnection_isConnected(conn) < 0)
1086 return NULL;
1087 if (args)
1088 numArgs = PyTuple_GET_SIZE(args);
1089 createArgs = PyTuple_New(1 + numArgs);
1090 if (!createArgs)
1091 return NULL;
1092 Py_INCREF(conn);
1093 PyTuple_SET_ITEM(createArgs, 0, (PyObject*) conn);
1094 for (i = 0; i < numArgs; i++) {
1095 arg = PyTuple_GET_ITEM(args, i);
1096 Py_INCREF(arg);
1097 PyTuple_SET_ITEM(createArgs, i + 1, arg);
1098 }
1099 result = PyObject_Call( (PyObject*) &cxoPyTypeCursor, createArgs,
1100 keywordArgs);
1101 Py_DECREF(createArgs);
1102 return result;
1103 }
1104
1105
1106 //-----------------------------------------------------------------------------
1107 // cxoConnection_cancel()
1108 // Cause Oracle to issue an immediate (asynchronous) abort of any currently
1109 // executing statement.
1110 //-----------------------------------------------------------------------------
1111 static PyObject *cxoConnection_cancel(cxoConnection *conn, PyObject *args)
1112 {
1113 if (cxoConnection_isConnected(conn) < 0)
1114 return NULL;
1115 if (dpiConn_breakExecution(conn->handle) < 0)
1116 return cxoError_raiseAndReturnNull();
1117
1118 Py_RETURN_NONE;
1119 }
1120
1121
1122 //-----------------------------------------------------------------------------
1123 // cxoConnection_newEnqueueOptions()
1124 // Creates a new enqueue options object and returns it.
1125 //-----------------------------------------------------------------------------
1126 static PyObject *cxoConnection_newEnqueueOptions(cxoConnection *conn,
1127 PyObject *args)
1128 {
1129 if (cxoConnection_isConnected(conn) < 0)
1130 return NULL;
1131 return (PyObject*) cxoEnqOptions_new(conn, NULL);
1132 }
1133
1134
1135 //-----------------------------------------------------------------------------
1136 // cxoConnection_newDequeueOptions()
1137 // Creates a new dequeue options object and returns it.
1138 //-----------------------------------------------------------------------------
1139 static PyObject *cxoConnection_newDequeueOptions(cxoConnection *conn,
1140 PyObject *args)
1141 {
1142 if (cxoConnection_isConnected(conn) < 0)
1143 return NULL;
1144 return (PyObject*) cxoDeqOptions_new(conn, NULL);
1145 }
1146
1147
1148 //-----------------------------------------------------------------------------
1149 // cxoConnection_newMessageProperties()
1150 // Creates a new message properties object and returns it.
1151 //-----------------------------------------------------------------------------
1152 static PyObject *cxoConnection_newMessageProperties(cxoConnection *conn,
1153 PyObject *args, PyObject *keywordArgs)
1154 {
1155 static char *keywordList[] = { "payload", "correlation", "delay",
1156 "exceptionq", "expiration", "priority", NULL };
1157 PyObject *payloadObj, *correlationObj, *exceptionQObj;
1158 int delay, expiration, priority, status;
1159 cxoMsgProps *props;
1160 cxoBuffer buffer;
1161
1162 // parse arguments
1163 expiration = -1;
1164 delay = priority = 0;
1165 payloadObj = correlationObj = exceptionQObj = NULL;
1166 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|OOiOii", keywordList,
1167 &payloadObj, &correlationObj, &delay, &exceptionQObj, &expiration,
1168 &priority))
1169 return NULL;
1170 if (cxoConnection_isConnected(conn) < 0)
1171 return NULL;
1172
1173 // create new message properties object
1174 props = cxoMsgProps_new(conn, NULL);
1175 if (!props)
1176 return NULL;
1177
1178 // set payload, if applicable
1179 if (payloadObj) {
1180 Py_INCREF(payloadObj);
1181 props->payload = payloadObj;
1182 }
1183
1184 // set correlation, if applicable
1185 if (correlationObj) {
1186 if (cxoBuffer_fromObject(&buffer, correlationObj,
1187 props->encoding) < 0) {
1188 Py_DECREF(props);
1189 return NULL;
1190 }
1191 status = dpiMsgProps_setCorrelation(props->handle, buffer.ptr,
1192 buffer.size);
1193 cxoBuffer_clear(&buffer);
1194 if (status < 0) {
1195 cxoError_raiseAndReturnNull();
1196 Py_DECREF(props);
1197 return NULL;
1198 }
1199 }
1200
1201 // set delay, if applicable
1202 if (delay != 0) {
1203 if (dpiMsgProps_setDelay(props->handle, (int32_t) delay) < 0) {
1204 cxoError_raiseAndReturnNull();
1205 Py_DECREF(props);
1206 return NULL;
1207 }
1208 }
1209
1210 // set exception queue, if applicable
1211 if (exceptionQObj) {
1212 if (cxoBuffer_fromObject(&buffer, exceptionQObj,
1213 props->encoding) < 0) {
1214 Py_DECREF(props);
1215 return NULL;
1216 }
1217 status = dpiMsgProps_setExceptionQ(props->handle, buffer.ptr,
1218 buffer.size);
1219 cxoBuffer_clear(&buffer);
1220 if (status < 0) {
1221 cxoError_raiseAndReturnNull();
1222 Py_DECREF(props);
1223 return NULL;
1224 }
1225 }
1226
1227 // set expiration, if applicable
1228 if (expiration != -1) {
1229 if (dpiMsgProps_setExpiration(props->handle,
1230 (int32_t) expiration) < 0) {
1231 cxoError_raiseAndReturnNull();
1232 Py_DECREF(props);
1233 return NULL;
1234 }
1235 }
1236
1237 // set priority, if applicable
1238 if (priority != 0) {
1239 if (dpiMsgProps_setPriority(props->handle, (int32_t) priority) < 0) {
1240 cxoError_raiseAndReturnNull();
1241 Py_DECREF(props);
1242 return NULL;
1243 }
1244 }
1245
1246 return (PyObject*) props;
1247 }
1248
1249
1250 //-----------------------------------------------------------------------------
1251 // cxoConnection_dequeue()
1252 // Dequeues a message using Advanced Queuing capabilities. The message ID is
1253 // returned if a message is available or None if no message is available.
1254 //-----------------------------------------------------------------------------
1255 static PyObject *cxoConnection_dequeue(cxoConnection *conn, PyObject* args,
1256 PyObject* keywordArgs)
1257 {
1258 static char *keywordList[] = { "name", "options", "msgproperties",
1259 "payload", NULL };
1260 cxoMsgProps *propertiesObj;
1261 const char *messageIdValue;
1262 cxoDeqOptions *optionsObj;
1263 uint32_t messageIdLength;
1264 cxoObject *payloadObj;
1265 cxoBuffer nameBuffer;
1266 PyObject *nameObj;
1267 int status;
1268
1269 // parse arguments
1270 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!O!O!", keywordList,
1271 &nameObj, &cxoPyTypeDeqOptions, &optionsObj, &cxoPyTypeMsgProps,
1272 &propertiesObj, &cxoPyTypeObject, &payloadObj))
1273 return NULL;
1274 if (cxoConnection_isConnected(conn) < 0)
1275 return NULL;
1276 if (cxoBuffer_fromObject(&nameBuffer, nameObj,
1277 conn->encodingInfo.encoding) < 0)
1278 return NULL;
1279
1280 // dequeue payload
1281 Py_BEGIN_ALLOW_THREADS
1282 status = dpiConn_deqObject(conn->handle, nameBuffer.ptr, nameBuffer.size,
1283 optionsObj->handle, propertiesObj->handle, payloadObj->handle,
1284 &messageIdValue, &messageIdLength);
1285 Py_END_ALLOW_THREADS
1286 cxoBuffer_clear(&nameBuffer);
1287 if (status < 0)
1288 return cxoError_raiseAndReturnNull();
1289
1290 // return message id
1291 if (!messageIdValue)
1292 Py_RETURN_NONE;
1293 return PyBytes_FromStringAndSize(messageIdValue, messageIdLength);
1294 }
1295
1296
1297 //-----------------------------------------------------------------------------
1298 // cxoConnection_enqueue()
1299 // Enqueues a message using Advanced Queuing capabilities. The message ID is
1300 // returned.
1301 //-----------------------------------------------------------------------------
1302 static PyObject *cxoConnection_enqueue(cxoConnection *conn, PyObject* args,
1303 PyObject* keywordArgs)
1304 {
1305 static char *keywordList[] = { "name", "options", "msgproperties",
1306 "payload", NULL };
1307 cxoMsgProps *propertiesObj;
1308 const char *messageIdValue;
1309 cxoEnqOptions *optionsObj;
1310 uint32_t messageIdLength;
1311 cxoObject *payloadObj;
1312 cxoBuffer nameBuffer;
1313 PyObject *nameObj;
1314 int status;
1315
1316 // parse arguments
1317 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!O!O!", keywordList,
1318 &nameObj, &cxoPyTypeEnqOptions, &optionsObj, &cxoPyTypeMsgProps,
1319 &propertiesObj, &cxoPyTypeObject, &payloadObj))
1320 return NULL;
1321 if (cxoConnection_isConnected(conn) < 0)
1322 return NULL;
1323 if (cxoBuffer_fromObject(&nameBuffer, nameObj,
1324 conn->encodingInfo.encoding) < 0)
1325 return NULL;
1326
1327 // enqueue payload
1328 Py_BEGIN_ALLOW_THREADS
1329 status = dpiConn_enqObject(conn->handle, nameBuffer.ptr, nameBuffer.size,
1330 optionsObj->handle, propertiesObj->handle, payloadObj->handle,
1331 &messageIdValue, &messageIdLength);
1332 Py_END_ALLOW_THREADS
1333 cxoBuffer_clear(&nameBuffer);
1334 if (status < 0)
1335 return cxoError_raiseAndReturnNull();
1336
1337 // return message id
1338 return PyBytes_FromStringAndSize(messageIdValue, messageIdLength);
1339 }
1340
1341
1342 //-----------------------------------------------------------------------------
1343 // cxoConnection_queue()
1344 // Creates a new queue associated with the connection and returns it to the
1345 // caller.
1346 //-----------------------------------------------------------------------------
1347 static PyObject *cxoConnection_queue(cxoConnection *conn, PyObject* args,
1348 PyObject* keywordArgs)
1349 {
1350 static char *keywordList[] = { "name", "payloadType", NULL };
1351 cxoObjectType *typeObj;
1352 cxoBuffer nameBuffer;
1353 PyObject *nameObj;
1354 dpiQueue *handle;
1355 cxoQueue *queue;
1356 int status;
1357
1358 // parse arguments
1359 typeObj = NULL;
1360 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O!", keywordList,
1361 &nameObj, &cxoPyTypeObjectType, &typeObj))
1362 return NULL;
1363 if (cxoConnection_isConnected(conn) < 0)
1364 return NULL;
1365 if (cxoBuffer_fromObject(&nameBuffer, nameObj,
1366 conn->encodingInfo.encoding) < 0)
1367 return NULL;
1368
1369 // create queue
1370 status = dpiConn_newQueue(conn->handle, nameBuffer.ptr, nameBuffer.size,
1371 (typeObj) ? typeObj->handle : NULL, &handle);
1372 cxoBuffer_clear(&nameBuffer);
1373 if (status < 0)
1374 return cxoError_raiseAndReturnNull();
1375 queue = cxoQueue_new(conn, handle);
1376 if (!queue)
1377 return NULL;
1378 Py_INCREF(nameObj);
1379 queue->name = nameObj;
1380 Py_XINCREF(typeObj);
1381 queue->payloadType = typeObj;
1382
1383 return (PyObject*) queue;
1384 }
1385
1386
1387 //-----------------------------------------------------------------------------
1388 // cxoConnection_contextManagerEnter()
1389 // Called when the connection is used as a context manager and simply returns
1390 // itconn as a convenience to the caller.
1391 //-----------------------------------------------------------------------------
1392 static PyObject *cxoConnection_contextManagerEnter(cxoConnection *conn,
1393 PyObject* args)
1394 {
1395 if (cxoConnection_isConnected(conn) < 0)
1396 return NULL;
1397 Py_INCREF(conn);
1398 return (PyObject*) conn;
1399 }
1400
1401
1402 //-----------------------------------------------------------------------------
1403 // cxoConnection_contextManagerExit()
1404 // Called when the connection is used as a context manager and if any
1405 // exception a rollback takes place; otherwise, a commit takes place.
1406 //-----------------------------------------------------------------------------
1407 static PyObject *cxoConnection_contextManagerExit(cxoConnection *conn,
1408 PyObject* args)
1409 {
1410 PyObject *excType, *excValue, *excTraceback, *result;
1411
1412 if (!PyArg_ParseTuple(args, "OOO", &excType, &excValue, &excTraceback))
1413 return NULL;
1414 result = cxoConnection_close(conn, NULL);
1415 if (!result)
1416 return NULL;
1417 Py_DECREF(result);
1418
1419 Py_INCREF(Py_False);
1420 return Py_False;
1421 }
1422
1423
1424 //-----------------------------------------------------------------------------
1425 // cxoConnection_ping()
1426 // Makes a round trip call to the server to confirm that the connection and
1427 // server are active.
1428 //-----------------------------------------------------------------------------
1429 static PyObject *cxoConnection_ping(cxoConnection *conn, PyObject* args)
1430 {
1431 int status;
1432
1433 if (cxoConnection_isConnected(conn) < 0)
1434 return NULL;
1435 Py_BEGIN_ALLOW_THREADS
1436 status = dpiConn_ping(conn->handle);
1437 Py_END_ALLOW_THREADS
1438 if (status < 0)
1439 return cxoError_raiseAndReturnNull();
1440
1441 Py_RETURN_NONE;
1442 }
1443
1444
1445 //-----------------------------------------------------------------------------
1446 // cxoConnection_shutdown()
1447 // Shuts down the database. Note that this must be done in two phases except
1448 // in the situation where the instance is aborted.
1449 //-----------------------------------------------------------------------------
1450 static PyObject *cxoConnection_shutdown(cxoConnection *conn, PyObject* args,
1451 PyObject* keywordArgs)
1452 {
1453 static char *keywordList[] = { "mode", NULL };
1454 dpiShutdownMode mode;
1455
1456 // parse arguments
1457 mode = DPI_MODE_SHUTDOWN_DEFAULT;
1458 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
1459 &mode))
1460 return NULL;
1461
1462 // make sure we are actually connected
1463 if (cxoConnection_isConnected(conn) < 0)
1464 return NULL;
1465
1466 // perform the work
1467 if (dpiConn_shutdownDatabase(conn->handle, mode) < 0)
1468 return cxoError_raiseAndReturnNull();
1469
1470 Py_RETURN_NONE;
1471 }
1472
1473
1474 //-----------------------------------------------------------------------------
1475 // cxoConnection_startup()
1476 // Starts up the database, equivalent to "startup nomount" in SQL*Plus.
1477 //-----------------------------------------------------------------------------
1478 static PyObject *cxoConnection_startup(cxoConnection *conn, PyObject* args,
1479 PyObject* keywordArgs)
1480 {
1481 static char *keywordList[] = { "force", "restrict", "pfile", NULL };
1482 PyObject *forceObj, *restrictObj, *pfileObj;
1483 cxoBuffer pfileBuffer;
1484 dpiStartupMode mode;
1485 int temp;
1486
1487 // parse arguments
1488 forceObj = restrictObj = pfileObj = NULL;
1489 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|OOO", keywordList,
1490 &forceObj, &restrictObj, &pfileObj))
1491 return NULL;
1492
1493 // set the flags to use during startup
1494 mode = DPI_MODE_STARTUP_DEFAULT;
1495 if (cxoUtils_getBooleanValue(forceObj, 0, &temp) < 0)
1496 return NULL;
1497 if (temp)
1498 mode |= DPI_MODE_STARTUP_FORCE;
1499 if (cxoUtils_getBooleanValue(restrictObj, 0, &temp) < 0)
1500 return NULL;
1501 if (temp)
1502 mode |= DPI_MODE_STARTUP_RESTRICT;
1503
1504 // check the pfile parameter
1505 if (cxoBuffer_fromObject(&pfileBuffer, pfileObj,
1506 conn->encodingInfo.encoding) < 0)
1507 return NULL;
1508
1509 // make sure we are actually connected
1510 if (cxoConnection_isConnected(conn) < 0) {
1511 cxoBuffer_clear(&pfileBuffer);
1512 return NULL;
1513 }
1514
1515 // perform the work
1516 temp = dpiConn_startupDatabaseWithPfile(conn->handle, pfileBuffer.ptr,
1517 pfileBuffer.size, mode);
1518 cxoBuffer_clear(&pfileBuffer);
1519 if (temp < 0)
1520 return cxoError_raiseAndReturnNull();
1521
1522 Py_RETURN_NONE;
1523 }
1524
1525
1526 //-----------------------------------------------------------------------------
1527 // cxoConnection_subscribe()
1528 // Create a subscription to events that take place in the database.
1529 //-----------------------------------------------------------------------------
1530 static PyObject *cxoConnection_subscribe(cxoConnection *conn, PyObject* args,
1531 PyObject* keywordArgs)
1532 {
1533 static char *keywordList[] = { "namespace", "protocol", "callback",
1534 "timeout", "operations", "port", "qos", "ipAddress",
1535 "groupingClass", "groupingValue", "groupingType", "name",
1536 "clientInitiated", NULL };
1537 PyObject *callback, *ipAddress, *name, *clientInitiatedObj;
1538 cxoBuffer ipAddressBuffer, nameBuffer;
1539 dpiSubscrCreateParams params;
1540 cxoSubscr *subscr;
1541
1542 // get default values for subscription parameters
1543 if (dpiContext_initSubscrCreateParams(cxoDpiContext, &params) < 0)
1544 return cxoError_raiseAndReturnNull();
1545
1546 // validate parameters
1547 callback = name = ipAddress = clientInitiatedObj = NULL;
1548 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|IIOIIIIObIbOO",
1549 keywordList, &params.subscrNamespace, &params.protocol, &callback,
1550 &params.timeout, &params.operations, &params.portNumber,
1551 &params.qos, &ipAddress, &params.groupingClass,
1552 &params.groupingValue, &params.groupingType, &name,
1553 &clientInitiatedObj))
1554 return NULL;
1555 if (cxoConnection_isConnected(conn) < 0)
1556 return NULL;
1557 if (cxoUtils_getBooleanValue(clientInitiatedObj, 0,
1558 &params.clientInitiated) < 0)
1559 return NULL;
1560
1561 // populate IP address in parameters, if applicable
1562 cxoBuffer_init(&ipAddressBuffer);
1563 if (ipAddress) {
1564 if (cxoBuffer_fromObject(&ipAddressBuffer, ipAddress,
1565 conn->encodingInfo.encoding) < 0)
1566 return NULL;
1567 params.ipAddress = ipAddressBuffer.ptr;
1568 params.ipAddressLength = ipAddressBuffer.size;
1569 }
1570
1571 // populate name in parameters, if applicable
1572 cxoBuffer_init(&nameBuffer);
1573 if (name) {
1574 if (cxoBuffer_fromObject(&nameBuffer, name,
1575 conn->encodingInfo.encoding) < 0) {
1576 cxoBuffer_clear(&ipAddressBuffer);
1577 return NULL;
1578 }
1579 params.name = nameBuffer.ptr;
1580 params.nameLength = nameBuffer.size;
1581 }
1582
1583 // create Python subscription object
1584 subscr = (cxoSubscr*) cxoPyTypeSubscr.tp_alloc(&cxoPyTypeSubscr, 0);
1585 if (!subscr) {
1586 cxoBuffer_clear(&ipAddressBuffer);
1587 cxoBuffer_clear(&nameBuffer);
1588 return NULL;
1589 }
1590 Py_INCREF(conn);
1591 subscr->connection = conn;
1592 Py_XINCREF(callback);
1593 subscr->callback = callback;
1594 subscr->namespace = params.subscrNamespace;
1595 subscr->protocol = params.protocol;
1596 Py_XINCREF(ipAddress);
1597 subscr->ipAddress = ipAddress;
1598 Py_XINCREF(name);
1599 subscr->name = name;
1600 subscr->port = params.portNumber;
1601 subscr->timeout = params.timeout;
1602 subscr->operations = params.operations;
1603 subscr->qos = params.qos;
1604 subscr->groupingClass = params.groupingClass;
1605 subscr->groupingValue = params.groupingValue;
1606 subscr->groupingType = params.groupingType;
1607
1608 // populate callback in parameters, if applicable
1609 if (callback) {
1610 params.callback = (dpiSubscrCallback) cxoSubscr_callback;
1611 params.callbackContext = subscr;
1612 }
1613
1614 // create ODPI-C subscription
1615 if (dpiConn_subscribe(conn->handle, &params, &subscr->handle) < 0) {
1616 cxoError_raiseAndReturnNull();
1617 cxoBuffer_clear(&ipAddressBuffer);
1618 cxoBuffer_clear(&nameBuffer);
1619 Py_DECREF(subscr);
1620 return NULL;
1621 }
1622 subscr->id = params.outRegId;
1623 cxoBuffer_clear(&ipAddressBuffer);
1624 cxoBuffer_clear(&nameBuffer);
1625
1626 return (PyObject*) subscr;
1627 }
1628
1629
1630 //-----------------------------------------------------------------------------
1631 // cxoConnection_unsubscribe()
1632 // Destroy a subscription to events that take place in the database.
1633 //-----------------------------------------------------------------------------
1634 static PyObject *cxoConnection_unsubscribe(cxoConnection *conn, PyObject* args,
1635 PyObject* keywordArgs)
1636 {
1637 static char *keywordList[] = { "subscription", NULL };
1638 PyObject *subscrObj;
1639 cxoSubscr *subscr;
1640 int status;
1641
1642 // validate parameters
1643 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!", keywordList,
1644 &cxoPyTypeSubscr, &subscrObj))
1645 return NULL;
1646 if (cxoConnection_isConnected(conn) < 0)
1647 return NULL;
1648
1649 // destroy ODPI-C subscription
1650 subscr = (cxoSubscr*) subscrObj;
1651 Py_BEGIN_ALLOW_THREADS
1652 status = dpiConn_unsubscribe(conn->handle, subscr->handle);
1653 Py_END_ALLOW_THREADS
1654 if (status < 0)
1655 return cxoError_raiseAndReturnNull();
1656 subscr->handle = NULL;
1657
1658 Py_RETURN_NONE;
1659 }
1660
1661
1662 //-----------------------------------------------------------------------------
1663 // cxoConnection_getSodaDatabase()
1664 // Create and return a new SODA database object associated with the
1665 // connection.
1666 //-----------------------------------------------------------------------------
1667 static PyObject *cxoConnection_getSodaDatabase(cxoConnection *conn,
1668 PyObject *args)
1669 {
1670 if (cxoConnection_isConnected(conn) < 0)
1671 return NULL;
1672 return (PyObject*) cxoSodaDatabase_new(conn);
1673 }
1674
1675
1676 //-----------------------------------------------------------------------------
1677 // cxoConnection_getOciAttr()
1678 // Return the value of the OCI attribute. This is intended to be used for
1679 // testing attributes which are not currently exposed directly and should only
1680 // be used for that purpose.
1681 //-----------------------------------------------------------------------------
1682 static PyObject *cxoConnection_getOciAttr(cxoConnection *conn, PyObject *args,
1683 PyObject *keywordArgs)
1684 {
1685 static char *keywordList[] = { "handle_type", "attr_num", "attr_type",
1686 NULL };
1687 unsigned handleType, attrNum, attrType;
1688 uint32_t valueLength;
1689 dpiDataBuffer value;
1690
1691 // validate parameters
1692 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "III", keywordList,
1693 &handleType, &attrNum, &attrType))
1694 return NULL;
1695 if (cxoConnection_isConnected(conn) < 0)
1696 return NULL;
1697
1698 // get value and convert it to the appropriate Python value
1699 if (dpiConn_getOciAttr(conn->handle, handleType, attrNum, &value,
1700 &valueLength) < 0)
1701 return cxoError_raiseAndReturnNull();
1702 return cxoUtils_convertOciAttrToPythonValue(attrType, &value, valueLength,
1703 conn->encodingInfo.encoding);
1704 }
1705
1706
1707 //-----------------------------------------------------------------------------
1708 // cxoConnection_getCurrentSchema()
1709 // Return the current schema associated with the connection.
1710 //-----------------------------------------------------------------------------
1711 static PyObject *cxoConnection_getCurrentSchema(cxoConnection* conn,
1712 void* unused)
1713 {
1714 return cxoConnection_getAttrText(conn, dpiConn_getCurrentSchema);
1715 }
1716
1717
1718 //-----------------------------------------------------------------------------
1719 // cxoConnection_getEdition()
1720 // Return the edition associated with the connection.
1721 //-----------------------------------------------------------------------------
1722 static PyObject *cxoConnection_getEdition(cxoConnection* conn, void* unused)
1723 {
1724 return cxoConnection_getAttrText(conn, dpiConn_getEdition);
1725 }
1726
1727
1728 //-----------------------------------------------------------------------------
1729 // cxoConnection_getExternalName()
1730 // Return the external name associated with the connection.
1731 //-----------------------------------------------------------------------------
1732 static PyObject *cxoConnection_getExternalName(cxoConnection* conn,
1733 void* unused)
1734 {
1735 return cxoConnection_getAttrText(conn, dpiConn_getExternalName);
1736 }
1737
1738
1739 //-----------------------------------------------------------------------------
1740 // cxoConnection_getInternalName()
1741 // Return the internal name associated with the connection.
1742 //-----------------------------------------------------------------------------
1743 static PyObject *cxoConnection_getInternalName(cxoConnection* conn,
1744 void* unused)
1745 {
1746 return cxoConnection_getAttrText(conn, dpiConn_getInternalName);
1747 }
1748
1749
1750 //-----------------------------------------------------------------------------
1751 // cxoConnection_getException()
1752 // Return the requested exception.
1753 //-----------------------------------------------------------------------------
1754 static PyObject *cxoConnection_getException(cxoConnection *conn, void *arg)
1755 {
1756 PyObject *exc = * (PyObject**) arg;
1757
1758 Py_INCREF(exc);
1759 return exc;
1760 }
1761
1762
1763 //-----------------------------------------------------------------------------
1764 // cxoConnection_setAction()
1765 // Set the action associated with the connection.
1766 //-----------------------------------------------------------------------------
1767 static int cxoConnection_setAction(cxoConnection* conn, PyObject *value,
1768 void* unused)
1769 {
1770 return cxoConnection_setAttrText(conn, value, dpiConn_setAction);
1771 }
1772
1773
1774 //-----------------------------------------------------------------------------
1775 // cxoConnection_setClientIdentifier()
1776 // Set the client identifier associated with the connection.
1777 //-----------------------------------------------------------------------------
1778 static int cxoConnection_setClientIdentifier(cxoConnection* conn,
1779 PyObject *value, void* unused)
1780 {
1781 return cxoConnection_setAttrText(conn, value, dpiConn_setClientIdentifier);
1782 }
1783
1784
1785 //-----------------------------------------------------------------------------
1786 // cxoConnection_setClientInfo()
1787 // Set the client info associated with the connection.
1788 //-----------------------------------------------------------------------------
1789 static int cxoConnection_setClientInfo(cxoConnection* conn, PyObject *value,
1790 void* unused)
1791 {
1792 return cxoConnection_setAttrText(conn, value, dpiConn_setClientInfo);
1793 }
1794
1795
1796 //-----------------------------------------------------------------------------
1797 // cxoConnection_setCurrentSchema()
1798 // Set the current schema associated with the connection.
1799 //-----------------------------------------------------------------------------
1800 static int cxoConnection_setCurrentSchema(cxoConnection* conn, PyObject *value,
1801 void* unused)
1802 {
1803 return cxoConnection_setAttrText(conn, value, dpiConn_setCurrentSchema);
1804 }
1805
1806
1807 //-----------------------------------------------------------------------------
1808 // cxoConnection_setDbOp()
1809 // Set the database operation associated with the connection.
1810 //-----------------------------------------------------------------------------
1811 static int cxoConnection_setDbOp(cxoConnection* conn, PyObject *value,
1812 void* unused)
1813 {
1814 return cxoConnection_setAttrText(conn, value, dpiConn_setDbOp);
1815 }
1816
1817
1818 //-----------------------------------------------------------------------------
1819 // cxoConnection_setExternalName()
1820 // Set the external name associated with the connection.
1821 //-----------------------------------------------------------------------------
1822 static int cxoConnection_setExternalName(cxoConnection* conn, PyObject *value,
1823 void* unused)
1824 {
1825 return cxoConnection_setAttrText(conn, value, dpiConn_setExternalName);
1826 }
1827
1828
1829 //-----------------------------------------------------------------------------
1830 // cxoConnection_setInternalName()
1831 // Set the internal name associated with the connection.
1832 //-----------------------------------------------------------------------------
1833 static int cxoConnection_setInternalName(cxoConnection* conn, PyObject *value,
1834 void* unused)
1835 {
1836 return cxoConnection_setAttrText(conn, value, dpiConn_setInternalName);
1837 }
1838
1839
1840 //-----------------------------------------------------------------------------
1841 // cxoConnection_setModule()
1842 // Set the module associated with the connection.
1843 //-----------------------------------------------------------------------------
1844 static int cxoConnection_setModule(cxoConnection* conn, PyObject *value,
1845 void* unused)
1846 {
1847 return cxoConnection_setAttrText(conn, value, dpiConn_setModule);
1848 }
1849
1850
1851 //-----------------------------------------------------------------------------
1852 // cxoConnection_setOciAttr()
1853 // Set the value of the OCI attribute to the specified value. This is
1854 // intended to be used for testing attributes which are not currently exposed
1855 // directly and should only be used for that purpose.
1856 //-----------------------------------------------------------------------------
1857 static PyObject *cxoConnection_setOciAttr(cxoConnection *conn, PyObject *args,
1858 PyObject *keywordArgs)
1859 {
1860 static char *keywordList[] = { "handle_type", "attr_num", "attr_type",
1861 "value", NULL };
1862 unsigned handleType, attrNum, attrType;
1863 uint32_t ociValueLength;
1864 dpiDataBuffer ociBuffer;
1865 cxoBuffer buffer;
1866 PyObject *value;
1867 void *ociValue;
1868
1869 // validate parameters
1870 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "IIIO", keywordList,
1871 &handleType, &attrNum, &attrType, &value))
1872 return NULL;
1873 if (cxoConnection_isConnected(conn) < 0)
1874 return NULL;
1875
1876 // convert value to OCI requirement and then set it
1877 cxoBuffer_init(&buffer);
1878 if (cxoUtils_convertPythonValueToOciAttr(value, attrType, &buffer,
1879 &ociBuffer, &ociValue, &ociValueLength,
1880 conn->encodingInfo.encoding) < 0)
1881 return NULL;
1882 if (dpiConn_setOciAttr(conn->handle, handleType, attrNum, ociValue,
1883 ociValueLength) < 0)
1884 return cxoError_raiseAndReturnNull();
1885 cxoBuffer_clear(&buffer);
1886
1887 Py_RETURN_NONE;
1888 }
1889
1890
1891 //-----------------------------------------------------------------------------
1892 // declaration of methods for the Python type
1893 //-----------------------------------------------------------------------------
1894 static PyMethodDef cxoMethods[] = {
761895 { "cursor", (PyCFunction) cxoConnection_newCursor,
771896 METH_VARARGS | METH_KEYWORDS },
781897 { "commit", (PyCFunction) cxoConnection_commit, METH_NOARGS },
1021921 { "enqoptions", (PyCFunction) cxoConnection_newEnqueueOptions,
1031922 METH_NOARGS },
1041923 { "msgproperties", (PyCFunction) cxoConnection_newMessageProperties,
105 METH_NOARGS },
1924 METH_VARARGS | METH_KEYWORDS },
1061925 { "deq", (PyCFunction) cxoConnection_dequeue,
1071926 METH_VARARGS | METH_KEYWORDS },
1081927 { "enq", (PyCFunction) cxoConnection_enqueue,
1091928 METH_VARARGS | METH_KEYWORDS },
1929 { "queue", (PyCFunction) cxoConnection_queue,
1930 METH_VARARGS | METH_KEYWORDS },
1101931 { "createlob", (PyCFunction) cxoConnection_createLob, METH_O },
1111932 { "getSodaDatabase", (PyCFunction) cxoConnection_getSodaDatabase,
1121933 METH_NOARGS },
1934 { "_get_oci_attr", (PyCFunction) cxoConnection_getOciAttr,
1935 METH_VARARGS | METH_KEYWORDS },
1936 { "_set_oci_attr", (PyCFunction) cxoConnection_setOciAttr,
1937 METH_VARARGS | METH_KEYWORDS },
1131938 { NULL }
1141939 };
1151940
1161941
1171942 //-----------------------------------------------------------------------------
118 // declaration of members for Python type "Connection"
119 //-----------------------------------------------------------------------------
120 static PyMemberDef cxoConnectionMembers[] = {
1943 // declaration of members for the Python type
1944 //-----------------------------------------------------------------------------
1945 static PyMemberDef cxoMembers[] = {
1211946 { "username", T_OBJECT, offsetof(cxoConnection, username), READONLY },
1221947 { "dsn", T_OBJECT, offsetof(cxoConnection, dsn), READONLY },
1231948 { "tnsentry", T_OBJECT, offsetof(cxoConnection, dsn), READONLY },
1321957
1331958
1341959 //-----------------------------------------------------------------------------
135 // declaration of calculated members for Python type "Connection"
136 //-----------------------------------------------------------------------------
137 static PyGetSetDef cxoConnectionCalcMembers[] = {
1960 // declaration of calculated members for the Python type
1961 //-----------------------------------------------------------------------------
1962 static PyGetSetDef cxoCalcMembers[] = {
1381963 { "version", (getter) cxoConnection_getVersion, 0, 0, 0 },
1391964 { "encoding", (getter) cxoConnection_getEncoding, 0, 0, 0 },
1401965 { "nencoding", (getter) cxoConnection_getNationalEncoding, 0, 0, 0 },
1842009
1852010
1862011 //-----------------------------------------------------------------------------
187 // declaration of Python type "Connection"
2012 // declaration of the Python type
1882013 //-----------------------------------------------------------------------------
1892014 PyTypeObject cxoPyTypeConnection = {
1902015 PyVarObject_HEAD_INIT(NULL, 0)
191 "cx_Oracle.Connection", // tp_name
192 sizeof(cxoConnection), // tp_basicsize
193 0, // tp_itemsize
194 (destructor) cxoConnection_free, // tp_dealloc
195 0, // tp_print
196 0, // tp_getattr
197 0, // tp_setattr
198 0, // tp_compare
199 (reprfunc) cxoConnection_repr, // tp_repr
200 0, // tp_as_number
201 0, // tp_as_sequence
202 0, // tp_as_mapping
203 0, // tp_hash
204 0, // tp_call
205 0, // tp_str
206 0, // tp_getattro
207 0, // tp_setattro
208 0, // tp_as_buffer
209 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
210 // tp_flags
211 0, // tp_doc
212 0, // tp_traverse
213 0, // tp_clear
214 0, // tp_richcompare
215 0, // tp_weaklistoffset
216 0, // tp_iter
217 0, // tp_iternext
218 cxoConnectionMethods, // tp_methods
219 cxoConnectionMembers, // tp_members
220 cxoConnectionCalcMembers, // tp_getset
221 0, // tp_base
222 0, // tp_dict
223 0, // tp_descr_get
224 0, // tp_descr_set
225 0, // tp_dictoffset
226 (initproc) cxoConnection_init, // tp_init
227 0, // tp_alloc
228 (newfunc) cxoConnection_new, // tp_new
229 0, // tp_free
230 0, // tp_is_gc
231 0 // tp_bases
2016 .tp_name = "cx_Oracle.Connection",
2017 .tp_basicsize = sizeof(cxoConnection),
2018 .tp_dealloc = (destructor) cxoConnection_free,
2019 .tp_repr = (reprfunc) cxoConnection_repr,
2020 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
2021 .tp_methods = cxoMethods,
2022 .tp_members = cxoMembers,
2023 .tp_getset = cxoCalcMembers,
2024 .tp_init = (initproc) cxoConnection_init,
2025 .tp_new = (newfunc) cxoConnection_new
2322026 };
233
234
235 //-----------------------------------------------------------------------------
236 // structure used to help in establishing a connection
237 //-----------------------------------------------------------------------------
238 typedef struct {
239 const char *encoding;
240 const char *nencoding;
241 cxoBuffer userNameBuffer;
242 cxoBuffer passwordBuffer;
243 cxoBuffer newPasswordBuffer;
244 cxoBuffer dsnBuffer;
245 cxoBuffer connectionClassBuffer;
246 cxoBuffer editionBuffer;
247 cxoBuffer tagBuffer;
248 uint32_t numAppContext;
249 dpiAppContext *appContext;
250 cxoBuffer *ctxNamespaceBuffers;
251 cxoBuffer *ctxNameBuffers;
252 cxoBuffer *ctxValueBuffers;
253 dpiShardingKeyColumn *shardingKeyColumns;
254 cxoBuffer *shardingKeyBuffers;
255 uint32_t numShardingKeyColumns;
256 dpiShardingKeyColumn *superShardingKeyColumns;
257 uint32_t numSuperShardingKeyColumns;
258 cxoBuffer *superShardingKeyBuffers;
259 } cxoConnectionParams;
260
261
262 //-----------------------------------------------------------------------------
263 // cxoConnectionParams_initialize()
264 // Initialize the parameters to default values.
265 //-----------------------------------------------------------------------------
266 static void cxoConnectionParams_initialize(cxoConnectionParams *params)
267 {
268 cxoBuffer_init(&params->userNameBuffer);
269 cxoBuffer_init(&params->passwordBuffer);
270 cxoBuffer_init(&params->newPasswordBuffer);
271 cxoBuffer_init(&params->dsnBuffer);
272 cxoBuffer_init(&params->connectionClassBuffer);
273 cxoBuffer_init(&params->editionBuffer);
274 cxoBuffer_init(&params->tagBuffer);
275 params->numAppContext = 0;
276 params->appContext = NULL;
277 params->ctxNamespaceBuffers = NULL;
278 params->ctxNameBuffers = NULL;
279 params->ctxValueBuffers = NULL;
280 params->numShardingKeyColumns = 0;
281 params->shardingKeyColumns = NULL;
282 params->shardingKeyBuffers = NULL;
283 params->numSuperShardingKeyColumns = 0;
284 params->superShardingKeyColumns = NULL;
285 params->superShardingKeyBuffers = NULL;
286 }
287
288
289 //-----------------------------------------------------------------------------
290 // cxoConnectionParams_ProcessContext()
291 // Process context for the connection parameters. This validates that the
292 // context passed in is a list of 3-tuples (namespace, name, value) and
293 // populates the parametrs with buffers for each of these.
294 //-----------------------------------------------------------------------------
295 static int cxoConnectionParams_processContext(cxoConnectionParams *params,
296 PyObject *context)
297 {
298 uint32_t numEntries, i;
299 dpiAppContext *entry;
300 PyObject *entryObj;
301 size_t memorySize;
302
303 // validate context is a list with at least one entry in it
304 if (!context)
305 return 0;
306 if (!PyList_Check(context)) {
307 PyErr_SetString(PyExc_TypeError,
308 "appcontext should be a list of 3-tuples");
309 return -1;
310 }
311 numEntries = (uint32_t) PyList_GET_SIZE(context);
312 if (numEntries == 0)
313 return 0;
314
315 // allocate memory for the buffers used to communicate with DPI
316 params->appContext = PyMem_Malloc(numEntries * sizeof(dpiAppContext));
317 memorySize = numEntries * sizeof(cxoBuffer);
318 params->ctxNamespaceBuffers = PyMem_Malloc(memorySize);
319 params->ctxNameBuffers = PyMem_Malloc(memorySize);
320 params->ctxValueBuffers = PyMem_Malloc(memorySize);
321 if (!params->appContext || !params->ctxNamespaceBuffers ||
322 !params->ctxNameBuffers || !params->ctxValueBuffers) {
323 PyErr_NoMemory();
324 return -1;
325 }
326
327 // initialize buffers
328 for (i = 0; i < numEntries; i++) {
329 cxoBuffer_init(&params->ctxNamespaceBuffers[i]);
330 cxoBuffer_init(&params->ctxNameBuffers[i]);
331 cxoBuffer_init(&params->ctxValueBuffers[i]);
332 }
333 params->numAppContext = numEntries;
334
335 // process each entry
336 for (i = 0; i < numEntries; i++) {
337 entryObj = PyList_GET_ITEM(context, i);
338 if (!PyTuple_Check(entryObj) || PyTuple_GET_SIZE(entryObj) != 3) {
339 PyErr_SetString(PyExc_TypeError,
340 "appcontext should be a list of 3-tuples");
341 return -1;
342 }
343 if (cxoBuffer_fromObject(&params->ctxNamespaceBuffers[i],
344 PyTuple_GET_ITEM(entryObj, 0), params->encoding) < 0)
345 return -1;
346 if (cxoBuffer_fromObject(&params->ctxNameBuffers[i],
347 PyTuple_GET_ITEM(entryObj, 1), params->encoding) < 0)
348 return -1;
349 if (cxoBuffer_fromObject(&params->ctxValueBuffers[i],
350 PyTuple_GET_ITEM(entryObj, 2), params->encoding) < 0)
351 return -1;
352 entry = &params->appContext[i];
353 entry->namespaceName = params->ctxNamespaceBuffers[i].ptr;
354 entry->namespaceNameLength = params->ctxNamespaceBuffers[i].size;
355 entry->name = params->ctxNameBuffers[i].ptr;
356 entry->nameLength = params->ctxNameBuffers[i].size;
357 entry->value = params->ctxValueBuffers[i].ptr;
358 entry->valueLength = params->ctxValueBuffers[i].size;
359 }
360
361 return 0;
362 }
363
364
365 //-----------------------------------------------------------------------------
366 // cxoConnectionParams_processShardingKeyValue()
367 // Process a single sharding key value.
368 //-----------------------------------------------------------------------------
369 static int cxoConnectionParams_processShardingKeyValue(
370 cxoConnectionParams *params, PyObject *value,
371 dpiShardingKeyColumn *column, cxoBuffer *buffer)
372 {
373 cxoTransformNum transformNum;
374
375 transformNum = cxoTransform_getNumFromValue(value, 0);
376 if (cxoTransform_fromPython(transformNum, value, &column->value, buffer,
377 params->encoding, params->nencoding, NULL, 0) < 0)
378 return -1;
379 cxoTransform_getTypeInfo(transformNum, &column->oracleTypeNum,
380 &column->nativeTypeNum);
381 return 0;
382 }
383
384
385 //-----------------------------------------------------------------------------
386 // cxoConnectionParams_processShardingKey()
387 // Process either the sharding key or the super sharding key. A sharding key
388 // is expected to be a sequence of values. A null value or a sequence of size
389 // 0 is ignored.
390 //-----------------------------------------------------------------------------
391 static int cxoConnectionParams_processShardingKey(cxoConnectionParams *params,
392 PyObject *shardingKeyObj, int isSuperShardingKey)
393 {
394 dpiShardingKeyColumn *columns;
395 uint32_t i, numColumns;
396 cxoBuffer *buffers;
397 PyObject *value;
398
399 // validate sharding key
400 if (!shardingKeyObj || shardingKeyObj == Py_None)
401 return 0;
402 if (!PySequence_Check(shardingKeyObj)) {
403 PyErr_SetString(PyExc_TypeError, "expecting a sequence");
404 return -1;
405 }
406 numColumns = (uint32_t) PySequence_Size(shardingKeyObj);
407 if (numColumns == 0)
408 return 0;
409
410 // allocate memory for the sharding key values
411 columns = PyMem_Malloc(numColumns * sizeof(dpiShardingKeyColumn));
412 buffers = PyMem_Malloc(numColumns * sizeof(cxoBuffer));
413 if (isSuperShardingKey) {
414 params->superShardingKeyColumns = columns;
415 params->superShardingKeyBuffers = buffers;
416 params->numSuperShardingKeyColumns = numColumns;
417 } else {
418 params->shardingKeyColumns = columns;
419 params->shardingKeyBuffers = buffers;
420 params->numShardingKeyColumns = numColumns;
421 }
422 if (!columns || !buffers) {
423 PyErr_NoMemory();
424 return -1;
425 }
426
427 // process each value
428 for (i = 0; i < numColumns; i++) {
429 cxoBuffer_init(&buffers[i]);
430 value = PySequence_GetItem(shardingKeyObj, i);
431 if (!value)
432 return -1;
433 if (cxoConnectionParams_processShardingKeyValue(params, value,
434 &columns[i], &buffers[i]) < 0)
435 return -1;
436 }
437
438 return 0;
439 }
440
441
442 //-----------------------------------------------------------------------------
443 // cxoConnectionParams_finalize()
444 // Finalize the parameters, freeing any resources that were allocated. The
445 // return value is a convenience to the caller.
446 //-----------------------------------------------------------------------------
447 static int cxoConnectionParams_finalize(cxoConnectionParams *params)
448 {
449 uint32_t i;
450
451 cxoBuffer_clear(&params->userNameBuffer);
452 cxoBuffer_clear(&params->passwordBuffer);
453 cxoBuffer_clear(&params->newPasswordBuffer);
454 cxoBuffer_clear(&params->dsnBuffer);
455 cxoBuffer_clear(&params->connectionClassBuffer);
456 cxoBuffer_clear(&params->editionBuffer);
457 cxoBuffer_clear(&params->tagBuffer);
458 for (i = 0; i < params->numAppContext; i++) {
459 cxoBuffer_clear(&params->ctxNamespaceBuffers[i]);
460 cxoBuffer_clear(&params->ctxNameBuffers[i]);
461 cxoBuffer_clear(&params->ctxValueBuffers[i]);
462 }
463 params->numAppContext = 0;
464 if (params->appContext) {
465 PyMem_Free(params->appContext);
466 params->appContext = NULL;
467 }
468 if (params->ctxNamespaceBuffers) {
469 PyMem_Free(params->ctxNamespaceBuffers);
470 params->ctxNamespaceBuffers = NULL;
471 }
472 if (params->ctxNameBuffers) {
473 PyMem_Free(params->ctxNameBuffers);
474 params->ctxNameBuffers = NULL;
475 }
476 if (params->ctxValueBuffers) {
477 PyMem_Free(params->ctxValueBuffers);
478 params->ctxValueBuffers = NULL;
479 }
480 for (i = 0; i < params->numShardingKeyColumns; i++)
481 cxoBuffer_clear(&params->shardingKeyBuffers[i]);
482 if (params->shardingKeyColumns) {
483 PyMem_Free(params->shardingKeyColumns);
484 params->shardingKeyColumns = NULL;
485 }
486 if (params->shardingKeyBuffers) {
487 PyMem_Free(params->shardingKeyBuffers);
488 params->shardingKeyBuffers = NULL;
489 }
490 for (i = 0; i < params->numSuperShardingKeyColumns; i++)
491 cxoBuffer_clear(&params->superShardingKeyBuffers[i]);
492 if (params->superShardingKeyColumns) {
493 PyMem_Free(params->superShardingKeyColumns);
494 params->superShardingKeyColumns = NULL;
495 }
496 if (params->superShardingKeyBuffers) {
497 PyMem_Free(params->superShardingKeyBuffers);
498 params->superShardingKeyBuffers = NULL;
499 }
500 return -1;
501 }
502
503
504 //-----------------------------------------------------------------------------
505 // cxoConnection_getSodaFlags()
506 // Get the flags to use for SODA. This checks the autocommit flag and enables
507 // atomic commit if set to a true value. It also checks to ensure that the
508 // connection is valid.
509 //-----------------------------------------------------------------------------
510 int cxoConnection_getSodaFlags(cxoConnection *conn, uint32_t *flags)
511 {
512 if (cxoConnection_isConnected(conn) < 0)
513 return -1;
514 *flags = (conn->autocommit) ? DPI_SODA_FLAGS_ATOMIC_COMMIT :
515 DPI_SODA_FLAGS_DEFAULT;
516 return 0;
517 }
518
519
520 //-----------------------------------------------------------------------------
521 // cxoConnection_isConnected()
522 // Determines if the connection object is connected to the database. If not,
523 // a Python exception is raised.
524 //-----------------------------------------------------------------------------
525 int cxoConnection_isConnected(cxoConnection *conn)
526 {
527 if (!conn->handle) {
528 cxoError_raiseFromString(cxoInterfaceErrorException, "not connected");
529 return -1;
530 }
531 return 0;
532 }
533
534
535 //-----------------------------------------------------------------------------
536 // cxoConnection_getAttrText()
537 // Get the value of the attribute returned from the given function. The value
538 // is assumed to be a text value.
539 //-----------------------------------------------------------------------------
540 static PyObject *cxoConnection_getAttrText(cxoConnection *conn,
541 int (*func)(dpiConn *conn, const char **value, uint32_t *valueLength))
542 {
543 uint32_t valueLength;
544 const char *value;
545
546 if (cxoConnection_isConnected(conn) < 0)
547 return NULL;
548 if ((*func)(conn->handle, &value, &valueLength) < 0)
549 return cxoError_raiseAndReturnNull();
550 if (!value)
551 Py_RETURN_NONE;
552 return cxoPyString_fromEncodedString(value, valueLength,
553 conn->encodingInfo.encoding, NULL);
554 }
555
556
557 //-----------------------------------------------------------------------------
558 // cxoConnection_setAttrText()
559 // Set the value of the attribute using the given function. The value is
560 // assumed to be a text value.
561 //-----------------------------------------------------------------------------
562 static int cxoConnection_setAttrText(cxoConnection *conn, PyObject *value,
563 int (*func)(dpiConn *conn, const char *value, uint32_t valueLength))
564 {
565 cxoBuffer buffer;
566 int status;
567
568 if (cxoConnection_isConnected(conn) < 0)
569 return -1;
570 if (cxoBuffer_fromObject(&buffer, value, conn->encodingInfo.encoding))
571 return -1;
572 status = (*func)(conn->handle, buffer.ptr, buffer.size);
573 cxoBuffer_clear(&buffer);
574 if (status < 0)
575 return cxoError_raiseAndReturnInt();
576 return 0;
577 }
578
579
580 //-----------------------------------------------------------------------------
581 // cxoConnection_changePassword()
582 // Change the password for the given connection.
583 //-----------------------------------------------------------------------------
584 static PyObject *cxoConnection_changePassword(cxoConnection *conn,
585 PyObject *args)
586 {
587 cxoBuffer usernameBuffer, oldPasswordBuffer, newPasswordBuffer;
588 PyObject *oldPasswordObj, *newPasswordObj;
589 int status;
590
591 // parse the arguments
592 if (!PyArg_ParseTuple(args, "OO", &oldPasswordObj, &newPasswordObj))
593 return NULL;
594
595 // populate buffers
596 cxoBuffer_init(&usernameBuffer);
597 cxoBuffer_init(&oldPasswordBuffer);
598 cxoBuffer_init(&newPasswordBuffer);
599 if (cxoBuffer_fromObject(&usernameBuffer, conn->username,
600 conn->encodingInfo.encoding) < 0 ||
601 cxoBuffer_fromObject(&oldPasswordBuffer, oldPasswordObj,
602 conn->encodingInfo.encoding) < 0 ||
603 cxoBuffer_fromObject(&newPasswordBuffer, newPasswordObj,
604 conn->encodingInfo.encoding) < 0) {
605 cxoBuffer_clear(&usernameBuffer);
606 cxoBuffer_clear(&oldPasswordBuffer);
607 cxoBuffer_clear(&newPasswordBuffer);
608 return NULL;
609 }
610
611 // change the password
612 Py_BEGIN_ALLOW_THREADS
613 status = dpiConn_changePassword(conn->handle, usernameBuffer.ptr,
614 usernameBuffer.size, oldPasswordBuffer.ptr, oldPasswordBuffer.size,
615 newPasswordBuffer.ptr, newPasswordBuffer.size);
616 Py_END_ALLOW_THREADS
617 cxoBuffer_clear(&usernameBuffer);
618 cxoBuffer_clear(&oldPasswordBuffer);
619 cxoBuffer_clear(&newPasswordBuffer);
620 if (status < 0)
621 return cxoError_raiseAndReturnNull();
622
623 Py_RETURN_NONE;
624 }
625
626
627 //-----------------------------------------------------------------------------
628 // cxoConnection_new()
629 // Create a new connection object and return it.
630 //-----------------------------------------------------------------------------
631 static PyObject *cxoConnection_new(PyTypeObject *type, PyObject *args,
632 PyObject *keywordArgs)
633 {
634 return type->tp_alloc(type, 0);
635 }
636
637
638 //-----------------------------------------------------------------------------
639 // cxoConnection_splitComponent()
640 // Split the component out of the source and replace the source with the
641 // characters up to the split string and put the characters after the split
642 // string in to the target.
643 //-----------------------------------------------------------------------------
644 static int cxoConnection_splitComponent(PyObject **sourceObj,
645 PyObject **targetObj, const char *splitString)
646 {
647 PyObject *temp, *posObj;
648 Py_ssize_t size, pos;
649
650 if (!*sourceObj || *targetObj)
651 return 0;
652 posObj = PyObject_CallMethod(*sourceObj, "find", "s", splitString);
653 if (!posObj)
654 return -1;
655 pos = PyInt_AsLong(posObj);
656 Py_DECREF(posObj);
657 if (PyErr_Occurred())
658 return -1;
659 if (pos >= 0) {
660 size = PySequence_Size(*sourceObj);
661 if (PyErr_Occurred())
662 return -1;
663 *targetObj = PySequence_GetSlice(*sourceObj, pos + 1, size);
664 if (!*targetObj)
665 return -1;
666 temp = PySequence_GetSlice(*sourceObj, 0, pos);
667 if (!temp)
668 return -1;
669 *sourceObj = temp;
670 }
671 return 0;
672 }
673
674
675 //-----------------------------------------------------------------------------
676 // cxoConnection_init()
677 // Initialize the connection members.
678 //-----------------------------------------------------------------------------
679 static int cxoConnection_init(cxoConnection *conn, PyObject *args,
680 PyObject *keywordArgs)
681 {
682 PyObject *tagObj, *matchAnyTagObj, *threadedObj, *eventsObj, *contextObj;
683 PyObject *usernameObj, *passwordObj, *dsnObj, *cclassObj, *editionObj;
684 PyObject *shardingKeyObj, *superShardingKeyObj, *tempObj;
685 int status, temp, invokeSessionCallback;
686 dpiCommonCreateParams dpiCommonParams;
687 dpiConnCreateParams dpiCreateParams;
688 unsigned long long externalHandle;
689 cxoConnectionParams params;
690 PyObject *newPasswordObj;
691 cxoSessionPool *pool;
692
693 // define keyword arguments
694 static char *keywordList[] = { "user", "password", "dsn", "mode",
695 "handle", "pool", "threaded", "events", "cclass", "purity",
696 "newpassword", "encoding", "nencoding", "edition", "appcontext",
697 "tag", "matchanytag", "shardingkey", "supershardingkey", NULL };
698
699 // parse arguments
700 pool = NULL;
701 tagObj = Py_None;
702 externalHandle = 0;
703 passwordObj = dsnObj = cclassObj = editionObj = NULL;
704 threadedObj = eventsObj = newPasswordObj = usernameObj = NULL;
705 matchAnyTagObj = contextObj = shardingKeyObj = superShardingKeyObj = NULL;
706 if (cxoUtils_initializeDPI() < 0)
707 return -1;
708 if (dpiContext_initCommonCreateParams(cxoDpiContext, &dpiCommonParams) < 0)
709 return cxoError_raiseAndReturnInt();
710 dpiCommonParams.driverName = CXO_DRIVER_NAME;
711 dpiCommonParams.driverNameLength =
712 (uint32_t) strlen(dpiCommonParams.driverName);
713 if (dpiContext_initConnCreateParams(cxoDpiContext, &dpiCreateParams) < 0)
714 return cxoError_raiseAndReturnInt();
715 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs,
716 "|OOOiKO!OOOiOssOOOOOO", keywordList, &usernameObj, &passwordObj,
717 &dsnObj, &dpiCreateParams.authMode, &externalHandle,
718 &cxoPyTypeSessionPool, &pool, &threadedObj, &eventsObj, &cclassObj,
719 &dpiCreateParams.purity, &newPasswordObj,
720 &dpiCommonParams.encoding, &dpiCommonParams.nencoding, &editionObj,
721 &contextObj, &tagObj, &matchAnyTagObj, &shardingKeyObj,
722 &superShardingKeyObj))
723 return -1;
724 dpiCreateParams.externalHandle = (void*) externalHandle;
725 if (cxoUtils_getBooleanValue(threadedObj, 0, &temp) < 0)
726 return -1;
727 if (temp)
728 dpiCommonParams.createMode |= DPI_MODE_CREATE_THREADED;
729 if (cxoUtils_getBooleanValue(eventsObj, 0, &temp) < 0)
730 return -1;
731 if (temp)
732 dpiCommonParams.createMode |= DPI_MODE_CREATE_EVENTS;
733 if (cxoUtils_getBooleanValue(matchAnyTagObj, 0,
734 &dpiCreateParams.matchAnyTag) < 0)
735 return -1;
736
737 // keep a copy of the user name and connect string (DSN)
738 Py_XINCREF(usernameObj);
739 conn->username = usernameObj;
740 Py_XINCREF(dsnObj);
741 conn->dsn = dsnObj;
742
743 // perform some parsing, if necessary
744 if (cxoConnection_splitComponent(&conn->username, &passwordObj, "/") < 0)
745 return -1;
746 if (cxoConnection_splitComponent(&passwordObj, &conn->dsn, "@") < 0)
747 return -1;
748
749 // setup parameters
750 cxoConnectionParams_initialize(&params);
751 if (pool) {
752 dpiCreateParams.pool = pool->handle;
753 params.encoding = pool->encodingInfo.encoding;
754 params.nencoding = pool->encodingInfo.nencoding;
755 } else {
756 params.encoding =
757 cxoUtils_getAdjustedEncoding(dpiCommonParams.encoding);
758 params.nencoding =
759 cxoUtils_getAdjustedEncoding(dpiCommonParams.nencoding);
760 }
761 if (cxoConnectionParams_processContext(&params, contextObj) < 0)
762 return cxoConnectionParams_finalize(&params);
763 if (cxoConnectionParams_processShardingKey(&params, shardingKeyObj, 0) < 0)
764 return cxoConnectionParams_finalize(&params);
765 if (cxoConnectionParams_processShardingKey(&params, superShardingKeyObj,
766 1) < 0)
767 return cxoConnectionParams_finalize(&params);
768 if (cxoBuffer_fromObject(&params.userNameBuffer, conn->username,
769 params.encoding) < 0 ||
770 cxoBuffer_fromObject(&params.passwordBuffer, passwordObj,
771 params.encoding) < 0 ||
772 cxoBuffer_fromObject(&params.dsnBuffer, conn->dsn,
773 params.encoding) < 0 ||
774 cxoBuffer_fromObject(&params.connectionClassBuffer, cclassObj,
775 params.encoding) < 0 ||
776 cxoBuffer_fromObject(&params.newPasswordBuffer, newPasswordObj,
777 params.encoding) < 0 ||
778 cxoBuffer_fromObject(&params.editionBuffer, editionObj,
779 params.encoding) < 0 ||
780 cxoBuffer_fromObject(&params.tagBuffer, tagObj,
781 params.encoding) < 0)
782 return cxoConnectionParams_finalize(&params);
783 if (params.userNameBuffer.size == 0 && params.passwordBuffer.size == 0)
784 dpiCreateParams.externalAuth = 1;
785 dpiCreateParams.connectionClass = params.connectionClassBuffer.ptr;
786 dpiCreateParams.connectionClassLength = params.connectionClassBuffer.size;
787 dpiCreateParams.newPassword = params.newPasswordBuffer.ptr;
788 dpiCreateParams.newPasswordLength = params.newPasswordBuffer.size;
789 dpiCommonParams.edition = params.editionBuffer.ptr;
790 dpiCommonParams.editionLength = params.editionBuffer.size;
791 dpiCreateParams.tag = params.tagBuffer.ptr;
792 dpiCreateParams.tagLength = params.tagBuffer.size;
793 dpiCreateParams.appContext = params.appContext;
794 dpiCreateParams.numAppContext = params.numAppContext;
795 dpiCreateParams.shardingKeyColumns = params.shardingKeyColumns;
796 dpiCreateParams.numShardingKeyColumns = params.numShardingKeyColumns;
797 dpiCreateParams.superShardingKeyColumns = params.superShardingKeyColumns;
798 dpiCreateParams.numSuperShardingKeyColumns =
799 params.numSuperShardingKeyColumns;
800 if (pool && !pool->homogeneous && pool->username && conn->username) {
801 temp = PyObject_RichCompareBool(conn->username, pool->username, Py_EQ);
802 if (temp < 0)
803 return cxoConnectionParams_finalize(&params);
804 if (temp)
805 params.userNameBuffer.size = 0;
806 }
807
808 // create connection
809 Py_BEGIN_ALLOW_THREADS
810 status = dpiConn_create(cxoDpiContext, params.userNameBuffer.ptr,
811 params.userNameBuffer.size, params.passwordBuffer.ptr,
812 params.passwordBuffer.size, params.dsnBuffer.ptr,
813 params.dsnBuffer.size, &dpiCommonParams, &dpiCreateParams,
814 &conn->handle);
815 Py_END_ALLOW_THREADS
816 if (status < 0) {
817 cxoConnectionParams_finalize(&params);
818 return cxoError_raiseAndReturnInt();
819 }
820
821 // determine if session callback should be invoked; this takes place if
822 // the connection is newly created by the pool or if the requested tag
823 // does not match the actal tag
824 invokeSessionCallback = 0;
825 if (dpiCreateParams.outNewSession ||
826 dpiCreateParams.outTagLength != params.tagBuffer.size ||
827 (dpiCreateParams.outTagLength > 0 &&
828 strncmp(dpiCreateParams.outTag, params.tagBuffer.ptr,
829 dpiCreateParams.outTagLength) != 0))
830 invokeSessionCallback = 1;
831 cxoConnectionParams_finalize(&params);
832
833 // determine encodings to use
834 if (pool)
835 conn->encodingInfo = pool->encodingInfo;
836 else {
837 if (dpiConn_getEncodingInfo(conn->handle, &conn->encodingInfo) < 0)
838 return cxoError_raiseAndReturnInt();
839 conn->encodingInfo.encoding =
840 cxoUtils_getAdjustedEncoding(conn->encodingInfo.encoding);
841 conn->encodingInfo.nencoding =
842 cxoUtils_getAdjustedEncoding(conn->encodingInfo.nencoding);
843 }
844
845 // set tag property
846 if (dpiCreateParams.outTagLength > 0) {
847 conn->tag = cxoPyString_fromEncodedString(dpiCreateParams.outTag,
848 dpiCreateParams.outTagLength, conn->encodingInfo.encoding,
849 NULL);
850 if (!conn->tag)
851 return -1;
852 }
853
854 // invoke the session callback if applicable
855 if (invokeSessionCallback && pool && pool->sessionCallback &&
856 PyCallable_Check(pool->sessionCallback)) {
857 tempObj = PyObject_CallFunctionObjArgs(pool->sessionCallback,
858 (PyObject*) conn, tagObj, NULL);
859 if (!tempObj)
860 return -1;
861 Py_DECREF(tempObj);
862 }
863
864 return 0;
865 }
866
867
868 //-----------------------------------------------------------------------------
869 // cxoConnection_free()
870 // Deallocate the connection, disconnecting from the database if necessary.
871 //-----------------------------------------------------------------------------
872 static void cxoConnection_free(cxoConnection *conn)
873 {
874 if (conn->handle) {
875 Py_BEGIN_ALLOW_THREADS
876 dpiConn_release(conn->handle);
877 Py_END_ALLOW_THREADS
878 conn->handle = NULL;
879 }
880 Py_CLEAR(conn->sessionPool);
881 Py_CLEAR(conn->username);
882 Py_CLEAR(conn->dsn);
883 Py_CLEAR(conn->version);
884 Py_CLEAR(conn->inputTypeHandler);
885 Py_CLEAR(conn->outputTypeHandler);
886 Py_CLEAR(conn->tag);
887 Py_TYPE(conn)->tp_free((PyObject*) conn);
888 }
889
890
891 //-----------------------------------------------------------------------------
892 // cxoConnection_repr()
893 // Return a string representation of the connection.
894 //-----------------------------------------------------------------------------
895 static PyObject *cxoConnection_repr(cxoConnection *connection)
896 {
897 PyObject *module, *name, *result;
898
899 if (cxoUtils_getModuleAndName(Py_TYPE(connection), &module, &name) < 0)
900 return NULL;
901 if (connection->username && connection->username != Py_None &&
902 connection->dsn && connection->dsn != Py_None) {
903 result = cxoUtils_formatString("<%s.%s to %s@%s>",
904 PyTuple_Pack(4, module, name, connection->username,
905 connection->dsn));
906 } else if (connection->username && connection->username != Py_None) {
907 result = cxoUtils_formatString("<%s.%s to user %s@local>",
908 PyTuple_Pack(3, module, name, connection->username));
909 } else {
910 result = cxoUtils_formatString("<%s.%s to externally identified user>",
911 PyTuple_Pack(2, module, name));
912 }
913 Py_DECREF(module);
914 Py_DECREF(name);
915 return result;
916 }
917
918
919 //-----------------------------------------------------------------------------
920 // cxoConnection_getStmtCacheSize()
921 // Return the Oracle statement cache size.
922 //-----------------------------------------------------------------------------
923 static PyObject *cxoConnection_getStmtCacheSize(cxoConnection* conn, void* arg)
924 {
925 uint32_t cacheSize;
926
927 if (cxoConnection_isConnected(conn) < 0)
928 return NULL;
929 if (dpiConn_getStmtCacheSize(conn->handle, &cacheSize) < 0)
930 return cxoError_raiseAndReturnNull();
931 return PyInt_FromLong(cacheSize);
932 }
933
934
935 //-----------------------------------------------------------------------------
936 // cxoConnection_setStmtCacheSize()
937 // Set the Oracle statement cache size.
938 //-----------------------------------------------------------------------------
939 static int cxoConnection_setStmtCacheSize(cxoConnection* conn, PyObject *value,
940 void* arg)
941 {
942 uint32_t cacheSize;
943
944 if (cxoConnection_isConnected(conn) < 0)
945 return -1;
946 if (!PyInt_Check(value)) {
947 PyErr_SetString(PyExc_TypeError, "value must be an integer");
948 return -1;
949 }
950 cacheSize = (uint32_t) PyInt_AsLong(value);
951 if (dpiConn_setStmtCacheSize(conn->handle, cacheSize) < 0)
952 return cxoError_raiseAndReturnInt();
953 return 0;
954 }
955
956
957 //-----------------------------------------------------------------------------
958 // cxoConnection_getCallTimeout()
959 // Return the call timeout (in milliseconds) for round-trips performed with
960 // this connection.
961 //-----------------------------------------------------------------------------
962 static PyObject *cxoConnection_getCallTimeout(cxoConnection* conn, void* arg)
963 {
964 uint32_t callTimeout;
965
966 if (cxoConnection_isConnected(conn) < 0)
967 return NULL;
968 if (dpiConn_getCallTimeout(conn->handle, &callTimeout) < 0)
969 return cxoError_raiseAndReturnNull();
970 return PyInt_FromLong(callTimeout);
971 }
972
973
974 //-----------------------------------------------------------------------------
975 // cxoConnection_setCallTimeout()
976 // Set the call timeout (in milliseconds) for round-trips performed with this
977 // connection.
978 //-----------------------------------------------------------------------------
979 static int cxoConnection_setCallTimeout(cxoConnection* conn, PyObject *value,
980 void* arg)
981 {
982 uint32_t callTimeout;
983
984 if (cxoConnection_isConnected(conn) < 0)
985 return -1;
986 callTimeout = (uint32_t) PyLong_AsLong(value);
987 if (PyErr_Occurred())
988 return -1;
989 if (dpiConn_setCallTimeout(conn->handle, callTimeout) < 0)
990 return cxoError_raiseAndReturnInt();
991 return 0;
992 }
993
994
995 //-----------------------------------------------------------------------------
996 // cxoConnection_getType()
997 // Return a type object given its name.
998 //-----------------------------------------------------------------------------
999 static PyObject *cxoConnection_getType(cxoConnection *conn, PyObject *nameObj)
1000 {
1001 return (PyObject*) cxoObjectType_newByName(conn, nameObj);
1002 }
1003
1004
1005 //-----------------------------------------------------------------------------
1006 // cxoConnection_createLob()
1007 // Create a new temporary LOB and return it.
1008 //-----------------------------------------------------------------------------
1009 static PyObject *cxoConnection_createLob(cxoConnection *conn,
1010 PyObject *lobType)
1011 {
1012 dpiOracleTypeNum oracleTypeNum;
1013 dpiLob *handle;
1014 PyObject *lob;
1015
1016 // verify connection is open
1017 if (cxoConnection_isConnected(conn) < 0)
1018 return NULL;
1019
1020 // verify the LOB type
1021 if (lobType == (PyObject*) &cxoPyTypeClobVar)
1022 oracleTypeNum = DPI_ORACLE_TYPE_CLOB;
1023 else if (lobType == (PyObject*) &cxoPyTypeBlobVar)
1024 oracleTypeNum = DPI_ORACLE_TYPE_BLOB;
1025 else if (lobType == (PyObject*) &cxoPyTypeNclobVar)
1026 oracleTypeNum = DPI_ORACLE_TYPE_NCLOB;
1027 else {
1028 PyErr_SetString(PyExc_TypeError,
1029 "parameter should be one of cx_Oracle.CLOB, cx_Oracle.BLOB "
1030 "or cx_Oracle.NCLOB");
1031 return NULL;
1032 }
1033
1034 // create a temporary LOB
1035 if (dpiConn_newTempLob(conn->handle, oracleTypeNum, &handle) < 0)
1036 return cxoError_raiseAndReturnNull();
1037 lob = cxoLob_new(conn, oracleTypeNum, handle);
1038 if (!lob)
1039 dpiLob_release(handle);
1040 return lob;
1041 }
1042
1043
1044 //-----------------------------------------------------------------------------
1045 // cxoConnection_getVersion()
1046 // Retrieve the version of the database and return it. Note that this
1047 // function also places the result in the associated dictionary so it is only
1048 // calculated once.
1049 //-----------------------------------------------------------------------------
1050 static PyObject *cxoConnection_getVersion(cxoConnection *conn, void *unused)
1051 {
1052 uint32_t releaseStringLength;
1053 dpiVersionInfo versionInfo;
1054 const char *releaseString;
1055 char buffer[25];
1056 int status;
1057
1058 Py_BEGIN_ALLOW_THREADS
1059 status = dpiConn_getServerVersion(conn->handle, &releaseString,
1060 &releaseStringLength, &versionInfo);
1061 Py_END_ALLOW_THREADS
1062 if (status < 0)
1063 return cxoError_raiseAndReturnNull();
1064 snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.%d", versionInfo.versionNum,
1065 versionInfo.releaseNum, versionInfo.updateNum,
1066 versionInfo.portReleaseNum, versionInfo.portUpdateNum);
1067 return cxoPyString_fromAscii(buffer);
1068 }
1069
1070
1071 //-----------------------------------------------------------------------------
1072 // cxoConnection_getEncoding()
1073 // Return the encoding associated with the environment of the connection.
1074 //-----------------------------------------------------------------------------
1075 static PyObject *cxoConnection_getEncoding(cxoConnection *conn, void *unused)
1076 {
1077 return cxoPyString_fromAscii(conn->encodingInfo.encoding);
1078 }
1079
1080
1081 //-----------------------------------------------------------------------------
1082 // cxoConnection_getLTXID()
1083 // Return the logical transaction id used with Transaction Guard.
1084 //-----------------------------------------------------------------------------
1085 static PyObject *cxoConnection_getLTXID(cxoConnection *conn, void *unused)
1086 {
1087 uint32_t ltxidLength;
1088 const char *ltxid;
1089
1090 if (cxoConnection_isConnected(conn) < 0)
1091 return NULL;
1092 if (dpiConn_getLTXID(conn->handle, &ltxid, &ltxidLength) < 0)
1093 return cxoError_raiseAndReturnNull();
1094 return PyBytes_FromStringAndSize(ltxid, ltxidLength);
1095 }
1096
1097
1098 //-----------------------------------------------------------------------------
1099 // cxoConnection_getHandle()
1100 // Return the OCI handle used by the connection.
1101 //-----------------------------------------------------------------------------
1102 static PyObject *cxoConnection_getHandle(cxoConnection *conn, void *unused)
1103 {
1104 void *handle;
1105
1106 if (cxoConnection_isConnected(conn) < 0)
1107 return NULL;
1108 if (dpiConn_getHandle(conn->handle, &handle) < 0)
1109 return cxoError_raiseAndReturnNull();
1110 return PyLong_FromUnsignedLongLong((unsigned long long) handle);
1111 }
1112
1113
1114 //-----------------------------------------------------------------------------
1115 // cxoConnection_getNationalEncoding()
1116 // Return the national encoding associated with the environment of the
1117 // connection.
1118 //-----------------------------------------------------------------------------
1119 static PyObject *cxoConnection_getNationalEncoding(cxoConnection *conn,
1120 void *unused)
1121 {
1122 return cxoPyString_fromAscii(conn->encodingInfo.nencoding);
1123 }
1124
1125
1126 //-----------------------------------------------------------------------------
1127 // cxoConnection_getMaxBytesPerCharacter()
1128 // Return the maximum number of bytes per character.
1129 //-----------------------------------------------------------------------------
1130 static PyObject *cxoConnection_getMaxBytesPerCharacter(cxoConnection *conn,
1131 void *unused)
1132 {
1133 return PyInt_FromLong(conn->encodingInfo.maxBytesPerCharacter);
1134 }
1135
1136
1137 //-----------------------------------------------------------------------------
1138 // cxoConnection_close()
1139 // Close the connection, disconnecting from the database.
1140 //-----------------------------------------------------------------------------
1141 static PyObject *cxoConnection_close(cxoConnection *conn, PyObject *args)
1142 {
1143 cxoBuffer tagBuffer;
1144 uint32_t mode;
1145 int status;
1146
1147 if (cxoConnection_isConnected(conn) < 0)
1148 return NULL;
1149 if (cxoBuffer_fromObject(&tagBuffer, conn->tag,
1150 conn->encodingInfo.encoding) < 0)
1151 return NULL;
1152 mode = DPI_MODE_CONN_CLOSE_DEFAULT;
1153 if (conn->tag && conn->tag != Py_None)
1154 mode |= DPI_MODE_CONN_CLOSE_RETAG;
1155 Py_BEGIN_ALLOW_THREADS
1156 status = dpiConn_close(conn->handle, mode, (char*) tagBuffer.ptr,
1157 tagBuffer.size);
1158 Py_END_ALLOW_THREADS
1159 cxoBuffer_clear(&tagBuffer);
1160 if (status < 0)
1161 return cxoError_raiseAndReturnNull();
1162
1163 Py_RETURN_NONE;
1164 }
1165
1166
1167 //-----------------------------------------------------------------------------
1168 // cxoConnection_commit()
1169 // Commit the transaction on the connection.
1170 //-----------------------------------------------------------------------------
1171 static PyObject *cxoConnection_commit(cxoConnection *conn, PyObject *args)
1172 {
1173 int status;
1174
1175 if (cxoConnection_isConnected(conn) < 0)
1176 return NULL;
1177 Py_BEGIN_ALLOW_THREADS
1178 status = dpiConn_commit(conn->handle);
1179 Py_END_ALLOW_THREADS
1180 if (status < 0)
1181 return cxoError_raiseAndReturnNull();
1182
1183 Py_RETURN_NONE;
1184 }
1185
1186
1187 //-----------------------------------------------------------------------------
1188 // cxoConnection_begin()
1189 // Begin a new transaction on the connection.
1190 //-----------------------------------------------------------------------------
1191 static PyObject *cxoConnection_begin(cxoConnection *conn, PyObject *args)
1192 {
1193 uint32_t transactionIdLength, branchIdLength;
1194 const char *transactionId, *branchId;
1195 int formatId, status;
1196
1197 // parse the arguments
1198 formatId = -1;
1199 transactionId = branchId = NULL;
1200 transactionIdLength = branchIdLength = 0;
1201 if (!PyArg_ParseTuple(args, "|is#s#", &formatId, &transactionId,
1202 &transactionIdLength, &branchId, &branchIdLength))
1203 return NULL;
1204
1205 // make sure we are actually connected
1206 if (cxoConnection_isConnected(conn) < 0)
1207 return NULL;
1208
1209 // begin the distributed transaction
1210 Py_BEGIN_ALLOW_THREADS
1211 status = dpiConn_beginDistribTrans(conn->handle, formatId, transactionId,
1212 transactionIdLength, branchId, branchIdLength);
1213 Py_END_ALLOW_THREADS
1214 if (status < 0)
1215 return cxoError_raiseAndReturnNull();
1216
1217 Py_RETURN_NONE;
1218 }
1219
1220
1221 //-----------------------------------------------------------------------------
1222 // cxoConnection_prepare()
1223 // Commit the transaction on the connection.
1224 //-----------------------------------------------------------------------------
1225 static PyObject *cxoConnection_prepare(cxoConnection *conn, PyObject *args)
1226 {
1227 int status, commitNeeded;
1228
1229 // make sure we are actually connected
1230 if (cxoConnection_isConnected(conn) < 0)
1231 return NULL;
1232
1233 // perform the prepare
1234 Py_BEGIN_ALLOW_THREADS
1235 status = dpiConn_prepareDistribTrans(conn->handle, &commitNeeded);
1236 Py_END_ALLOW_THREADS
1237 if (status < 0)
1238 return cxoError_raiseAndReturnNull();
1239
1240 // return whether a commit is needed in order to allow for avoiding the
1241 // call to commit() which will fail with ORA-24756 (transaction does not
1242 // exist)
1243 return PyBool_FromLong(commitNeeded);
1244 }
1245
1246
1247 //-----------------------------------------------------------------------------
1248 // cxoConnection_rollback()
1249 // Rollback the transaction on the connection.
1250 //-----------------------------------------------------------------------------
1251 static PyObject *cxoConnection_rollback(cxoConnection *conn, PyObject *args)
1252 {
1253 int status;
1254
1255 if (cxoConnection_isConnected(conn) < 0)
1256 return NULL;
1257 Py_BEGIN_ALLOW_THREADS
1258 status = dpiConn_rollback(conn->handle);
1259 Py_END_ALLOW_THREADS
1260 if (status < 0)
1261 return cxoError_raiseAndReturnNull();
1262
1263 Py_RETURN_NONE;
1264 }
1265
1266
1267 //-----------------------------------------------------------------------------
1268 // cxoConnection_newCursor()
1269 // Create a new cursor (statement) referencing the connection.
1270 //-----------------------------------------------------------------------------
1271 static PyObject *cxoConnection_newCursor(cxoConnection *conn, PyObject *args,
1272 PyObject *keywordArgs)
1273 {
1274 PyObject *createArgs, *result, *arg;
1275 Py_ssize_t numArgs = 0, i;
1276
1277 if (args)
1278 numArgs = PyTuple_GET_SIZE(args);
1279 createArgs = PyTuple_New(1 + numArgs);
1280 if (!createArgs)
1281 return NULL;
1282 Py_INCREF(conn);
1283 PyTuple_SET_ITEM(createArgs, 0, (PyObject*) conn);
1284 for (i = 0; i < numArgs; i++) {
1285 arg = PyTuple_GET_ITEM(args, i);
1286 Py_INCREF(arg);
1287 PyTuple_SET_ITEM(createArgs, i + 1, arg);
1288 }
1289 result = PyObject_Call( (PyObject*) &cxoPyTypeCursor, createArgs,
1290 keywordArgs);
1291 Py_DECREF(createArgs);
1292 return result;
1293 }
1294
1295
1296 //-----------------------------------------------------------------------------
1297 // cxoConnection_cancel()
1298 // Cause Oracle to issue an immediate (asynchronous) abort of any currently
1299 // executing statement.
1300 //-----------------------------------------------------------------------------
1301 static PyObject *cxoConnection_cancel(cxoConnection *conn, PyObject *args)
1302 {
1303 if (cxoConnection_isConnected(conn) < 0)
1304 return NULL;
1305 if (dpiConn_breakExecution(conn->handle) < 0)
1306 return cxoError_raiseAndReturnNull();
1307
1308 Py_RETURN_NONE;
1309 }
1310
1311
1312 //-----------------------------------------------------------------------------
1313 // cxoConnection_newEnqueueOptions()
1314 // Creates a new enqueue options object and returns it.
1315 //-----------------------------------------------------------------------------
1316 static PyObject *cxoConnection_newEnqueueOptions(cxoConnection *conn,
1317 PyObject *args)
1318 {
1319 return (PyObject*) cxoEnqOptions_new(conn);
1320 }
1321
1322
1323 //-----------------------------------------------------------------------------
1324 // cxoConnection_newDequeueOptions()
1325 // Creates a new dequeue options object and returns it.
1326 //-----------------------------------------------------------------------------
1327 static PyObject *cxoConnection_newDequeueOptions(cxoConnection *conn,
1328 PyObject *args)
1329 {
1330 return (PyObject*) cxoDeqOptions_new(conn);
1331 }
1332
1333
1334 //-----------------------------------------------------------------------------
1335 // cxoConnection_newMessageProperties()
1336 // Creates a new message properties object and returns it.
1337 //-----------------------------------------------------------------------------
1338 static PyObject *cxoConnection_newMessageProperties(cxoConnection *conn,
1339 PyObject *args)
1340 {
1341 return (PyObject*) cxoMsgProps_new(conn);
1342 }
1343
1344
1345 //-----------------------------------------------------------------------------
1346 // cxoConnection_dequeue()
1347 // Dequeues a message using Advanced Queuing capabilities. The message ID is
1348 // returned if a message is available or None if no message is available.
1349 //-----------------------------------------------------------------------------
1350 static PyObject *cxoConnection_dequeue(cxoConnection *conn, PyObject* args,
1351 PyObject* keywordArgs)
1352 {
1353 static char *keywordList[] = { "name", "options", "msgproperties",
1354 "payload", NULL };
1355 cxoMsgProps *propertiesObj;
1356 const char *messageIdValue;
1357 cxoDeqOptions *optionsObj;
1358 uint32_t messageIdLength;
1359 cxoObject *payloadObj;
1360 cxoBuffer nameBuffer;
1361 PyObject *nameObj;
1362 int status;
1363
1364 // parse arguments
1365 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!O!O!", keywordList,
1366 &nameObj, &cxoPyTypeDeqOptions, &optionsObj, &cxoPyTypeMsgProps,
1367 &propertiesObj, &cxoPyTypeObject, &payloadObj))
1368 return NULL;
1369 if (cxoBuffer_fromObject(&nameBuffer, nameObj,
1370 conn->encodingInfo.encoding) < 0)
1371 return NULL;
1372
1373 // dequeue payload
1374 status = dpiConn_deqObject(conn->handle, nameBuffer.ptr, nameBuffer.size,
1375 optionsObj->handle, propertiesObj->handle, payloadObj->handle,
1376 &messageIdValue, &messageIdLength);
1377 cxoBuffer_clear(&nameBuffer);
1378 if (status < 0)
1379 return cxoError_raiseAndReturnNull();
1380
1381 // return message id
1382 if (!messageIdValue)
1383 Py_RETURN_NONE;
1384 return PyBytes_FromStringAndSize(messageIdValue, messageIdLength);
1385 }
1386
1387
1388 //-----------------------------------------------------------------------------
1389 // cxoConnection_enqueue()
1390 // Enqueues a message using Advanced Queuing capabilities. The message ID is
1391 // returned.
1392 //-----------------------------------------------------------------------------
1393 static PyObject *cxoConnection_enqueue(cxoConnection *conn, PyObject* args,
1394 PyObject* keywordArgs)
1395 {
1396 static char *keywordList[] = { "name", "options", "msgproperties",
1397 "payload", NULL };
1398 cxoMsgProps *propertiesObj;
1399 const char *messageIdValue;
1400 cxoEnqOptions *optionsObj;
1401 uint32_t messageIdLength;
1402 cxoObject *payloadObj;
1403 cxoBuffer nameBuffer;
1404 PyObject *nameObj;
1405 int status;
1406
1407 // parse arguments
1408 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!O!O!", keywordList,
1409 &nameObj, &cxoPyTypeEnqOptions, &optionsObj, &cxoPyTypeMsgProps,
1410 &propertiesObj, &cxoPyTypeObject, &payloadObj))
1411 return NULL;
1412 if (cxoBuffer_fromObject(&nameBuffer, nameObj,
1413 conn->encodingInfo.encoding) < 0)
1414 return NULL;
1415
1416 // enqueue payload
1417 status = dpiConn_enqObject(conn->handle, nameBuffer.ptr, nameBuffer.size,
1418 optionsObj->handle, propertiesObj->handle, payloadObj->handle,
1419 &messageIdValue, &messageIdLength);
1420 cxoBuffer_clear(&nameBuffer);
1421 if (status < 0)
1422 return cxoError_raiseAndReturnNull();
1423
1424 // return message id
1425 return PyBytes_FromStringAndSize(messageIdValue, messageIdLength);
1426 }
1427
1428
1429 //-----------------------------------------------------------------------------
1430 // cxoConnection_contextManagerEnter()
1431 // Called when the connection is used as a context manager and simply returns
1432 // itconn as a convenience to the caller.
1433 //-----------------------------------------------------------------------------
1434 static PyObject *cxoConnection_contextManagerEnter(cxoConnection *conn,
1435 PyObject* args)
1436 {
1437 Py_INCREF(conn);
1438 return (PyObject*) conn;
1439 }
1440
1441
1442 //-----------------------------------------------------------------------------
1443 // cxoConnection_contextManagerExit()
1444 // Called when the connection is used as a context manager and if any
1445 // exception a rollback takes place; otherwise, a commit takes place.
1446 //-----------------------------------------------------------------------------
1447 static PyObject *cxoConnection_contextManagerExit(cxoConnection *conn,
1448 PyObject* args)
1449 {
1450 PyObject *excType, *excValue, *excTraceback, *result;
1451
1452 if (!PyArg_ParseTuple(args, "OOO", &excType, &excValue, &excTraceback))
1453 return NULL;
1454 result = cxoConnection_close(conn, NULL);
1455 if (!result)
1456 return NULL;
1457 Py_DECREF(result);
1458
1459 Py_INCREF(Py_False);
1460 return Py_False;
1461 }
1462
1463
1464 //-----------------------------------------------------------------------------
1465 // cxoConnection_ping()
1466 // Makes a round trip call to the server to confirm that the connection and
1467 // server are active.
1468 //-----------------------------------------------------------------------------
1469 static PyObject *cxoConnection_ping(cxoConnection *conn, PyObject* args)
1470 {
1471 int status;
1472
1473 if (cxoConnection_isConnected(conn) < 0)
1474 return NULL;
1475 Py_BEGIN_ALLOW_THREADS
1476 status = dpiConn_ping(conn->handle);
1477 Py_END_ALLOW_THREADS
1478 if (status < 0)
1479 return cxoError_raiseAndReturnNull();
1480
1481 Py_RETURN_NONE;
1482 }
1483
1484
1485 //-----------------------------------------------------------------------------
1486 // cxoConnection_shutdown()
1487 // Shuts down the database. Note that this must be done in two phases except
1488 // in the situation where the instance is aborted.
1489 //-----------------------------------------------------------------------------
1490 static PyObject *cxoConnection_shutdown(cxoConnection *conn, PyObject* args,
1491 PyObject* keywordArgs)
1492 {
1493 static char *keywordList[] = { "mode", NULL };
1494 dpiShutdownMode mode;
1495
1496 // parse arguments
1497 mode = DPI_MODE_SHUTDOWN_DEFAULT;
1498 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
1499 &mode))
1500 return NULL;
1501
1502 // make sure we are actually connected
1503 if (cxoConnection_isConnected(conn) < 0)
1504 return NULL;
1505
1506 // perform the work
1507 if (dpiConn_shutdownDatabase(conn->handle, mode) < 0)
1508 return cxoError_raiseAndReturnNull();
1509
1510 Py_RETURN_NONE;
1511 }
1512
1513
1514 //-----------------------------------------------------------------------------
1515 // cxoConnection_startup()
1516 // Starts up the database, equivalent to "startup nomount" in SQL*Plus.
1517 //-----------------------------------------------------------------------------
1518 static PyObject *cxoConnection_startup(cxoConnection *conn, PyObject* args,
1519 PyObject* keywordArgs)
1520 {
1521 static char *keywordList[] = { "force", "restrict", NULL };
1522 PyObject *forceObj, *restrictObj;
1523 dpiStartupMode mode;
1524 int temp;
1525
1526 // parse arguments
1527 forceObj = restrictObj = NULL;
1528 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|OO", keywordList,
1529 &forceObj, &restrictObj))
1530 return NULL;
1531
1532 // set the flags to use during startup
1533 mode = DPI_MODE_STARTUP_DEFAULT;
1534 if (cxoUtils_getBooleanValue(forceObj, 0, &temp) < 0)
1535 return NULL;
1536 if (temp)
1537 mode |= DPI_MODE_STARTUP_FORCE;
1538 if (cxoUtils_getBooleanValue(restrictObj, 0, &temp) < 0)
1539 return NULL;
1540 if (temp)
1541 mode |= DPI_MODE_STARTUP_RESTRICT;
1542
1543 // make sure we are actually connected
1544 if (cxoConnection_isConnected(conn) < 0)
1545 return NULL;
1546
1547 // perform the work
1548 if (dpiConn_startupDatabase(conn->handle, mode) < 0)
1549 return cxoError_raiseAndReturnNull();
1550
1551 Py_RETURN_NONE;
1552 }
1553
1554
1555 //-----------------------------------------------------------------------------
1556 // cxoConnection_subscribe()
1557 // Create a subscription to events that take place in the database.
1558 //-----------------------------------------------------------------------------
1559 static PyObject *cxoConnection_subscribe(cxoConnection *conn, PyObject* args,
1560 PyObject* keywordArgs)
1561 {
1562 static char *keywordList[] = { "namespace", "protocol", "callback",
1563 "timeout", "operations", "port", "qos", "ipAddress",
1564 "groupingClass", "groupingValue", "groupingType", "name", NULL };
1565 PyObject *callback, *ipAddress, *name;
1566 cxoBuffer ipAddressBuffer, nameBuffer;
1567 dpiSubscrCreateParams params;
1568 cxoSubscr *subscr;
1569
1570 // get default values for subscription parameters
1571 if (dpiContext_initSubscrCreateParams(cxoDpiContext, &params) < 0)
1572 return cxoError_raiseAndReturnNull();
1573
1574 // validate parameters
1575 callback = name = ipAddress = NULL;
1576 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|IIOIIIIObIbO",
1577 keywordList, &params.subscrNamespace, &params.protocol, &callback,
1578 &params.timeout, &params.operations, &params.portNumber,
1579 &params.qos, &ipAddress, &params.groupingClass,
1580 &params.groupingValue, &params.groupingType, &name))
1581 return NULL;
1582
1583 // populate IP address in parameters, if applicable
1584 cxoBuffer_init(&ipAddressBuffer);
1585 if (ipAddress) {
1586 if (cxoBuffer_fromObject(&ipAddressBuffer, ipAddress,
1587 conn->encodingInfo.encoding) < 0)
1588 return NULL;
1589 params.ipAddress = ipAddressBuffer.ptr;
1590 params.ipAddressLength = ipAddressBuffer.size;
1591 }
1592
1593 // populate name in parameters, if applicable
1594 cxoBuffer_init(&nameBuffer);
1595 if (name) {
1596 if (cxoBuffer_fromObject(&nameBuffer, name,
1597 conn->encodingInfo.encoding) < 0) {
1598 cxoBuffer_clear(&ipAddressBuffer);
1599 return NULL;
1600 }
1601 params.name = nameBuffer.ptr;
1602 params.nameLength = nameBuffer.size;
1603 }
1604
1605 // create Python subscription object
1606 subscr = (cxoSubscr*) cxoPyTypeSubscr.tp_alloc(&cxoPyTypeSubscr, 0);
1607 if (!subscr) {
1608 cxoBuffer_clear(&ipAddressBuffer);
1609 cxoBuffer_clear(&nameBuffer);
1610 return NULL;
1611 }
1612 Py_INCREF(conn);
1613 subscr->connection = conn;
1614 Py_XINCREF(callback);
1615 subscr->callback = callback;
1616 subscr->namespace = params.subscrNamespace;
1617 subscr->protocol = params.protocol;
1618 Py_XINCREF(ipAddress);
1619 subscr->ipAddress = ipAddress;
1620 Py_XINCREF(name);
1621 subscr->name = name;
1622 subscr->port = params.portNumber;
1623 subscr->timeout = params.timeout;
1624 subscr->operations = params.operations;
1625 subscr->qos = params.qos;
1626 subscr->groupingClass = params.groupingClass;
1627 subscr->groupingValue = params.groupingValue;
1628 subscr->groupingType = params.groupingType;
1629
1630 // populate callback in parameters, if applicable
1631 if (callback) {
1632 params.callback = (dpiSubscrCallback) cxoSubscr_callback;
1633 params.callbackContext = subscr;
1634 }
1635
1636 // create ODPI-C subscription
1637 if (dpiConn_subscribe(conn->handle, &params, &subscr->handle) < 0) {
1638 cxoError_raiseAndReturnNull();
1639 cxoBuffer_clear(&ipAddressBuffer);
1640 cxoBuffer_clear(&nameBuffer);
1641 Py_DECREF(subscr);
1642 return NULL;
1643 }
1644 cxoBuffer_clear(&ipAddressBuffer);
1645 cxoBuffer_clear(&nameBuffer);
1646
1647 return (PyObject*) subscr;
1648 }
1649
1650
1651 //-----------------------------------------------------------------------------
1652 // cxoConnection_unsubscribe()
1653 // Destroy a subscription to events that take place in the database.
1654 //-----------------------------------------------------------------------------
1655 static PyObject *cxoConnection_unsubscribe(cxoConnection *conn, PyObject* args,
1656 PyObject* keywordArgs)
1657 {
1658 static char *keywordList[] = { "subscription", NULL };
1659 PyObject *subscrObj;
1660 cxoSubscr *subscr;
1661 int status;
1662
1663 // validate parameters
1664 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!", keywordList,
1665 &cxoPyTypeSubscr, &subscrObj))
1666 return NULL;
1667
1668 // destroy ODPI-C subscription
1669 subscr = (cxoSubscr*) subscrObj;
1670 Py_BEGIN_ALLOW_THREADS
1671 status = dpiConn_unsubscribe(conn->handle, subscr->handle);
1672 Py_END_ALLOW_THREADS
1673 if (status < 0)
1674 return cxoError_raiseAndReturnNull();
1675 subscr->handle = NULL;
1676
1677 Py_RETURN_NONE;
1678 }
1679
1680
1681 //-----------------------------------------------------------------------------
1682 // cxoConnection_commit()
1683 // Commit the transaction on the connection.
1684 //-----------------------------------------------------------------------------
1685 static PyObject *cxoConnection_getSodaDatabase(cxoConnection *conn,
1686 PyObject *args)
1687 {
1688 if (cxoConnection_isConnected(conn) < 0)
1689 return NULL;
1690 return (PyObject*) cxoSodaDatabase_new(conn);
1691 }
1692
1693
1694 //-----------------------------------------------------------------------------
1695 // cxoConnection_getCurrentSchema()
1696 // Return the current schema associated with the connection.
1697 //-----------------------------------------------------------------------------
1698 static PyObject *cxoConnection_getCurrentSchema(cxoConnection* conn,
1699 void* unused)
1700 {
1701 return cxoConnection_getAttrText(conn, dpiConn_getCurrentSchema);
1702 }
1703
1704
1705 //-----------------------------------------------------------------------------
1706 // cxoConnection_getEdition()
1707 // Return the edition associated with the connection.
1708 //-----------------------------------------------------------------------------
1709 static PyObject *cxoConnection_getEdition(cxoConnection* conn, void* unused)
1710 {
1711 return cxoConnection_getAttrText(conn, dpiConn_getEdition);
1712 }
1713
1714
1715 //-----------------------------------------------------------------------------
1716 // cxoConnection_getExternalName()
1717 // Return the external name associated with the connection.
1718 //-----------------------------------------------------------------------------
1719 static PyObject *cxoConnection_getExternalName(cxoConnection* conn,
1720 void* unused)
1721 {
1722 return cxoConnection_getAttrText(conn, dpiConn_getExternalName);
1723 }
1724
1725
1726 //-----------------------------------------------------------------------------
1727 // cxoConnection_getInternalName()
1728 // Return the internal name associated with the connection.
1729 //-----------------------------------------------------------------------------
1730 static PyObject *cxoConnection_getInternalName(cxoConnection* conn,
1731 void* unused)
1732 {
1733 return cxoConnection_getAttrText(conn, dpiConn_getInternalName);
1734 }
1735
1736
1737 //-----------------------------------------------------------------------------
1738 // cxoConnection_getException()
1739 // Return the requested exception.
1740 //-----------------------------------------------------------------------------
1741 static PyObject *cxoConnection_getException(cxoConnection *conn, void *arg)
1742 {
1743 PyObject *exc = * (PyObject**) arg;
1744
1745 Py_INCREF(exc);
1746 return exc;
1747 }
1748
1749
1750 //-----------------------------------------------------------------------------
1751 // cxoConnection_setAction()
1752 // Set the action associated with the connection.
1753 //-----------------------------------------------------------------------------
1754 static int cxoConnection_setAction(cxoConnection* conn, PyObject *value,
1755 void* unused)
1756 {
1757 return cxoConnection_setAttrText(conn, value, dpiConn_setAction);
1758 }
1759
1760
1761 //-----------------------------------------------------------------------------
1762 // cxoConnection_setClientIdentifier()
1763 // Set the client identifier associated with the connection.
1764 //-----------------------------------------------------------------------------
1765 static int cxoConnection_setClientIdentifier(cxoConnection* conn,
1766 PyObject *value, void* unused)
1767 {
1768 return cxoConnection_setAttrText(conn, value, dpiConn_setClientIdentifier);
1769 }
1770
1771
1772 //-----------------------------------------------------------------------------
1773 // cxoConnection_setClientInfo()
1774 // Set the client info associated with the connection.
1775 //-----------------------------------------------------------------------------
1776 static int cxoConnection_setClientInfo(cxoConnection* conn, PyObject *value,
1777 void* unused)
1778 {
1779 return cxoConnection_setAttrText(conn, value, dpiConn_setClientInfo);
1780 }
1781
1782
1783 //-----------------------------------------------------------------------------
1784 // cxoConnection_setCurrentSchema()
1785 // Set the current schema associated with the connection.
1786 //-----------------------------------------------------------------------------
1787 static int cxoConnection_setCurrentSchema(cxoConnection* conn, PyObject *value,
1788 void* unused)
1789 {
1790 return cxoConnection_setAttrText(conn, value, dpiConn_setCurrentSchema);
1791 }
1792
1793
1794 //-----------------------------------------------------------------------------
1795 // cxoConnection_setDbOp()
1796 // Set the database operation associated with the connection.
1797 //-----------------------------------------------------------------------------
1798 static int cxoConnection_setDbOp(cxoConnection* conn, PyObject *value,
1799 void* unused)
1800 {
1801 return cxoConnection_setAttrText(conn, value, dpiConn_setDbOp);
1802 }
1803
1804
1805 //-----------------------------------------------------------------------------
1806 // cxoConnection_setExternalName()
1807 // Set the external name associated with the connection.
1808 //-----------------------------------------------------------------------------
1809 static int cxoConnection_setExternalName(cxoConnection* conn, PyObject *value,
1810 void* unused)
1811 {
1812 return cxoConnection_setAttrText(conn, value, dpiConn_setExternalName);
1813 }
1814
1815
1816 //-----------------------------------------------------------------------------
1817 // cxoConnection_setInternalName()
1818 // Set the internal name associated with the connection.
1819 //-----------------------------------------------------------------------------
1820 static int cxoConnection_setInternalName(cxoConnection* conn, PyObject *value,
1821 void* unused)
1822 {
1823 return cxoConnection_setAttrText(conn, value, dpiConn_setInternalName);
1824 }
1825
1826
1827 //-----------------------------------------------------------------------------
1828 // cxoConnection_setModule()
1829 // Set the module associated with the connection.
1830 //-----------------------------------------------------------------------------
1831 static int cxoConnection_setModule(cxoConnection* conn, PyObject *value,
1832 void* unused)
1833 {
1834 return cxoConnection_setAttrText(conn, value, dpiConn_setModule);
1835 }
1836
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // functions for the Python type "Cursor"
18 //-----------------------------------------------------------------------------
19 static void cxoCursor_free(cxoCursor*);
20 static PyObject *cxoCursor_getIter(cxoCursor*);
21 static PyObject *cxoCursor_getNext(cxoCursor*);
22 static PyObject *cxoCursor_close(cxoCursor*, PyObject*);
23 static PyObject *cxoCursor_callFunc(cxoCursor*, PyObject*, PyObject*);
24 static PyObject *cxoCursor_callProc(cxoCursor*, PyObject*, PyObject*);
25 static PyObject *cxoCursor_execute(cxoCursor*, PyObject*, PyObject*);
26 static PyObject *cxoCursor_executeMany(cxoCursor*, PyObject*, PyObject*);
27 static PyObject *cxoCursor_executeManyPrepared(cxoCursor*, PyObject*);
28 static PyObject *cxoCursor_fetchOne(cxoCursor*, PyObject*);
29 static PyObject *cxoCursor_fetchMany(cxoCursor*, PyObject*, PyObject*);
30 static PyObject *cxoCursor_fetchAll(cxoCursor*, PyObject*);
31 static PyObject *cxoCursor_fetchRaw(cxoCursor*, PyObject*, PyObject*);
32 static PyObject *cxoCursor_parse(cxoCursor*, PyObject*);
33 static PyObject *cxoCursor_prepare(cxoCursor*, PyObject*);
34 static PyObject *cxoCursor_scroll(cxoCursor*, PyObject*, PyObject*);
35 static PyObject *cxoCursor_setInputSizes(cxoCursor*, PyObject*, PyObject*);
36 static PyObject *cxoCursor_setOutputSize(cxoCursor*, PyObject*);
37 static PyObject *cxoCursor_var(cxoCursor*, PyObject*, PyObject*);
38 static PyObject *cxoCursor_arrayVar(cxoCursor*, PyObject*);
39 static PyObject *cxoCursor_bindNames(cxoCursor*, PyObject*);
40 static PyObject *cxoCursor_getDescription(cxoCursor*, void*);
41 static PyObject *cxoCursor_new(PyTypeObject*, PyObject*, PyObject*);
42 static int cxoCursor_init(cxoCursor*, PyObject*, PyObject*);
43 static PyObject *cxoCursor_repr(cxoCursor*);
44 static PyObject* cxoCursor_getBatchErrors(cxoCursor*);
45 static PyObject *cxoCursor_getArrayDMLRowCounts(cxoCursor*);
46 static PyObject *cxoCursor_getImplicitResults(cxoCursor*);
47 static PyObject *cxoCursor_contextManagerEnter(cxoCursor*, PyObject*);
48 static PyObject *cxoCursor_contextManagerExit(cxoCursor*, PyObject*);
49 static int cxoCursor_performDefine(cxoCursor*, uint32_t);
50
51
52 //-----------------------------------------------------------------------------
53 // declaration of methods for Python type "Cursor"
54 //-----------------------------------------------------------------------------
55 static PyMethodDef cxoCursorMethods[] = {
17 // cxoCursor_new()
18 // Create a new cursor object.
19 //-----------------------------------------------------------------------------
20 static PyObject *cxoCursor_new(PyTypeObject *type, PyObject *args,
21 PyObject *keywordArgs)
22 {
23 return type->tp_alloc(type, 0);
24 }
25
26
27 //-----------------------------------------------------------------------------
28 // cxoCursor_init()
29 // Create a new cursor object.
30 //-----------------------------------------------------------------------------
31 static int cxoCursor_init(cxoCursor *cursor, PyObject *args,
32 PyObject *keywordArgs)
33 {
34 static char *keywordList[] = { "connection", "scrollable", NULL };
35 cxoConnection *connection;
36 PyObject *scrollableObj;
37 int isScrollable;
38
39 // parse arguments
40 scrollableObj = NULL;
41 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!|O", keywordList,
42 &cxoPyTypeConnection, &connection, &scrollableObj))
43 return -1;
44 if (cxoUtils_getBooleanValue(scrollableObj, 0, &isScrollable) < 0)
45 return -1;
46 cursor->isScrollable = (char) isScrollable;
47
48 // initialize members
49 Py_INCREF(connection);
50 cursor->connection = connection;
51 cursor->arraySize = 100;
52 cursor->fetchArraySize = 100;
53 cursor->prefetchRows = DPI_DEFAULT_PREFETCH_ROWS;
54 cursor->bindArraySize = 1;
55 cursor->isOpen = 1;
56
57 return 0;
58 }
59
60
61 //-----------------------------------------------------------------------------
62 // cxoCursor_repr()
63 // Return a string representation of the cursor.
64 //-----------------------------------------------------------------------------
65 static PyObject *cxoCursor_repr(cxoCursor *cursor)
66 {
67 PyObject *connectionRepr, *module, *name, *result;
68
69 connectionRepr = PyObject_Repr((PyObject*) cursor->connection);
70 if (!connectionRepr)
71 return NULL;
72 if (cxoUtils_getModuleAndName(Py_TYPE(cursor), &module, &name) < 0) {
73 Py_DECREF(connectionRepr);
74 return NULL;
75 }
76 result = cxoUtils_formatString("<%s.%s on %s>",
77 PyTuple_Pack(3, module, name, connectionRepr));
78 Py_DECREF(module);
79 Py_DECREF(name);
80 Py_DECREF(connectionRepr);
81 return result;
82 }
83
84
85 //-----------------------------------------------------------------------------
86 // cxoCursor_free()
87 // Deallocate the cursor.
88 //-----------------------------------------------------------------------------
89 static void cxoCursor_free(cxoCursor *cursor)
90 {
91 Py_CLEAR(cursor->statement);
92 Py_CLEAR(cursor->statementTag);
93 Py_CLEAR(cursor->bindVariables);
94 Py_CLEAR(cursor->fetchVariables);
95 if (cursor->handle) {
96 dpiStmt_release(cursor->handle);
97 cursor->handle = NULL;
98 }
99 Py_CLEAR(cursor->connection);
100 Py_CLEAR(cursor->rowFactory);
101 Py_CLEAR(cursor->inputTypeHandler);
102 Py_CLEAR(cursor->outputTypeHandler);
103 Py_TYPE(cursor)->tp_free((PyObject*) cursor);
104 }
105
106
107 //-----------------------------------------------------------------------------
108 // cxoCursor_isOpen()
109 // Determines if the cursor object is open. Since the same cursor can be
110 // used to execute multiple statements, simply checking for the DPI statement
111 // handle is insufficient.
112 //-----------------------------------------------------------------------------
113 static int cxoCursor_isOpen(cxoCursor *cursor)
114 {
115 if (!cursor->isOpen) {
116 cxoError_raiseFromString(cxoInterfaceErrorException, "not open");
117 return -1;
118 }
119 return cxoConnection_isConnected(cursor->connection);
120 }
121
122
123 //-----------------------------------------------------------------------------
124 // cxoCursor_fetchRow()
125 // Fetch a single row from the cursor. Internally the number of rows left in
126 // the buffer is managed in order to minimize calls to Py_BEGIN_ALLOW_THREADS
127 // and Py_END_ALLOW_THREADS which have a significant overhead.
128 //-----------------------------------------------------------------------------
129 static int cxoCursor_fetchRow(cxoCursor *cursor, int *found,
130 uint32_t *bufferRowIndex)
131 {
132 int status;
133
134 // if the number of rows in the fetch buffer is zero and there are more
135 // rows to fetch, call DPI with threading enabled in order to perform any
136 // fetch requiring a network round trip
137 if (cursor->numRowsInFetchBuffer == 0 && cursor->moreRowsToFetch) {
138 Py_BEGIN_ALLOW_THREADS
139 status = dpiStmt_fetchRows(cursor->handle, cursor->fetchArraySize,
140 &cursor->fetchBufferRowIndex, &cursor->numRowsInFetchBuffer,
141 &cursor->moreRowsToFetch);
142 Py_END_ALLOW_THREADS
143 if (status < 0)
144 return cxoError_raiseAndReturnInt();
145 }
146
147 // keep track of where we are in the fetch buffer
148 if (cursor->numRowsInFetchBuffer == 0)
149 *found = 0;
150 else {
151 *found = 1;
152 *bufferRowIndex = cursor->fetchBufferRowIndex++;
153 cursor->numRowsInFetchBuffer--;
154 }
155
156 return 0;
157 }
158
159
160 //-----------------------------------------------------------------------------
161 // cxoCursor_performDefine()
162 // Perform the defines for the cursor. At this point it is assumed that the
163 // statement being executed is in fact a query.
164 //-----------------------------------------------------------------------------
165 static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
166 {
167 PyObject *outputTypeHandler, *result;
168 cxoTransformNum transformNum;
169 cxoObjectType *objectType;
170 dpiQueryInfo queryInfo;
171 uint32_t pos, size;
172 cxoDbType *dbType;
173 char message[120];
174 cxoVar *var;
175
176 // initialize fetching variables; these are used to reduce the number of
177 // times that Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS is called as
178 // there is a significant amount of overhead in making these calls
179 cursor->numRowsInFetchBuffer = 0;
180 cursor->moreRowsToFetch = 1;
181
182 // if fetch variables already exist, nothing more to do (we are executing
183 // the same statement and therefore all defines have already been
184 // performed)
185 if (cursor->fetchVariables)
186 return 0;
187
188 // create a list corresponding to the number of items
189 cursor->fetchVariables = PyList_New(numQueryColumns);
190 if (!cursor->fetchVariables)
191 return -1;
192
193 // create a variable for each of the query columns
194 cursor->fetchArraySize = cursor->arraySize;
195 for (pos = 1; pos <= numQueryColumns; pos++) {
196
197 // get query information for the column position
198 if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0)
199 return cxoError_raiseAndReturnInt();
200 if (queryInfo.typeInfo.sizeInChars)
201 size = queryInfo.typeInfo.sizeInChars;
202 else size = queryInfo.typeInfo.clientSizeInBytes;
203
204 // determine object type, if applicable
205 objectType = NULL;
206 if (queryInfo.typeInfo.objectType) {
207 objectType = cxoObjectType_new(cursor->connection,
208 queryInfo.typeInfo.objectType);
209 if (!objectType)
210 return -1;
211 }
212
213 // determine the default types to use
214 transformNum =
215 cxoTransform_getNumFromDataTypeInfo(&queryInfo.typeInfo);
216 if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
217 snprintf(message, sizeof(message), "Oracle type %d not supported.",
218 queryInfo.typeInfo.oracleTypeNum);
219 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
220 return -1;
221 }
222 dbType = cxoDbType_fromTransformNum(transformNum);
223 if (!dbType)
224 return -1;
225
226 // see if an output type handler should be used
227 var = NULL;
228 outputTypeHandler = NULL;
229 if (cursor->outputTypeHandler && cursor->outputTypeHandler != Py_None)
230 outputTypeHandler = cursor->outputTypeHandler;
231 else if (cursor->connection->outputTypeHandler &&
232 cursor->connection->outputTypeHandler != Py_None)
233 outputTypeHandler = cursor->connection->outputTypeHandler;
234
235 // if using an output type handler, None implies default behavior
236 if (outputTypeHandler) {
237 result = PyObject_CallFunction(outputTypeHandler, "Os#Oiii",
238 cursor, queryInfo.name, (Py_ssize_t) queryInfo.nameLength,
239 dbType, size, queryInfo.typeInfo.precision,
240 queryInfo.typeInfo.scale);
241 if (!result) {
242 Py_XDECREF(objectType);
243 return -1;
244 } else if (result == Py_None)
245 Py_DECREF(result);
246 else if (!cxoVar_check(result)) {
247 Py_DECREF(result);
248 Py_XDECREF(objectType);
249 PyErr_SetString(PyExc_TypeError,
250 "expecting variable from output type handler");
251 return -1;
252 } else {
253 var = (cxoVar*) result;
254 if (var->allocatedElements < cursor->fetchArraySize) {
255 Py_DECREF(result);
256 Py_XDECREF(objectType);
257 PyErr_SetString(PyExc_TypeError,
258 "expecting variable with array size large "
259 "enough for fetch");
260 return -1;
261 }
262 }
263 }
264
265 // if no variable created yet, use the database metadata
266 if (!var) {
267 var = cxoVar_new(cursor, cursor->fetchArraySize, transformNum,
268 size, 0, objectType);
269 if (!var) {
270 Py_XDECREF(objectType);
271 return -1;
272 }
273 }
274
275 // add the variable to the fetch variables and perform define
276 Py_XDECREF(objectType);
277 PyList_SET_ITEM(cursor->fetchVariables, pos - 1, (PyObject *) var);
278 if (dpiStmt_define(cursor->handle, pos, var->handle) < 0)
279 return cxoError_raiseAndReturnInt();
280
281 }
282
283 return 0;
284 }
285
286
287 //-----------------------------------------------------------------------------
288 // cxoCursor_verifyFetch()
289 // Verify that fetching may happen from this cursor.
290 //-----------------------------------------------------------------------------
291 static int cxoCursor_verifyFetch(cxoCursor *cursor)
292 {
293 uint32_t numQueryColumns;
294
295 // make sure the cursor is open
296 if (cxoCursor_isOpen(cursor) < 0)
297 return -1;
298
299 // fixup REF cursor, if applicable
300 if (cursor->fixupRefCursor) {
301 cursor->fetchArraySize = cursor->arraySize;
302 if (dpiStmt_setFetchArraySize(cursor->handle,
303 cursor->fetchArraySize) < 0)
304 return cxoError_raiseAndReturnInt();
305 if (dpiStmt_getNumQueryColumns(cursor->handle, &numQueryColumns) < 0)
306 return cxoError_raiseAndReturnInt();
307 if (cxoCursor_performDefine(cursor, numQueryColumns) < 0)
308 return cxoError_raiseAndReturnInt();
309 cursor->fixupRefCursor = 0;
310 }
311
312 // make sure the cursor is for a query
313 if (!cursor->fetchVariables) {
314 cxoError_raiseFromString(cxoInterfaceErrorException, "not a query");
315 return -1;
316 }
317
318 return 0;
319 }
320
321
322 //-----------------------------------------------------------------------------
323 // cxoCursor_itemDescription()
324 // Return a tuple describing the item at the given position.
325 //-----------------------------------------------------------------------------
326 static PyObject *cxoCursor_itemDescription(cxoCursor *cursor, uint32_t pos)
327 {
328 int displaySize, index;
329 dpiQueryInfo queryInfo;
330 PyObject *tuple, *temp;
331 cxoDbType *dbType;
332
333 // get information about the column position
334 if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0)
335 return NULL;
336 dbType = cxoDbType_fromDataTypeInfo(&queryInfo.typeInfo);
337 if (!dbType)
338 return NULL;
339
340 // set display size based on data type
341 switch (queryInfo.typeInfo.oracleTypeNum) {
342 case DPI_ORACLE_TYPE_VARCHAR:
343 case DPI_ORACLE_TYPE_NVARCHAR:
344 case DPI_ORACLE_TYPE_CHAR:
345 case DPI_ORACLE_TYPE_NCHAR:
346 case DPI_ORACLE_TYPE_ROWID:
347 displaySize = (int) queryInfo.typeInfo.sizeInChars;
348 break;
349 case DPI_ORACLE_TYPE_RAW:
350 displaySize = (int) queryInfo.typeInfo.clientSizeInBytes;
351 break;
352 case DPI_ORACLE_TYPE_NATIVE_FLOAT:
353 case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
354 case DPI_ORACLE_TYPE_NATIVE_INT:
355 case DPI_ORACLE_TYPE_NUMBER:
356 if (queryInfo.typeInfo.precision) {
357 displaySize = queryInfo.typeInfo.precision + 1;
358 if (queryInfo.typeInfo.scale > 0)
359 displaySize += queryInfo.typeInfo.scale + 1;
360 }
361 else displaySize = 127;
362 break;
363 case DPI_ORACLE_TYPE_DATE:
364 case DPI_ORACLE_TYPE_TIMESTAMP:
365 displaySize = 23;
366 break;
367 default:
368 displaySize = 0;
369 }
370
371 // create the tuple and populate it
372 tuple = PyTuple_New(7);
373 if (!tuple)
374 return NULL;
375
376 // set each of the items in the tuple
377 PyTuple_SET_ITEM(tuple, 0, PyUnicode_Decode(queryInfo.name,
378 queryInfo.nameLength, cursor->connection->encodingInfo.encoding,
379 NULL));
380 Py_INCREF(dbType);
381 PyTuple_SET_ITEM(tuple, 1, (PyObject*) dbType);
382 if (displaySize)
383 PyTuple_SET_ITEM(tuple, 2, PyLong_FromLong(displaySize));
384 else {
385 Py_INCREF(Py_None);
386 PyTuple_SET_ITEM(tuple, 2, Py_None);
387 }
388 if (queryInfo.typeInfo.clientSizeInBytes)
389 PyTuple_SET_ITEM(tuple, 3,
390 PyLong_FromLong(queryInfo.typeInfo.clientSizeInBytes));
391 else {
392 Py_INCREF(Py_None);
393 PyTuple_SET_ITEM(tuple, 3, Py_None);
394 }
395 if (queryInfo.typeInfo.precision || queryInfo.typeInfo.scale ||
396 queryInfo.typeInfo.fsPrecision) {
397 PyTuple_SET_ITEM(tuple, 4,
398 PyLong_FromLong(queryInfo.typeInfo.precision));
399 PyTuple_SET_ITEM(tuple, 5,
400 PyLong_FromLong(queryInfo.typeInfo.scale +
401 queryInfo.typeInfo.fsPrecision));
402 } else {
403 Py_INCREF(Py_None);
404 PyTuple_SET_ITEM(tuple, 4, Py_None);
405 Py_INCREF(Py_None);
406 PyTuple_SET_ITEM(tuple, 5, Py_None);
407 }
408 PyTuple_SET_ITEM(tuple, 6, PyLong_FromLong(queryInfo.nullOk != 0));
409
410 // make sure the tuple is ok
411 for (index = 0; index < 7; index++) {
412 temp = PyTuple_GET_ITEM(tuple, index);
413 if (!temp) {
414 Py_DECREF(tuple);
415 return NULL;
416 } else if (temp == Py_None)
417 Py_INCREF(temp);
418 }
419
420 return tuple;
421 }
422
423
424 //-----------------------------------------------------------------------------
425 // cxoCursor_getDescription()
426 // Return a list of 7-tuples consisting of the description of the define
427 // variables.
428 //-----------------------------------------------------------------------------
429 static PyObject *cxoCursor_getDescription(cxoCursor *cursor, void *unused)
430 {
431 uint32_t numQueryColumns, i;
432 PyObject *results, *tuple;
433
434 // make sure the cursor is open
435 if (cxoCursor_isOpen(cursor) < 0)
436 return NULL;
437
438 // determine the number of query columns; if not a query return None
439 if (!cursor->handle)
440 Py_RETURN_NONE;
441 if (dpiStmt_getNumQueryColumns(cursor->handle, &numQueryColumns) < 0)
442 return cxoError_raiseAndReturnNull();
443 if (numQueryColumns == 0)
444 Py_RETURN_NONE;
445
446 // create a list of the required length
447 results = PyList_New(numQueryColumns);
448 if (!results)
449 return NULL;
450
451 // create tuples corresponding to the select-items
452 for (i = 0; i < numQueryColumns; i++) {
453 tuple = cxoCursor_itemDescription(cursor, i + 1);
454 if (!tuple) {
455 Py_DECREF(results);
456 return NULL;
457 }
458 PyList_SET_ITEM(results, i, tuple);
459 }
460
461 return results;
462 }
463
464
465 //-----------------------------------------------------------------------------
466 // cxoCursor_getLastRowid()
467 // Return the rowid of the last modified row if applicable. If no row was
468 // modified the value None is returned.
469 //-----------------------------------------------------------------------------
470 static PyObject *cxoCursor_getLastRowid(cxoCursor *cursor, void *unused)
471 {
472 uint32_t rowidStrLength;
473 const char *rowidStr;
474 dpiRowid *rowid;
475
476 // make sure the cursor is open
477 if (cxoCursor_isOpen(cursor) < 0)
478 return NULL;
479
480 // get the value, if applicable
481 if (cursor->handle) {
482 if (dpiStmt_getLastRowid(cursor->handle, &rowid) < 0)
483 return cxoError_raiseAndReturnNull();
484 if (rowid) {
485 if (dpiRowid_getStringValue(rowid, &rowidStr, &rowidStrLength) < 0)
486 return cxoError_raiseAndReturnNull();
487 return PyUnicode_Decode(rowidStr, rowidStrLength,
488 cursor->connection->encodingInfo.encoding, NULL);
489 }
490 }
491
492 Py_RETURN_NONE;
493 }
494
495
496 //-----------------------------------------------------------------------------
497 // cxoCursor_getOciAttr()
498 // Return the value of the OCI attribute. This is intended to be used for
499 // testing attributes which are not currently exposed directly and should only
500 // be used for that purpose.
501 //-----------------------------------------------------------------------------
502 static PyObject *cxoCursor_getOciAttr(cxoCursor *cursor, PyObject *args,
503 PyObject *keywordArgs)
504 {
505 static char *keywordList[] = { "attr_num", "attr_type", NULL };
506 unsigned attrNum, attrType;
507 uint32_t valueLength;
508 dpiDataBuffer value;
509
510 // validate parameters
511 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "II", keywordList,
512 &attrNum, &attrType))
513 return NULL;
514 if (cxoCursor_isOpen(cursor) < 0)
515 return NULL;
516
517 // get value and convert it to the appropriate Python value
518 if (dpiStmt_getOciAttr(cursor->handle, attrNum, &value, &valueLength) < 0)
519 return cxoError_raiseAndReturnNull();
520 return cxoUtils_convertOciAttrToPythonValue(attrType, &value, valueLength,
521 cursor->connection->encodingInfo.encoding);
522 }
523
524
525 //-----------------------------------------------------------------------------
526 // cxoCursor_getPrefetchRows()
527 // Return an integer providing the number of rows that are prefetched by the
528 // Oracle Client library.
529 //-----------------------------------------------------------------------------
530 static PyObject *cxoCursor_getPrefetchRows(cxoCursor *cursor, void *unused)
531 {
532 if (cxoCursor_isOpen(cursor) < 0)
533 return NULL;
534 return PyLong_FromUnsignedLong(cursor->prefetchRows);
535 }
536
537
538 //-----------------------------------------------------------------------------
539 // cxoCursor_close()
540 // Close the cursor. Any action taken on this cursor from this point forward
541 // results in an exception being raised.
542 //-----------------------------------------------------------------------------
543 static PyObject *cxoCursor_close(cxoCursor *cursor, PyObject *args)
544 {
545 if (cxoCursor_isOpen(cursor) < 0)
546 return NULL;
547 Py_CLEAR(cursor->bindVariables);
548 Py_CLEAR(cursor->fetchVariables);
549 if (cursor->handle) {
550 if (dpiStmt_close(cursor->handle, NULL, 0) < 0)
551 return cxoError_raiseAndReturnNull();
552 dpiStmt_release(cursor->handle);
553 cursor->handle = NULL;
554 }
555 cursor->isOpen = 0;
556
557 Py_RETURN_NONE;
558 }
559
560
561 //-----------------------------------------------------------------------------
562 // cxoCursor_setBindVariableHelper()
563 // Helper for setting a bind variable.
564 //-----------------------------------------------------------------------------
565 static int cxoCursor_setBindVariableHelper(cxoCursor *cursor,
566 unsigned numElements, unsigned arrayPos, PyObject *value,
567 cxoVar *origVar, cxoVar **newVar, int deferTypeAssignment)
568 {
569 cxoVar *varToSet;
570 int isValueVar;
571
572 // initialization
573 *newVar = NULL;
574 isValueVar = cxoVar_check(value);
575
576 // handle case where variable is already bound, either from a prior
577 // execution or a call to setinputsizes()
578 if (origVar) {
579
580 // if the value is a variable object, rebind it if necessary
581 if (isValueVar) {
582 if ( (PyObject*) origVar != value) {
583 Py_INCREF(value);
584 *newVar = (cxoVar*) value;
585 }
586
587 // otherwise, attempt to set the value, but if this fails, simply
588 // ignore the original bind variable and create a new one; this is
589 // intended for cases where the type changes between executions of a
590 // statement or where setinputsizes() has been called with the wrong
591 // type (as mandated by the DB API)
592 } else {
593 varToSet = origVar;
594
595 // first check to see if the variable transform is for None (which
596 // can happen if all of the values in a previous invocation of
597 // executemany() were None) and there is now a value; in this case,
598 // discard the original variable and have a new one created
599 if (origVar->transformNum == CXO_TRANSFORM_NONE &&
600 value != Py_None) {
601 origVar = NULL;
602 varToSet = NULL;
603
604 // otherwise, if the number of elements has changed, create a new
605 // variable this is only necessary for executemany() since
606 // execute() always passes a value of 1 for the number of elements
607 } else if (numElements > origVar->allocatedElements) {
608 *newVar = cxoVar_new(cursor, numElements,
609 origVar->transformNum, origVar->size, origVar->isArray,
610 origVar->objectType);
611 if (!*newVar)
612 return -1;
613 varToSet = *newVar;
614 }
615
616 // attempt to set the value
617 if (varToSet && cxoVar_setValue(varToSet, arrayPos, value) < 0) {
618
619 // executemany() should simply fail after the first element
620 if (arrayPos > 0)
621 return -1;
622
623 // clear the exception and try to create a new variable
624 PyErr_Clear();
625 Py_CLEAR(*newVar);
626 origVar = NULL;
627
628 }
629
630 }
631
632 }
633
634 // if no original variable used, create a new one
635 if (!origVar) {
636
637 // if the value is a variable object, bind it directly
638 if (isValueVar) {
639 Py_INCREF(value);
640 *newVar = (cxoVar*) value;
641
642 // otherwise, create a new variable, unless the value is None and
643 // we wish to defer type assignment
644 } else if (value != Py_None || !deferTypeAssignment) {
645 *newVar = cxoVar_newByValue(cursor, value, numElements);
646 if (!*newVar)
647 return -1;
648 if (cxoVar_setValue(*newVar, arrayPos, value) < 0) {
649 Py_CLEAR(*newVar);
650 return -1;
651 }
652 }
653
654 }
655
656 return 0;
657 }
658
659
660 //-----------------------------------------------------------------------------
661 // cxoCursor_setBindVariables()
662 // Create or set bind variables.
663 //-----------------------------------------------------------------------------
664 int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters,
665 unsigned numElements, unsigned arrayPos, int deferTypeAssignment)
666 {
667 uint32_t i, origBoundByPos, origNumParams, boundByPos, numParams;
668 PyObject *key, *value, *origVar;
669 cxoVar *newVar;
670 Py_ssize_t pos, temp;
671
672 // make sure positional and named binds are not being intermixed
673 origNumParams = numParams = 0;
674 boundByPos = PySequence_Check(parameters);
675 if (boundByPos) {
676 temp = PySequence_Size(parameters);
677 if (temp < 0)
678 return -1;
679 numParams = (uint32_t) temp;
680 }
681 if (cursor->bindVariables) {
682 origBoundByPos = PyList_Check(cursor->bindVariables);
683 if (boundByPos != origBoundByPos) {
684 cxoError_raiseFromString(cxoProgrammingErrorException,
685 "positional and named binds cannot be intermixed");
686 return -1;
687 }
688 if (origBoundByPos)
689 origNumParams = (uint32_t) PyList_GET_SIZE(cursor->bindVariables);
690
691 // otherwise, create the list or dictionary if needed
692 } else {
693 if (boundByPos)
694 cursor->bindVariables = PyList_New(numParams);
695 else cursor->bindVariables = PyDict_New();
696 if (!cursor->bindVariables)
697 return -1;
698 }
699
700 // handle positional binds
701 if (boundByPos) {
702 for (i = 0; i < numParams; i++) {
703 value = PySequence_GetItem(parameters, i);
704 if (!value)
705 return -1;
706 Py_DECREF(value);
707 if (i < origNumParams) {
708 origVar = PyList_GET_ITEM(cursor->bindVariables, i);
709 if (origVar == Py_None)
710 origVar = NULL;
711 } else origVar = NULL;
712 if (cxoCursor_setBindVariableHelper(cursor, numElements, arrayPos,
713 value, (cxoVar*) origVar, &newVar,
714 deferTypeAssignment) < 0)
715 return -1;
716 if (newVar) {
717 if (i < (uint32_t) PyList_GET_SIZE(cursor->bindVariables)) {
718 if (PyList_SetItem(cursor->bindVariables, i,
719 (PyObject*) newVar) < 0) {
720 Py_DECREF(newVar);
721 return -1;
722 }
723 } else {
724 if (PyList_Append(cursor->bindVariables,
725 (PyObject*) newVar) < 0) {
726 Py_DECREF(newVar);
727 return -1;
728 }
729 Py_DECREF(newVar);
730 }
731 }
732 }
733
734 // handle named binds
735 } else {
736 pos = 0;
737 while (PyDict_Next(parameters, &pos, &key, &value)) {
738 origVar = PyDict_GetItem(cursor->bindVariables, key);
739 if (cxoCursor_setBindVariableHelper(cursor, numElements, arrayPos,
740 value, (cxoVar*) origVar, &newVar,
741 deferTypeAssignment) < 0)
742 return -1;
743 if (newVar) {
744 if (PyDict_SetItem(cursor->bindVariables, key,
745 (PyObject*) newVar) < 0) {
746 Py_DECREF(newVar);
747 return -1;
748 }
749 Py_DECREF(newVar);
750 }
751 }
752 }
753
754 return 0;
755 }
756
757
758 //-----------------------------------------------------------------------------
759 // cxoCursor_performBind()
760 // Perform the binds on the cursor.
761 //-----------------------------------------------------------------------------
762 int cxoCursor_performBind(cxoCursor *cursor)
763 {
764 PyObject *key, *var;
765 Py_ssize_t pos;
766 int i;
767
768 // ensure that input sizes are reset
769 // this is done before binding is attempted so that if binding fails and
770 // a new statement is prepared, the bind variables will be reset and
771 // spurious errors will not occur
772 cursor->setInputSizes = 0;
773
774 // set values and perform binds for all bind variables
775 if (cursor->bindVariables) {
776 if (PyDict_Check(cursor->bindVariables)) {
777 pos = 0;
778 while (PyDict_Next(cursor->bindVariables, &pos, &key, &var)) {
779 if (cxoVar_bind((cxoVar*) var, cursor, key, 0) < 0)
780 return -1;
781 }
782 } else {
783 for (i = 0; i < PyList_GET_SIZE(cursor->bindVariables); i++) {
784 var = PyList_GET_ITEM(cursor->bindVariables, i);
785 if (var != Py_None) {
786 if (cxoVar_bind((cxoVar*) var, cursor, NULL,
787 i + 1) < 0)
788 return -1;
789 }
790 }
791 }
792 }
793
794 return 0;
795 }
796
797
798 //-----------------------------------------------------------------------------
799 // cxoCursor_createRow()
800 // Create an object for the row. The object created is a tuple unless a row
801 // factory function has been defined in which case it is the result of the
802 // row factory function called with the argument tuple that would otherwise be
803 // returned.
804 //-----------------------------------------------------------------------------
805 static PyObject *cxoCursor_createRow(cxoCursor *cursor, uint32_t pos)
806 {
807 PyObject *tuple, *item, *result;
808 Py_ssize_t numItems, i;
809 cxoVar *var;
810
811 // bump row count as a new row has been found
812 cursor->rowCount++;
813
814 // create a new tuple
815 numItems = PyList_GET_SIZE(cursor->fetchVariables);
816 tuple = PyTuple_New(numItems);
817 if (!tuple)
818 return NULL;
819
820 // acquire the value for each item
821 for (i = 0; i < numItems; i++) {
822 var = (cxoVar*) PyList_GET_ITEM(cursor->fetchVariables, i);
823 item = cxoVar_getSingleValue(var, var->data, pos);
824 if (!item) {
825 Py_DECREF(tuple);
826 return NULL;
827 }
828 PyTuple_SET_ITEM(tuple, i, item);
829 }
830
831 // if a row factory is defined, call it
832 if (cursor->rowFactory && cursor->rowFactory != Py_None) {
833 result = PyObject_CallObject(cursor->rowFactory, tuple);
834 Py_DECREF(tuple);
835 return result;
836 }
837
838 return tuple;
839 }
840
841
842 //-----------------------------------------------------------------------------
843 // cxoCursor_internalPrepare()
844 // Internal method for preparing a statement for execution.
845 //-----------------------------------------------------------------------------
846 static int cxoCursor_internalPrepare(cxoCursor *cursor, PyObject *statement,
847 PyObject *statementTag)
848 {
849 cxoBuffer statementBuffer, tagBuffer;
850 int status;
851
852 // make sure we don't get a situation where nothing is to be executed
853 if (statement == Py_None && !cursor->statement) {
854 cxoError_raiseFromString(cxoProgrammingErrorException,
855 "no statement specified and no prior statement prepared");
856 return -1;
857 }
858
859 // nothing to do if the statement is identical to the one already stored
860 // but go ahead and prepare anyway for create, alter and drop statments
861 if (statement == Py_None || statement == cursor->statement) {
862 if (cursor->handle && !cursor->stmtInfo.isDDL)
863 return 0;
864 statement = cursor->statement;
865 }
866
867 // keep track of the statement
868 Py_XDECREF(cursor->statement);
869 Py_INCREF(statement);
870 cursor->statement = statement;
871
872 // keep track of the tag
873 Py_XDECREF(cursor->statementTag);
874 Py_XINCREF(statementTag);
875 cursor->statementTag = statementTag;
876
877 // clear fetch and bind variables if applicable
878 Py_CLEAR(cursor->fetchVariables);
879 if (!cursor->setInputSizes)
880 Py_CLEAR(cursor->bindVariables);
881
882 // prepare statement
883 if (cxoBuffer_fromObject(&statementBuffer, statement,
884 cursor->connection->encodingInfo.encoding) < 0)
885 return -1;
886 if (cxoBuffer_fromObject(&tagBuffer, statementTag,
887 cursor->connection->encodingInfo.encoding) < 0) {
888 cxoBuffer_clear(&statementBuffer);
889 return -1;
890 }
891 Py_BEGIN_ALLOW_THREADS
892 if (cursor->handle)
893 dpiStmt_release(cursor->handle);
894 status = dpiConn_prepareStmt(cursor->connection->handle,
895 cursor->isScrollable, (const char*) statementBuffer.ptr,
896 statementBuffer.size, (const char*) tagBuffer.ptr, tagBuffer.size,
897 &cursor->handle);
898 Py_END_ALLOW_THREADS
899 cxoBuffer_clear(&statementBuffer);
900 cxoBuffer_clear(&tagBuffer);
901 if (status < 0)
902 return cxoError_raiseAndReturnInt();
903
904 // get statement information
905 if (dpiStmt_getInfo(cursor->handle, &cursor->stmtInfo) < 0)
906 return cxoError_raiseAndReturnInt();
907
908 // set the fetch array size, if applicable
909 if (cursor->stmtInfo.statementType == DPI_STMT_TYPE_SELECT) {
910 if (dpiStmt_setFetchArraySize(cursor->handle, cursor->arraySize) < 0)
911 return cxoError_raiseAndReturnInt();
912 }
913
914 // set number of rows to prefetch on execute, if applicable
915 if (cursor->prefetchRows != DPI_DEFAULT_PREFETCH_ROWS) {
916 if (dpiStmt_setPrefetchRows(cursor->handle, cursor->prefetchRows) < 0)
917 return cxoError_raiseAndReturnInt();
918 }
919
920 // clear row factory, if applicable
921 Py_CLEAR(cursor->rowFactory);
922
923 return 0;
924 }
925
926
927 //-----------------------------------------------------------------------------
928 // cxoCursor_parse()
929 // Parse the statement without executing it. This also retrieves information
930 // about the select list for select statements.
931 //-----------------------------------------------------------------------------
932 static PyObject *cxoCursor_parse(cxoCursor *cursor, PyObject *statement)
933 {
934 uint32_t mode, numQueryColumns;
935 dpiStmtInfo stmtInfo;
936 int status;
937
938 // make sure the cursor is open
939 if (cxoCursor_isOpen(cursor) < 0)
940 return NULL;
941
942 // prepare the statement and get statement information
943 if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0)
944 return NULL;
945 if (dpiStmt_getInfo(cursor->handle, &stmtInfo) < 0)
946 return cxoError_raiseAndReturnNull();
947
948 // parse the statement
949 if (stmtInfo.isQuery)
950 mode = DPI_MODE_EXEC_DESCRIBE_ONLY;
951 else mode = DPI_MODE_EXEC_PARSE_ONLY;
952 Py_BEGIN_ALLOW_THREADS
953 status = dpiStmt_execute(cursor->handle, mode, &numQueryColumns);
954 Py_END_ALLOW_THREADS
955 if (status < 0)
956 return cxoError_raiseAndReturnNull();
957
958 Py_RETURN_NONE;
959 }
960
961
962 //-----------------------------------------------------------------------------
963 // cxoCursor_prepare()
964 // Prepare the statement for execution.
965 //-----------------------------------------------------------------------------
966 static PyObject *cxoCursor_prepare(cxoCursor *cursor, PyObject *args)
967 {
968 PyObject *statement, *statementTag;
969
970 // statement text and optional tag is expected
971 statementTag = NULL;
972 if (!PyArg_ParseTuple(args, "O|O", &statement, &statementTag))
973 return NULL;
974
975 // make sure the cursor is open
976 if (cxoCursor_isOpen(cursor) < 0)
977 return NULL;
978
979 // prepare the statement
980 if (cxoCursor_internalPrepare(cursor, statement, statementTag) < 0)
981 return NULL;
982
983 Py_RETURN_NONE;
984 }
985
986
987 //-----------------------------------------------------------------------------
988 // cxoCursor_callCalculateSize()
989 // Calculate the size of the statement that is to be executed.
990 //-----------------------------------------------------------------------------
991 static int cxoCursor_callCalculateSize(PyObject *name,
992 cxoVar *returnValue, PyObject *listOfArguments,
993 PyObject *keywordArguments, int *size)
994 {
995 Py_ssize_t numPositionalArgs, numKeywordArgs;
996
997 // set base size without any arguments
998 *size = 17;
999
1000 // add any additional space required to handle the return value
1001 if (returnValue)
1002 *size += 6;
1003
1004 // assume up to 9 characters for each positional argument
1005 // this allows up to four digits for the placeholder if the bind variale
1006 // is a boolean value (prior to Oracle 12.1)
1007 numPositionalArgs = 0;
1008 if (listOfArguments) {
1009 numPositionalArgs = PySequence_Size(listOfArguments);
1010 if (numPositionalArgs < 0)
1011 return -1;
1012 *size += (int) (numPositionalArgs * 9);
1013 }
1014
1015 // assume up to 15 characters for each keyword argument
1016 // this allows up to four digits for the placeholder if the bind variable
1017 // is a boolean value (prior to Oracle 12.1)
1018 numKeywordArgs = 0;
1019 if (keywordArguments) {
1020 numKeywordArgs = PyDict_Size(keywordArguments);
1021 if (numKeywordArgs < 0)
1022 return -1;
1023 *size += (int) (numKeywordArgs * 15);
1024 }
1025
1026 // the above assume a maximum of 10,000 arguments; check and raise an
1027 // error if the number of arguments exceeds this value; more than this
1028 // number would probably be unusable in any case!
1029 if (numPositionalArgs + numKeywordArgs > 10000) {
1030 cxoError_raiseFromString(cxoInterfaceErrorException,
1031 "too many arguments");
1032 return -1;
1033 }
1034
1035 return 0;
1036 }
1037
1038
1039 //-----------------------------------------------------------------------------
1040 // cxoCursor_callBuildStatement()
1041 // Determine the statement and the bind variables to bind to the statement
1042 // that is created for calling a stored procedure or function.
1043 //-----------------------------------------------------------------------------
1044 static int cxoCursor_callBuildStatement(PyObject *name,
1045 cxoVar *returnValue, PyObject *listOfArguments,
1046 PyObject *keywordArguments, char *statement, PyObject **statementObj,
1047 PyObject **bindVariables)
1048 {
1049 PyObject *key, *value, *formatArgs, *positionalArgs;
1050 uint32_t i, argNum, numPositionalArgs;
1051 Py_ssize_t pos;
1052 char *ptr;
1053
1054 // initialize the bind variables to the list of positional arguments
1055 if (listOfArguments)
1056 *bindVariables = PySequence_List(listOfArguments);
1057 else *bindVariables = PyList_New(0);
1058 if (!*bindVariables)
1059 return -1;
1060
1061 // insert the return variable, if applicable
1062 if (returnValue) {
1063 if (PyList_Insert(*bindVariables, 0, (PyObject*) returnValue) < 0)
1064 return -1;
1065 }
1066
1067 // initialize format arguments
1068 formatArgs = PyList_New(0);
1069 if (!formatArgs)
1070 return -1;
1071 if (PyList_Append(formatArgs, name) < 0) {
1072 Py_DECREF(formatArgs);
1073 return -1;
1074 }
1075
1076 // begin building the statement
1077 argNum = 1;
1078 strcpy(statement, "begin ");
1079 if (returnValue) {
1080 strcat(statement, ":1 := ");
1081 argNum++;
1082 }
1083 strcat(statement, "%s");
1084 ptr = statement + strlen(statement);
1085 *ptr++ = '(';
1086
1087 // include any positional arguments first
1088 if (listOfArguments) {
1089 positionalArgs = PySequence_Fast(listOfArguments,
1090 "expecting sequence of arguments");
1091 if (!positionalArgs) {
1092 Py_DECREF(formatArgs);
1093 return -1;
1094 }
1095 numPositionalArgs = (uint32_t) PySequence_Size(listOfArguments);
1096 for (i = 0; i < numPositionalArgs; i++) {
1097 if (i > 0)
1098 *ptr++ = ',';
1099 ptr += sprintf(ptr, ":%d", argNum++);
1100 if (cxoClientVersionInfo.versionNum < 12 &&
1101 PyBool_Check(PySequence_Fast_GET_ITEM(positionalArgs, i)))
1102 ptr += sprintf(ptr, " = 1");
1103 }
1104 Py_DECREF(positionalArgs);
1105 }
1106
1107 // next append any keyword arguments
1108 if (keywordArguments) {
1109 pos = 0;
1110 while (PyDict_Next(keywordArguments, &pos, &key, &value)) {
1111 if (PyList_Append(*bindVariables, value) < 0) {
1112 Py_DECREF(formatArgs);
1113 return -1;
1114 }
1115 if (PyList_Append(formatArgs, key) < 0) {
1116 Py_DECREF(formatArgs);
1117 return -1;
1118 }
1119 if ((argNum > 1 && !returnValue) || (argNum > 2 && returnValue))
1120 *ptr++ = ',';
1121 ptr += sprintf(ptr, "%%s => :%d", argNum++);
1122 if (cxoClientVersionInfo.versionNum < 12 &&
1123 PyBool_Check(value))
1124 ptr += sprintf(ptr, " = 1");
1125 }
1126 }
1127
1128 // create statement object
1129 strcpy(ptr, "); end;");
1130 *statementObj = cxoUtils_formatString(statement,
1131 PyList_AsTuple(formatArgs));
1132 Py_DECREF(formatArgs);
1133 if (!*statementObj)
1134 return -1;
1135
1136 return 0;
1137 }
1138
1139
1140 //-----------------------------------------------------------------------------
1141 // cxoCursor_call()
1142 // Call a stored procedure or function.
1143 //-----------------------------------------------------------------------------
1144 static int cxoCursor_call(cxoCursor *cursor, cxoVar *returnValue,
1145 PyObject *name, PyObject *listOfArguments, PyObject *keywordArguments)
1146 {
1147 PyObject *bindVariables, *statementObj, *results;
1148 int statementSize;
1149 char *statement;
1150
1151 // verify that the arguments are passed correctly
1152 if (listOfArguments) {
1153 if (!PySequence_Check(listOfArguments)) {
1154 PyErr_SetString(PyExc_TypeError, "arguments must be a sequence");
1155 return -1;
1156 }
1157 }
1158 if (keywordArguments) {
1159 if (!PyDict_Check(keywordArguments)) {
1160 PyErr_SetString(PyExc_TypeError,
1161 "keyword arguments must be a dictionary");
1162 return -1;
1163 }
1164 }
1165
1166 // make sure the cursor is open
1167 if (cxoCursor_isOpen(cursor) < 0)
1168 return -1;
1169
1170 // determine the statement size
1171 if (cxoCursor_callCalculateSize(name, returnValue, listOfArguments,
1172 keywordArguments, &statementSize) < 0)
1173 return -1;
1174
1175 // allocate a string for the statement
1176 statement = (char*) PyMem_Malloc(statementSize);
1177 if (!statement) {
1178 PyErr_NoMemory();
1179 return -1;
1180 }
1181
1182 // determine the statement to execute and the argument to pass
1183 bindVariables = statementObj = NULL;
1184 if (cxoCursor_callBuildStatement(name, returnValue, listOfArguments,
1185 keywordArguments, statement, &statementObj, &bindVariables) < 0) {
1186 PyMem_Free(statement);
1187 Py_XDECREF(statementObj);
1188 Py_XDECREF(bindVariables);
1189 return -1;
1190 }
1191 PyMem_Free(statement);
1192
1193 // execute the statement on the cursor
1194 results = PyObject_CallMethod( (PyObject*) cursor, "execute", "OO",
1195 statementObj, bindVariables);
1196 Py_DECREF(statementObj);
1197 Py_DECREF(bindVariables);
1198 if (!results)
1199 return -1;
1200 Py_DECREF(results);
1201
1202 return 0;
1203 }
1204
1205
1206 //-----------------------------------------------------------------------------
1207 // cxoCursor_callFunc()
1208 // Call a stored function and return the return value of the function.
1209 //-----------------------------------------------------------------------------
1210 static PyObject *cxoCursor_callFunc(cxoCursor *cursor, PyObject *args,
1211 PyObject *keywordArgs)
1212 {
1213 static char *keywordList[] = { "name", "returnType", "parameters",
1214 "keywordParameters", NULL };
1215 PyObject *listOfArguments, *keywordArguments, *returnType, *results, *name;
1216 cxoVar *var;
1217
1218 // parse arguments
1219 listOfArguments = keywordArguments = NULL;
1220 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|OO", keywordList,
1221 &name, &returnType, &listOfArguments, &keywordArguments))
1222 return NULL;
1223
1224 // create the return variable
1225 var = cxoVar_newByType(cursor, returnType, 1);
1226 if (!var)
1227 return NULL;
1228
1229 // call the function
1230 if (cxoCursor_call(cursor, var, name, listOfArguments,
1231 keywordArguments) < 0)
1232 return NULL;
1233
1234 // determine the results
1235 results = cxoVar_getValue(var, 0);
1236 Py_DECREF(var);
1237 return results;
1238 }
1239
1240
1241 //-----------------------------------------------------------------------------
1242 // cxoCursor_callProc()
1243 // Call a stored procedure and return the (possibly modified) arguments.
1244 //-----------------------------------------------------------------------------
1245 static PyObject *cxoCursor_callProc(cxoCursor *cursor, PyObject *args,
1246 PyObject *keywordArgs)
1247 {
1248 static char *keywordList[] = { "name", "parameters", "keywordParameters",
1249 NULL };
1250 PyObject *listOfArguments, *keywordArguments, *results, *var, *temp, *name;
1251 Py_ssize_t numArgs, i;
1252
1253 // parse arguments
1254 listOfArguments = keywordArguments = NULL;
1255 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OO", keywordList,
1256 &name, &listOfArguments, &keywordArguments))
1257 return NULL;
1258
1259 // call the stored procedure
1260 if (cxoCursor_call(cursor, NULL, name, listOfArguments,
1261 keywordArguments) < 0)
1262 return NULL;
1263
1264 // create the return value (only positional arguments are returned)
1265 numArgs = (listOfArguments) ? PySequence_Size(listOfArguments) : 0;
1266 results = PyList_New(numArgs);
1267 if (!results)
1268 return NULL;
1269 for (i = 0; i < numArgs; i++) {
1270 var = PyList_GET_ITEM(cursor->bindVariables, i);
1271 temp = cxoVar_getValue((cxoVar*) var, 0);
1272 if (!temp) {
1273 Py_DECREF(results);
1274 return NULL;
1275 }
1276 PyList_SET_ITEM(results, i, temp);
1277 }
1278
1279 return results;
1280 }
1281
1282
1283 //-----------------------------------------------------------------------------
1284 // cxoCursor_execute()
1285 // Execute the statement.
1286 //-----------------------------------------------------------------------------
1287 static PyObject *cxoCursor_execute(cxoCursor *cursor, PyObject *args,
1288 PyObject *keywordArgs)
1289 {
1290 PyObject *statement, *executeArgs;
1291 uint32_t numQueryColumns, mode;
1292 int status;
1293
1294 executeArgs = NULL;
1295 if (!PyArg_ParseTuple(args, "O|O", &statement, &executeArgs))
1296 return NULL;
1297 if (executeArgs && keywordArgs) {
1298 if (PyDict_Size(keywordArgs) == 0)
1299 keywordArgs = NULL;
1300 else return cxoError_raiseFromString(cxoInterfaceErrorException,
1301 "expecting argument or keyword arguments, not both");
1302 }
1303 if (keywordArgs)
1304 executeArgs = keywordArgs;
1305 if (executeArgs) {
1306 if (!PyDict_Check(executeArgs) && !PySequence_Check(executeArgs)) {
1307 PyErr_SetString(PyExc_TypeError,
1308 "expecting a dictionary, sequence or keyword args");
1309 return NULL;
1310 }
1311 }
1312
1313 // make sure the cursor is open
1314 if (cxoCursor_isOpen(cursor) < 0)
1315 return NULL;
1316
1317 // prepare the statement, if applicable
1318 if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0)
1319 return NULL;
1320
1321 // perform binds
1322 if (executeArgs && cxoCursor_setBindVariables(cursor, executeArgs, 1, 0,
1323 0) < 0)
1324 return NULL;
1325 if (cxoCursor_performBind(cursor) < 0)
1326 return NULL;
1327
1328 // execute the statement
1329 Py_BEGIN_ALLOW_THREADS
1330 mode = (cursor->connection->autocommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS :
1331 DPI_MODE_EXEC_DEFAULT;
1332 status = dpiStmt_execute(cursor->handle, mode, &numQueryColumns);
1333 Py_END_ALLOW_THREADS
1334 if (status < 0)
1335 return cxoError_raiseAndReturnNull();
1336
1337 // get the count of the rows affected
1338 if (dpiStmt_getRowCount(cursor->handle, &cursor->rowCount) < 0)
1339 return cxoError_raiseAndReturnNull();
1340
1341 // for queries, return the cursor for convenience
1342 if (numQueryColumns > 0) {
1343 if (cxoCursor_performDefine(cursor, numQueryColumns) < 0) {
1344 Py_CLEAR(cursor->fetchVariables);
1345 return NULL;
1346 }
1347 Py_INCREF(cursor);
1348 return (PyObject*) cursor;
1349 }
1350
1351 // for statements other than queries, simply return None
1352 Py_RETURN_NONE;
1353 }
1354
1355
1356 //-----------------------------------------------------------------------------
1357 // cxoCursor_executeMany()
1358 // Execute the statement many times. The number of times is equivalent to the
1359 // number of elements in the list of parameters, or the provided integer if no
1360 // parameters are required.
1361 //-----------------------------------------------------------------------------
1362 static PyObject *cxoCursor_executeMany(cxoCursor *cursor, PyObject *args,
1363 PyObject *keywordArgs)
1364 {
1365 static char *keywordList[] = { "statement", "parameters", "batcherrors",
1366 "arraydmlrowcounts", NULL };
1367 int arrayDMLRowCountsEnabled = 0, batchErrorsEnabled = 0;
1368 PyObject *arguments, *parameters, *statement;
1369 uint32_t mode, i, numRows;
1370 int status;
1371
1372 // validate parameters
1373 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|ii", keywordList,
1374 &statement, &parameters, &batchErrorsEnabled,
1375 &arrayDMLRowCountsEnabled))
1376 return NULL;
1377 if (!PyList_Check(parameters) && !PyLong_Check(parameters)) {
1378 PyErr_SetString(PyExc_TypeError,
1379 "parameters should be a list of sequences/dictionaries "
1380 "or an integer specifying the number of times to execute "
1381 "the statement");
1382 return NULL;
1383 }
1384
1385 // make sure the cursor is open
1386 if (cxoCursor_isOpen(cursor) < 0)
1387 return NULL;
1388
1389 // determine execution mode
1390 mode = (cursor->connection->autocommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS :
1391 DPI_MODE_EXEC_DEFAULT;
1392 if (batchErrorsEnabled)
1393 mode |= DPI_MODE_EXEC_BATCH_ERRORS;
1394 if (arrayDMLRowCountsEnabled)
1395 mode |= DPI_MODE_EXEC_ARRAY_DML_ROWCOUNTS;
1396
1397 // prepare the statement
1398 if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0)
1399 return NULL;
1400
1401 // perform binds, as required
1402 if (PyLong_Check(parameters))
1403 numRows = (uint32_t) PyLong_AsLong(parameters);
1404 else {
1405 numRows = (uint32_t) PyList_GET_SIZE(parameters);
1406 for (i = 0; i < numRows; i++) {
1407 arguments = PyList_GET_ITEM(parameters, i);
1408 if (!PyDict_Check(arguments) && !PySequence_Check(arguments))
1409 return cxoError_raiseFromString(cxoInterfaceErrorException,
1410 "expecting a list of dictionaries or sequences");
1411 if (cxoCursor_setBindVariables(cursor, arguments, numRows, i,
1412 (i < numRows - 1)) < 0)
1413 return NULL;
1414 }
1415 }
1416 if (cxoCursor_performBind(cursor) < 0)
1417 return NULL;
1418
1419 // execute the statement, but only if the number of rows is greater than
1420 // zero since Oracle raises an error otherwise
1421 if (numRows > 0) {
1422 Py_BEGIN_ALLOW_THREADS
1423 status = dpiStmt_executeMany(cursor->handle, mode, numRows);
1424 Py_END_ALLOW_THREADS
1425 if (status < 0) {
1426 cxoError_raiseAndReturnNull();
1427 dpiStmt_getRowCount(cursor->handle, &cursor->rowCount);
1428 return NULL;
1429 }
1430 if (dpiStmt_getRowCount(cursor->handle, &cursor->rowCount) < 0)
1431 return cxoError_raiseAndReturnNull();
1432 }
1433
1434 Py_RETURN_NONE;
1435 }
1436
1437
1438 //-----------------------------------------------------------------------------
1439 // cxoCursor_executeManyPrepared()
1440 // Execute the prepared statement the number of times requested. At this
1441 // point, the statement must have been already prepared and the bind variables
1442 // must have their values set.
1443 //-----------------------------------------------------------------------------
1444 static PyObject *cxoCursor_executeManyPrepared(cxoCursor *cursor,
1445 PyObject *args)
1446 {
1447 int numIters, status;
1448
1449 // expect number of times to execute the statement
1450 if (!PyArg_ParseTuple(args, "i", &numIters))
1451 return NULL;
1452
1453 // make sure the cursor is open
1454 if (cxoCursor_isOpen(cursor) < 0)
1455 return NULL;
1456
1457 // perform binds
1458 if (cxoCursor_performBind(cursor) < 0)
1459 return NULL;
1460
1461 // execute the statement
1462 Py_BEGIN_ALLOW_THREADS
1463 status = dpiStmt_executeMany(cursor->handle, DPI_MODE_EXEC_DEFAULT,
1464 numIters);
1465 Py_END_ALLOW_THREADS
1466 if (status < 0 || dpiStmt_getRowCount(cursor->handle,
1467 &cursor->rowCount) < 0)
1468 return cxoError_raiseAndReturnNull();
1469
1470 Py_RETURN_NONE;
1471 }
1472
1473
1474 //-----------------------------------------------------------------------------
1475 // cxoCursor_multiFetch()
1476 // Return a list consisting of the remaining rows up to the given row limit
1477 // (if specified).
1478 //-----------------------------------------------------------------------------
1479 static PyObject *cxoCursor_multiFetch(cxoCursor *cursor, int rowLimit)
1480 {
1481 uint32_t bufferRowIndex = 0;
1482 PyObject *results, *row;
1483 int found, rowNum;
1484
1485 // verify fetch can be performed
1486 if (cxoCursor_verifyFetch(cursor) < 0)
1487 return NULL;
1488
1489 // create an empty list
1490 results = PyList_New(0);
1491 if (!results)
1492 return NULL;
1493
1494 // fetch as many rows as possible
1495 for (rowNum = 0; rowLimit == 0 || rowNum < rowLimit; rowNum++) {
1496 if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0) {
1497 Py_DECREF(results);
1498 return NULL;
1499 }
1500 if (!found)
1501 break;
1502 row = cxoCursor_createRow(cursor, bufferRowIndex);
1503 if (!row) {
1504 Py_DECREF(results);
1505 return NULL;
1506 }
1507 if (PyList_Append(results, row) < 0) {
1508 Py_DECREF(row);
1509 Py_DECREF(results);
1510 return NULL;
1511 }
1512 Py_DECREF(row);
1513 }
1514
1515 return results;
1516 }
1517
1518
1519 //-----------------------------------------------------------------------------
1520 // cxoCursor_fetchOne()
1521 // Fetch a single row from the cursor.
1522 //-----------------------------------------------------------------------------
1523 static PyObject *cxoCursor_fetchOne(cxoCursor *cursor, PyObject *args)
1524 {
1525 uint32_t bufferRowIndex = 0;
1526 int found = 0;
1527
1528 if (cxoCursor_verifyFetch(cursor) < 0)
1529 return NULL;
1530 if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0)
1531 return NULL;
1532 if (found)
1533 return cxoCursor_createRow(cursor, bufferRowIndex);
1534
1535 Py_RETURN_NONE;
1536 }
1537
1538
1539 //-----------------------------------------------------------------------------
1540 // cxoCursor_fetchMany()
1541 // Fetch multiple rows from the cursor based on the arraysize.
1542 //-----------------------------------------------------------------------------
1543 static PyObject *cxoCursor_fetchMany(cxoCursor *cursor, PyObject *args,
1544 PyObject *keywordArgs)
1545 {
1546 static char *keywordList[] = { "numRows", NULL };
1547 int rowLimit;
1548
1549 // parse arguments -- optional rowlimit expected
1550 rowLimit = cursor->arraySize;
1551 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
1552 &rowLimit))
1553 return NULL;
1554
1555 return cxoCursor_multiFetch(cursor, rowLimit);
1556 }
1557
1558
1559 //-----------------------------------------------------------------------------
1560 // cxoCursor_fetchAll()
1561 // Fetch all remaining rows from the cursor.
1562 //-----------------------------------------------------------------------------
1563 static PyObject *cxoCursor_fetchAll(cxoCursor *cursor, PyObject *args)
1564 {
1565 return cxoCursor_multiFetch(cursor, 0);
1566 }
1567
1568
1569 //-----------------------------------------------------------------------------
1570 // cxoCursor_fetchRaw()
1571 // Perform raw fetch on the cursor; return the actual number of rows fetched.
1572 //-----------------------------------------------------------------------------
1573 static PyObject *cxoCursor_fetchRaw(cxoCursor *cursor, PyObject *args,
1574 PyObject *keywordArgs)
1575 {
1576 static char *keywordList[] = { "numRows", NULL };
1577 uint32_t numRowsToFetch, numRowsFetched, bufferRowIndex;
1578 int moreRows;
1579
1580 // expect an optional number of rows to retrieve
1581 numRowsToFetch = cursor->fetchArraySize;
1582 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
1583 &numRowsToFetch))
1584 return NULL;
1585 if (numRowsToFetch > cursor->fetchArraySize)
1586 return cxoError_raiseFromString(cxoInterfaceErrorException,
1587 "rows to fetch exceeds array size");
1588
1589 // perform the fetch
1590 if (dpiStmt_fetchRows(cursor->handle, numRowsToFetch, &bufferRowIndex,
1591 &numRowsFetched, &moreRows) < 0)
1592 return cxoError_raiseAndReturnNull();
1593 cursor->rowCount += numRowsFetched;
1594 cursor->numRowsInFetchBuffer = 0;
1595 return PyLong_FromLong(numRowsFetched);
1596 }
1597
1598
1599 //-----------------------------------------------------------------------------
1600 // cxoCursor_scroll()
1601 // Scroll the cursor using the value and mode specified.
1602 //-----------------------------------------------------------------------------
1603 static PyObject *cxoCursor_scroll(cxoCursor *cursor, PyObject *args,
1604 PyObject *keywordArgs)
1605 {
1606 static char *keywordList[] = { "value", "mode", NULL };
1607 dpiFetchMode mode;
1608 int32_t offset;
1609 char *strMode;
1610 int status;
1611
1612 // parse arguments
1613 offset = 0;
1614 strMode = NULL;
1615 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|is", keywordList,
1616 &offset, &strMode))
1617 return NULL;
1618
1619 // validate mode
1620 if (!strMode)
1621 mode = DPI_MODE_FETCH_RELATIVE;
1622 else if (strcmp(strMode, "relative") == 0)
1623 mode = DPI_MODE_FETCH_RELATIVE;
1624 else if (strcmp(strMode, "absolute") == 0)
1625 mode = DPI_MODE_FETCH_ABSOLUTE;
1626 else if (strcmp(strMode, "first") == 0)
1627 mode = DPI_MODE_FETCH_FIRST;
1628 else if (strcmp(strMode, "last") == 0)
1629 mode = DPI_MODE_FETCH_LAST;
1630 else return cxoError_raiseFromString(cxoInterfaceErrorException,
1631 "mode must be one of relative, absolute, first or last");
1632
1633 // make sure the cursor is open
1634 if (cxoCursor_isOpen(cursor) < 0)
1635 return NULL;
1636
1637 // perform scroll and get new row count and number of rows in buffer
1638 Py_BEGIN_ALLOW_THREADS
1639 status = dpiStmt_scroll(cursor->handle, mode, offset,
1640 0 - cursor->numRowsInFetchBuffer);
1641 if (status == 0)
1642 status = dpiStmt_fetchRows(cursor->handle, cursor->fetchArraySize,
1643 &cursor->fetchBufferRowIndex, &cursor->numRowsInFetchBuffer,
1644 &cursor->moreRowsToFetch);
1645 if (status == 0)
1646 status = dpiStmt_getRowCount(cursor->handle, &cursor->rowCount);
1647 Py_END_ALLOW_THREADS
1648 if (status < 0)
1649 return cxoError_raiseAndReturnNull();
1650 cursor->rowCount -= cursor->numRowsInFetchBuffer;
1651
1652 Py_RETURN_NONE;
1653 }
1654
1655
1656 //-----------------------------------------------------------------------------
1657 // cxoCursor_setInputSizes()
1658 // Set the sizes of the bind variables.
1659 //-----------------------------------------------------------------------------
1660 static PyObject *cxoCursor_setInputSizes(cxoCursor *cursor, PyObject *args,
1661 PyObject *keywordArgs)
1662 {
1663 Py_ssize_t numPositionalArgs, numKeywordArgs = 0, i;
1664 PyObject *key, *value;
1665 cxoVar *var;
1666
1667 // only expect keyword arguments or positional arguments, not both
1668 numPositionalArgs = PyTuple_Size(args);
1669 if (keywordArgs)
1670 numKeywordArgs = PyDict_Size(keywordArgs);
1671 if (numKeywordArgs > 0 && numPositionalArgs > 0)
1672 return cxoError_raiseFromString(cxoInterfaceErrorException,
1673 "expecting arguments or keyword arguments, not both");
1674
1675 // make sure the cursor is open
1676 if (cxoCursor_isOpen(cursor) < 0)
1677 return NULL;
1678
1679 // eliminate existing bind variables
1680 Py_CLEAR(cursor->bindVariables);
1681
1682 // if no values passed, do nothing further, but return an empty list or
1683 // dictionary as appropriate
1684 if (numKeywordArgs == 0 && numPositionalArgs == 0) {
1685 if (keywordArgs)
1686 return PyDict_New();
1687 return PyList_New(0);
1688 }
1689
1690 // retain bind variables
1691 cursor->setInputSizes = 1;
1692 if (numKeywordArgs > 0)
1693 cursor->bindVariables = PyDict_New();
1694 else cursor->bindVariables = PyList_New(numPositionalArgs);
1695 if (!cursor->bindVariables)
1696 return NULL;
1697
1698 // process each input
1699 if (numKeywordArgs > 0) {
1700 i = 0;
1701 while (PyDict_Next(keywordArgs, &i, &key, &value)) {
1702 var = cxoVar_newByType(cursor, value, cursor->bindArraySize);
1703 if (!var)
1704 return NULL;
1705 if (PyDict_SetItem(cursor->bindVariables, key,
1706 (PyObject*) var) < 0) {
1707 Py_DECREF(var);
1708 return NULL;
1709 }
1710 Py_DECREF(var);
1711 }
1712 } else {
1713 for (i = 0; i < numPositionalArgs; i++) {
1714 value = PyTuple_GET_ITEM(args, i);
1715 if (value == Py_None) {
1716 Py_INCREF(Py_None);
1717 PyList_SET_ITEM(cursor->bindVariables, i, Py_None);
1718 } else {
1719 var = cxoVar_newByType(cursor, value, cursor->bindArraySize);
1720 if (!var)
1721 return NULL;
1722 PyList_SET_ITEM(cursor->bindVariables, i, (PyObject*) var);
1723 }
1724 }
1725 }
1726
1727 Py_INCREF(cursor->bindVariables);
1728 return cursor->bindVariables;
1729 }
1730
1731
1732 //-----------------------------------------------------------------------------
1733 // cxoCursor_setOciAttr()
1734 // Set the value of the OCI attribute to the specified value. This is
1735 // intended to be used for testing attributes which are not currently exposed
1736 // directly and should only be used for that purpose.
1737 //-----------------------------------------------------------------------------
1738 static PyObject *cxoCursor_setOciAttr(cxoCursor *cursor, PyObject *args,
1739 PyObject *keywordArgs)
1740 {
1741 static char *keywordList[] = { "attr_num", "attr_type", "value", NULL };
1742 unsigned attrNum, attrType;
1743 uint32_t ociValueLength;
1744 dpiDataBuffer ociBuffer;
1745 cxoBuffer buffer;
1746 PyObject *value;
1747 void *ociValue;
1748
1749 // validate parameters
1750 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "IIO", keywordList,
1751 &attrNum, &attrType, &value))
1752 return NULL;
1753 if (cxoCursor_isOpen(cursor) < 0)
1754 return NULL;
1755
1756 // convert value to OCI requirement and then set it
1757 cxoBuffer_init(&buffer);
1758 if (cxoUtils_convertPythonValueToOciAttr(value, attrType, &buffer,
1759 &ociBuffer, &ociValue, &ociValueLength,
1760 cursor->connection->encodingInfo.encoding) < 0)
1761 return NULL;
1762 if (dpiStmt_setOciAttr(cursor->handle, attrNum, ociValue,
1763 ociValueLength) < 0)
1764 return cxoError_raiseAndReturnNull();
1765 cxoBuffer_clear(&buffer);
1766
1767 Py_RETURN_NONE;
1768 }
1769
1770
1771 //-----------------------------------------------------------------------------
1772 // cxoCursor_setOutputSize()
1773 // Does nothing as ODPI-C handles long columns dynamically without the need
1774 // to specify a maximum length.
1775 //-----------------------------------------------------------------------------
1776 static PyObject *cxoCursor_setOutputSize(cxoCursor *cursor, PyObject *args)
1777 {
1778 int outputSize, outputSizeColumn;
1779
1780 if (!PyArg_ParseTuple(args, "i|i", &outputSize, &outputSizeColumn))
1781 return NULL;
1782 Py_RETURN_NONE;
1783 }
1784
1785
1786 //-----------------------------------------------------------------------------
1787 // cxoCursor_var()
1788 // Create a bind variable and return it.
1789 //-----------------------------------------------------------------------------
1790 static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
1791 PyObject *keywordArgs)
1792 {
1793 static char *keywordList[] = { "type", "size", "arraysize",
1794 "inconverter", "outconverter", "typename", "encodingErrors",
1795 NULL };
1796 PyObject *inConverter, *outConverter, *typeNameObj;
1797 Py_ssize_t encodingErrorsLength;
1798 cxoTransformNum transformNum;
1799 const char *encodingErrors;
1800 cxoObjectType *objType;
1801 int size, arraySize;
1802 PyObject *type;
1803 cxoVar *var;
1804
1805 // parse arguments
1806 size = 0;
1807 encodingErrors = NULL;
1808 arraySize = cursor->bindArraySize;
1809 inConverter = outConverter = typeNameObj = NULL;
1810 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|iiOOOz#",
1811 keywordList, &type, &size, &arraySize, &inConverter, &outConverter,
1812 &typeNameObj, &encodingErrors, &encodingErrorsLength))
1813 return NULL;
1814
1815 // determine the type of variable
1816 if (cxoTransform_getNumFromType(type, &transformNum, &objType) < 0)
1817 return NULL;
1818 Py_XINCREF(objType);
1819 if (typeNameObj && typeNameObj != Py_None && !objType) {
1820 objType = cxoObjectType_newByName(cursor->connection, typeNameObj);
1821 if (!objType)
1822 return NULL;
1823 }
1824
1825 // create the variable
1826 var = cxoVar_new(cursor, arraySize, transformNum, size, 0, objType);
1827 Py_XDECREF(objType);
1828 if (!var)
1829 return NULL;
1830 Py_XINCREF(inConverter);
1831 var->inConverter = inConverter;
1832 Py_XINCREF(outConverter);
1833 var->outConverter = outConverter;
1834
1835 // assign encoding errors, if applicable
1836 if (encodingErrors) {
1837 var->encodingErrors = PyMem_Malloc(encodingErrorsLength + 1);
1838 if (!var->encodingErrors) {
1839 Py_DECREF(var);
1840 return NULL;
1841 }
1842 strcpy((char*) var->encodingErrors, encodingErrors);
1843 }
1844
1845 return (PyObject*) var;
1846 }
1847
1848
1849 //-----------------------------------------------------------------------------
1850 // cxoCursor_arrayVar()
1851 // Create an array bind variable and return it.
1852 //-----------------------------------------------------------------------------
1853 static PyObject *cxoCursor_arrayVar(cxoCursor *cursor, PyObject *args)
1854 {
1855 cxoTransformNum transformNum;
1856 uint32_t size, numElements;
1857 PyObject *type, *value;
1858 cxoObjectType *objType;
1859 cxoVar *var;
1860
1861 // parse arguments
1862 size = 0;
1863 if (!PyArg_ParseTuple(args, "OO|i", &type, &value, &size))
1864 return NULL;
1865
1866 // determine the transform to use for the variable
1867 if (cxoTransform_getNumFromType(type, &transformNum, &objType) < 0)
1868 return NULL;
1869
1870 // determine the number of elements to create
1871 if (PyList_Check(value))
1872 numElements = (uint32_t) PyList_GET_SIZE(value);
1873 else if (PyLong_Check(value)) {
1874 numElements = (uint32_t) PyLong_AsLong(value);
1875 if (PyErr_Occurred())
1876 return NULL;
1877 } else {
1878 PyErr_SetString(PyExc_TypeError,
1879 "expecting integer or list of values");
1880 return NULL;
1881 }
1882
1883 // create the variable
1884 var = cxoVar_new(cursor, numElements, transformNum, size, 1, objType);
1885 if (!var)
1886 return NULL;
1887
1888 // set the value, if applicable
1889 if (PyList_Check(value)) {
1890 if (cxoVar_setValue(var, 0, value) < 0)
1891 return NULL;
1892 }
1893
1894 return (PyObject*) var;
1895 }
1896
1897
1898 //-----------------------------------------------------------------------------
1899 // cxoCursor_bindNames()
1900 // Return a list of bind variable names.
1901 //-----------------------------------------------------------------------------
1902 static PyObject *cxoCursor_bindNames(cxoCursor *cursor, PyObject *args)
1903 {
1904 uint32_t numBinds, *nameLengths, i;
1905 PyObject *namesList, *temp;
1906 const char **names;
1907
1908 // make sure the cursor is open
1909 if (cxoCursor_isOpen(cursor) < 0)
1910 return NULL;
1911
1912 // ensure that a statement has already been prepared
1913 if (!cursor->statement)
1914 return cxoError_raiseFromString(cxoProgrammingErrorException,
1915 "statement must be prepared first");
1916
1917 // determine the number of binds
1918 if (dpiStmt_getBindCount(cursor->handle, &numBinds) < 0)
1919 return cxoError_raiseAndReturnNull();
1920
1921 // if the number of binds is zero, nothing to do
1922 if (numBinds == 0)
1923 return PyList_New(0);
1924
1925 // allocate memory for the bind names and their lengths
1926 names = (const char**) PyMem_Malloc(numBinds * sizeof(char*));
1927 if (!names)
1928 return PyErr_NoMemory();
1929 nameLengths = (uint32_t*) PyMem_Malloc(numBinds * sizeof(uint32_t));
1930 if (!nameLengths) {
1931 PyMem_Free((void*) names);
1932 return PyErr_NoMemory();
1933 }
1934
1935 // get the bind names
1936 if (dpiStmt_getBindNames(cursor->handle, &numBinds, names,
1937 nameLengths) < 0) {
1938 PyMem_Free((void*) names);
1939 PyMem_Free(nameLengths);
1940 return cxoError_raiseAndReturnNull();
1941 }
1942
1943 // populate list with the results
1944 namesList = PyList_New(numBinds);
1945 if (namesList) {
1946 for (i = 0; i < numBinds; i++) {
1947 temp = PyUnicode_Decode(names[i], nameLengths[i],
1948 cursor->connection->encodingInfo.encoding, NULL);
1949 if (!temp) {
1950 Py_CLEAR(namesList);
1951 break;
1952 }
1953 PyList_SET_ITEM(namesList, i, temp);
1954 }
1955 }
1956 PyMem_Free((void*) names);
1957 PyMem_Free(nameLengths);
1958 return namesList;
1959 }
1960
1961
1962 //-----------------------------------------------------------------------------
1963 // cxoCursor_getIter()
1964 // Return a reference to the cursor which supports the iterator protocol.
1965 //-----------------------------------------------------------------------------
1966 static PyObject *cxoCursor_getIter(cxoCursor *cursor)
1967 {
1968 if (cxoCursor_verifyFetch(cursor) < 0)
1969 return NULL;
1970 Py_INCREF(cursor);
1971 return (PyObject*) cursor;
1972 }
1973
1974
1975 //-----------------------------------------------------------------------------
1976 // cxoCursor_getNext()
1977 // Return a reference to the cursor which supports the iterator protocol.
1978 //-----------------------------------------------------------------------------
1979 static PyObject *cxoCursor_getNext(cxoCursor *cursor)
1980 {
1981 uint32_t bufferRowIndex = 0;
1982 int found = 0;
1983
1984 if (cxoCursor_verifyFetch(cursor) < 0)
1985 return NULL;
1986 if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0)
1987 return NULL;
1988 if (found)
1989 return cxoCursor_createRow(cursor, bufferRowIndex);
1990
1991 // no more rows, return NULL without setting an exception
1992 return NULL;
1993 }
1994
1995
1996 //-----------------------------------------------------------------------------
1997 // cxoCursor_getBatchErrors()
1998 // Returns a list of batch error objects.
1999 //-----------------------------------------------------------------------------
2000 static PyObject* cxoCursor_getBatchErrors(cxoCursor *cursor)
2001 {
2002 uint32_t numErrors, i;
2003 dpiErrorInfo *errors;
2004 PyObject *result;
2005 cxoError *error;
2006
2007 // determine the number of errors
2008 if (dpiStmt_getBatchErrorCount(cursor->handle, &numErrors) < 0)
2009 return cxoError_raiseAndReturnNull();
2010 if (numErrors == 0)
2011 return PyList_New(0);
2012
2013 // allocate memory for the errors
2014 errors = PyMem_Malloc(numErrors * sizeof(dpiErrorInfo));
2015 if (!errors)
2016 return PyErr_NoMemory();
2017
2018 // get error information
2019 if (dpiStmt_getBatchErrors(cursor->handle, numErrors, errors) < 0) {
2020 PyMem_Free(errors);
2021 return cxoError_raiseAndReturnNull();
2022 }
2023
2024 // create result
2025 result = PyList_New(numErrors);
2026 if (result) {
2027 for (i = 0; i < numErrors; i++) {
2028 error = cxoError_newFromInfo(&errors[i]);
2029 if (!error) {
2030 Py_CLEAR(result);
2031 break;
2032 }
2033 PyList_SET_ITEM(result, i, (PyObject*) error);
2034 }
2035 }
2036 PyMem_Free(errors);
2037 return result;
2038 }
2039
2040
2041 //-----------------------------------------------------------------------------
2042 // cxoCursor_getArrayDMLRowCounts
2043 // Populates the array dml row count list.
2044 //-----------------------------------------------------------------------------
2045 static PyObject* cxoCursor_getArrayDMLRowCounts(cxoCursor *cursor)
2046 {
2047 PyObject *result, *element;
2048 uint32_t numRowCounts, i;
2049 uint64_t *rowCounts;
2050
2051 // get row counts from DPI
2052 if (dpiStmt_getRowCounts(cursor->handle, &numRowCounts, &rowCounts) < 0)
2053 return cxoError_raiseAndReturnNull();
2054
2055 // return array
2056 result = PyList_New(numRowCounts);
2057 if (!result)
2058 return NULL;
2059 for (i = 0; i < numRowCounts; i++) {
2060 element = PyLong_FromUnsignedLong((unsigned long) rowCounts[i]);
2061 if (!element) {
2062 Py_DECREF(result);
2063 return NULL;
2064 }
2065 PyList_SET_ITEM(result, i, element);
2066 }
2067
2068 return result;
2069 }
2070
2071
2072 //-----------------------------------------------------------------------------
2073 // cxoCursor_getImplicitResults
2074 // Return a list of cursors available implicitly after execution of a PL/SQL
2075 // block or stored procedure. If none are available, an empty list is returned.
2076 //-----------------------------------------------------------------------------
2077 static PyObject *cxoCursor_getImplicitResults(cxoCursor *cursor)
2078 {
2079 cxoCursor *childCursor;
2080 dpiStmt *childStmt;
2081 PyObject *result;
2082
2083 // make sure the cursor is open
2084 if (cxoCursor_isOpen(cursor) < 0)
2085 return NULL;
2086
2087 // make sure we have a statement executed (handle defined)
2088 if (!cursor->handle)
2089 return cxoError_raiseFromString(cxoInterfaceErrorException,
2090 "no statement executed");
2091
2092 // create result
2093 result = PyList_New(0);
2094 if (!result)
2095 return NULL;
2096 while (1) {
2097 if (dpiStmt_getImplicitResult(cursor->handle, &childStmt) < 0)
2098 return cxoError_raiseAndReturnNull();
2099 if (!childStmt)
2100 break;
2101 childCursor = (cxoCursor*) PyObject_CallMethod(
2102 (PyObject*) cursor->connection, "cursor", NULL);
2103 if (!childCursor) {
2104 dpiStmt_release(childStmt);
2105 Py_DECREF(result);
2106 return NULL;
2107 }
2108 childCursor->handle = childStmt;
2109 childCursor->fixupRefCursor = 1;
2110 if (PyList_Append(result, (PyObject*) childCursor) < 0) {
2111 Py_DECREF(result);
2112 Py_DECREF(childCursor);
2113 return NULL;
2114 }
2115 Py_DECREF(childCursor);
2116 }
2117
2118 return result;
2119 }
2120
2121
2122 //-----------------------------------------------------------------------------
2123 // cxoCursor_contextManagerEnter()
2124 // Called when the cursor is used as a context manager and simply returns it
2125 // to the caller.
2126 //-----------------------------------------------------------------------------
2127 static PyObject *cxoCursor_contextManagerEnter(cxoCursor *cursor,
2128 PyObject* args)
2129 {
2130 Py_INCREF(cursor);
2131 return (PyObject*) cursor;
2132 }
2133
2134
2135 //-----------------------------------------------------------------------------
2136 // cxoCursor_contextManagerExit()
2137 // Called when the cursor is used as a context manager and simply closes the
2138 // cursor.
2139 //-----------------------------------------------------------------------------
2140 static PyObject *cxoCursor_contextManagerExit(cxoCursor *cursor,
2141 PyObject* args)
2142 {
2143 PyObject *excType, *excValue, *excTraceback, *result;
2144
2145 if (!PyArg_ParseTuple(args, "OOO", &excType, &excValue, &excTraceback))
2146 return NULL;
2147 result = cxoCursor_close(cursor, NULL);
2148 if (!result)
2149 return NULL;
2150 Py_DECREF(result);
2151 Py_INCREF(Py_False);
2152 return Py_False;
2153 }
2154
2155
2156 //-----------------------------------------------------------------------------
2157 // cxoCursor_setPrefetchRows()
2158 // Set the number of rows that are prefetched by the Oracle Client library.
2159 //-----------------------------------------------------------------------------
2160 static int cxoCursor_setPrefetchRows(cxoCursor* cursor, PyObject *value,
2161 void* arg)
2162 {
2163 unsigned long numRows;
2164
2165 if (cxoCursor_isOpen(cursor) < 0)
2166 return -1;
2167 numRows = PyLong_AsUnsignedLong(value);
2168 if (PyErr_Occurred())
2169 return -1;
2170 cursor->prefetchRows = (uint32_t) numRows;
2171 if (cursor->handle && dpiStmt_setPrefetchRows(cursor->handle,
2172 cursor->prefetchRows) < 0)
2173 return cxoError_raiseAndReturnInt();
2174 return 0;
2175 }
2176
2177
2178 //-----------------------------------------------------------------------------
2179 // declaration of methods for Python type
2180 //-----------------------------------------------------------------------------
2181 static PyMethodDef cxoMethods[] = {
562182 { "execute", (PyCFunction) cxoCursor_execute,
572183 METH_VARARGS | METH_KEYWORDS },
582184 { "fetchall", (PyCFunction) cxoCursor_fetchAll, METH_NOARGS },
862212 METH_NOARGS },
872213 { "__enter__", (PyCFunction) cxoCursor_contextManagerEnter, METH_NOARGS },
882214 { "__exit__", (PyCFunction) cxoCursor_contextManagerExit, METH_VARARGS },
2215 { "_get_oci_attr", (PyCFunction) cxoCursor_getOciAttr,
2216 METH_VARARGS | METH_KEYWORDS },
2217 { "_set_oci_attr", (PyCFunction) cxoCursor_setOciAttr,
2218 METH_VARARGS | METH_KEYWORDS },
892219 { NULL, NULL }
902220 };
912221
922222
932223 //-----------------------------------------------------------------------------
94 // declaration of members for Python type "Cursor"
95 //-----------------------------------------------------------------------------
96 static PyMemberDef cxoCursorMembers[] = {
2224 // declaration of members for Python type
2225 //-----------------------------------------------------------------------------
2226 static PyMemberDef cxoMembers[] = {
972227 { "arraysize", T_UINT, offsetof(cxoCursor, arraySize), 0 },
982228 { "bindarraysize", T_UINT, offsetof(cxoCursor, bindArraySize), 0 },
992229 { "rowcount", T_ULONGLONG, offsetof(cxoCursor, rowCount), READONLY },
1122242
1132243
1142244 //-----------------------------------------------------------------------------
115 // declaration of calculated members for Python type "Connection"
116 //-----------------------------------------------------------------------------
117 static PyGetSetDef cxoCursorCalcMembers[] = {
2245 // declaration of calculated members for Python type
2246 //-----------------------------------------------------------------------------
2247 static PyGetSetDef cxoCalcMembers[] = {
1182248 { "description", (getter) cxoCursor_getDescription, 0, 0, 0 },
2249 { "lastrowid", (getter) cxoCursor_getLastRowid, 0, 0, 0 },
2250 { "prefetchrows", (getter) cxoCursor_getPrefetchRows,
2251 (setter) cxoCursor_setPrefetchRows, 0, 0 },
1192252 { NULL }
1202253 };
1212254
1222255
1232256 //-----------------------------------------------------------------------------
124 // declaration of Python type "Cursor"
2257 // declaration of Python type
1252258 //-----------------------------------------------------------------------------
1262259 PyTypeObject cxoPyTypeCursor = {
1272260 PyVarObject_HEAD_INIT(NULL, 0)
128 "cx_Oracle.Cursor", // tp_name
129 sizeof(cxoCursor), // tp_basicsize
130 0, // tp_itemsize
131 (destructor) cxoCursor_free, // tp_dealloc
132 0, // tp_print
133 0, // tp_getattr
134 0, // tp_setattr
135 0, // tp_compare
136 (reprfunc) cxoCursor_repr, // tp_repr
137 0, // tp_as_number
138 0, // tp_as_sequence
139 0, // tp_as_mapping
140 0, // tp_hash
141 0, // tp_call
142 0, // tp_str
143 0, // tp_getattro
144 0, // tp_setattro
145 0, // tp_as_buffer
146 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
147 // tp_flags
148 0, // tp_doc
149 0, // tp_traverse
150 0, // tp_clear
151 0, // tp_richcompare
152 0, // tp_weaklistoffset
153 (getiterfunc) cxoCursor_getIter, // tp_iter
154 (iternextfunc) cxoCursor_getNext, // tp_iternext
155 cxoCursorMethods, // tp_methods
156 cxoCursorMembers, // tp_members
157 cxoCursorCalcMembers, // tp_getset
158 0, // tp_base
159 0, // tp_dict
160 0, // tp_descr_get
161 0, // tp_descr_set
162 0, // tp_dictoffset
163 (initproc) cxoCursor_init, // tp_init
164 0, // tp_alloc
165 cxoCursor_new, // tp_new
166 0, // tp_free
167 0, // tp_is_gc
168 0 // tp_bases
2261 .tp_name = "cx_Oracle.Cursor",
2262 .tp_basicsize = sizeof(cxoCursor),
2263 .tp_dealloc = (destructor) cxoCursor_free,
2264 .tp_repr = (reprfunc) cxoCursor_repr,
2265 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
2266 .tp_iter = (getiterfunc) cxoCursor_getIter,
2267 .tp_iternext = (iternextfunc) cxoCursor_getNext,
2268 .tp_methods = cxoMethods,
2269 .tp_members = cxoMembers,
2270 .tp_getset = cxoCalcMembers,
2271 .tp_init = (initproc) cxoCursor_init,
2272 .tp_new = cxoCursor_new
1692273 };
170
171
172 //-----------------------------------------------------------------------------
173 // cxoCursor_new()
174 // Create a new cursor object.
175 //-----------------------------------------------------------------------------
176 static PyObject *cxoCursor_new(PyTypeObject *type, PyObject *args,
177 PyObject *keywordArgs)
178 {
179 return type->tp_alloc(type, 0);
180 }
181
182
183 //-----------------------------------------------------------------------------
184 // cxoCursor_init()
185 // Create a new cursor object.
186 //-----------------------------------------------------------------------------
187 static int cxoCursor_init(cxoCursor *cursor, PyObject *args,
188 PyObject *keywordArgs)
189 {
190 static char *keywordList[] = { "connection", "scrollable", NULL };
191 cxoConnection *connection;
192 PyObject *scrollableObj;
193
194 // parse arguments
195 scrollableObj = NULL;
196 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!|O", keywordList,
197 &cxoPyTypeConnection, &connection, &scrollableObj))
198 return -1;
199 if (cxoUtils_getBooleanValue(scrollableObj, 0, &cursor->isScrollable) < 0)
200 return -1;
201
202 // initialize members
203 Py_INCREF(connection);
204 cursor->connection = connection;
205 cursor->arraySize = 100;
206 cursor->fetchArraySize = 100;
207 cursor->bindArraySize = 1;
208 cursor->isOpen = 1;
209
210 return 0;
211 }
212
213
214 //-----------------------------------------------------------------------------
215 // cxoCursor_repr()
216 // Return a string representation of the cursor.
217 //-----------------------------------------------------------------------------
218 static PyObject *cxoCursor_repr(cxoCursor *cursor)
219 {
220 PyObject *connectionRepr, *module, *name, *result;
221
222 connectionRepr = PyObject_Repr((PyObject*) cursor->connection);
223 if (!connectionRepr)
224 return NULL;
225 if (cxoUtils_getModuleAndName(Py_TYPE(cursor), &module, &name) < 0) {
226 Py_DECREF(connectionRepr);
227 return NULL;
228 }
229 result = cxoUtils_formatString("<%s.%s on %s>",
230 PyTuple_Pack(3, module, name, connectionRepr));
231 Py_DECREF(module);
232 Py_DECREF(name);
233 Py_DECREF(connectionRepr);
234 return result;
235 }
236
237
238 //-----------------------------------------------------------------------------
239 // cxoCursor_free()
240 // Deallocate the cursor.
241 //-----------------------------------------------------------------------------
242 static void cxoCursor_free(cxoCursor *cursor)
243 {
244 Py_CLEAR(cursor->statement);
245 Py_CLEAR(cursor->statementTag);
246 Py_CLEAR(cursor->bindVariables);
247 Py_CLEAR(cursor->fetchVariables);
248 if (cursor->handle) {
249 dpiStmt_release(cursor->handle);
250 cursor->handle = NULL;
251 }
252 Py_CLEAR(cursor->connection);
253 Py_CLEAR(cursor->rowFactory);
254 Py_CLEAR(cursor->inputTypeHandler);
255 Py_CLEAR(cursor->outputTypeHandler);
256 Py_TYPE(cursor)->tp_free((PyObject*) cursor);
257 }
258
259
260 //-----------------------------------------------------------------------------
261 // cxoCursor_isOpen()
262 // Determines if the cursor object is open. Since the same cursor can be
263 // used to execute multiple statements, simply checking for the DPI statement
264 // handle is insufficient.
265 //-----------------------------------------------------------------------------
266 static int cxoCursor_isOpen(cxoCursor *cursor)
267 {
268 if (!cursor->isOpen) {
269 cxoError_raiseFromString(cxoInterfaceErrorException, "not open");
270 return -1;
271 }
272 return cxoConnection_isConnected(cursor->connection);
273 }
274
275
276 //-----------------------------------------------------------------------------
277 // cxoCursor_verifyFetch()
278 // Verify that fetching may happen from this cursor.
279 //-----------------------------------------------------------------------------
280 static int cxoCursor_verifyFetch(cxoCursor *cursor)
281 {
282 uint32_t numQueryColumns;
283
284 // make sure the cursor is open
285 if (cxoCursor_isOpen(cursor) < 0)
286 return -1;
287
288 // fixup REF cursor, if applicable
289 if (cursor->fixupRefCursor) {
290 cursor->fetchArraySize = cursor->arraySize;
291 if (dpiStmt_setFetchArraySize(cursor->handle,
292 cursor->fetchArraySize) < 0)
293 return cxoError_raiseAndReturnInt();
294 if (dpiStmt_getNumQueryColumns(cursor->handle, &numQueryColumns) < 0)
295 return cxoError_raiseAndReturnInt();
296 if (cxoCursor_performDefine(cursor, numQueryColumns) < 0)
297 return cxoError_raiseAndReturnInt();
298 cursor->fixupRefCursor = 0;
299 }
300
301 // make sure the cursor is for a query
302 if (!cursor->fetchVariables) {
303 cxoError_raiseFromString(cxoInterfaceErrorException, "not a query");
304 return -1;
305 }
306
307 return 0;
308 }
309
310
311 //-----------------------------------------------------------------------------
312 // cxoCursor_fetchRow()
313 // Fetch a single row from the cursor. Internally the number of rows left in
314 // the buffer is managed in order to minimize calls to Py_BEGIN_ALLOW_THREADS
315 // and Py_END_ALLOW_THREADS which have a significant overhead.
316 //-----------------------------------------------------------------------------
317 static int cxoCursor_fetchRow(cxoCursor *cursor, int *found,
318 uint32_t *bufferRowIndex)
319 {
320 int status;
321
322 // if the number of rows in the fetch buffer is zero and there are more
323 // rows to fetch, call DPI with threading enabled in order to perform any
324 // fetch requiring a network round trip
325 if (cursor->numRowsInFetchBuffer == 0 && cursor->moreRowsToFetch) {
326 Py_BEGIN_ALLOW_THREADS
327 status = dpiStmt_fetchRows(cursor->handle, cursor->fetchArraySize,
328 &cursor->fetchBufferRowIndex, &cursor->numRowsInFetchBuffer,
329 &cursor->moreRowsToFetch);
330 Py_END_ALLOW_THREADS
331 if (status < 0)
332 return cxoError_raiseAndReturnInt();
333 }
334
335 // keep track of where we are in the fetch buffer
336 if (cursor->numRowsInFetchBuffer == 0)
337 *found = 0;
338 else {
339 *found = 1;
340 *bufferRowIndex = cursor->fetchBufferRowIndex++;
341 cursor->numRowsInFetchBuffer--;
342 }
343
344 return 0;
345 }
346
347
348 //-----------------------------------------------------------------------------
349 // cxoCursor_performDefine()
350 // Perform the defines for the cursor. At this point it is assumed that the
351 // statement being executed is in fact a query.
352 //-----------------------------------------------------------------------------
353 static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
354 {
355 PyObject *outputTypeHandler, *result;
356 cxoObjectType *objectType;
357 cxoVarType *varType;
358 dpiQueryInfo queryInfo;
359 uint32_t pos, size;
360 cxoVar *var;
361
362 // initialize fetching variables; these are used to reduce the number of
363 // times that Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS is called as
364 // there is a significant amount of overhead in making these calls
365 cursor->numRowsInFetchBuffer = 0;
366 cursor->moreRowsToFetch = 1;
367
368 // if fetch variables already exist, nothing more to do (we are executing
369 // the same statement and therefore all defines have already been
370 // performed)
371 if (cursor->fetchVariables)
372 return 0;
373
374 // create a list corresponding to the number of items
375 cursor->fetchVariables = PyList_New(numQueryColumns);
376 if (!cursor->fetchVariables)
377 return -1;
378
379 // create a variable for each of the query columns
380 cursor->fetchArraySize = cursor->arraySize;
381 for (pos = 1; pos <= numQueryColumns; pos++) {
382
383 // get query information for the column position
384 if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0)
385 return cxoError_raiseAndReturnInt();
386 if (queryInfo.typeInfo.sizeInChars)
387 size = queryInfo.typeInfo.sizeInChars;
388 else size = queryInfo.typeInfo.clientSizeInBytes;
389
390 // determine object type, if applicable
391 objectType = NULL;
392 if (queryInfo.typeInfo.objectType) {
393 objectType = cxoObjectType_new(cursor->connection,
394 queryInfo.typeInfo.objectType);
395 if (!objectType)
396 return -1;
397 }
398
399 // determine the default type
400 varType = cxoVarType_fromDataTypeInfo(&queryInfo.typeInfo);
401 if (!varType)
402 return -1;
403
404 // see if an output type handler should be used
405 var = NULL;
406 outputTypeHandler = NULL;
407 if (cursor->outputTypeHandler && cursor->outputTypeHandler != Py_None)
408 outputTypeHandler = cursor->outputTypeHandler;
409 else if (cursor->connection->outputTypeHandler &&
410 cursor->connection->outputTypeHandler != Py_None)
411 outputTypeHandler = cursor->connection->outputTypeHandler;
412
413 // if using an output type handler, None implies default behavior
414 if (outputTypeHandler) {
415 result = PyObject_CallFunction(outputTypeHandler, "Os#Oiii",
416 cursor, queryInfo.name, queryInfo.nameLength,
417 varType->pythonType, size, queryInfo.typeInfo.precision,
418 queryInfo.typeInfo.scale);
419 if (!result) {
420 Py_XDECREF(objectType);
421 return -1;
422 } else if (result == Py_None)
423 Py_DECREF(result);
424 else if (!cxoVar_check(result)) {
425 Py_DECREF(result);
426 Py_XDECREF(objectType);
427 PyErr_SetString(PyExc_TypeError,
428 "expecting variable from output type handler");
429 return -1;
430 } else {
431 var = (cxoVar*) result;
432 if (var->allocatedElements < cursor->fetchArraySize) {
433 Py_DECREF(result);
434 Py_XDECREF(objectType);
435 PyErr_SetString(PyExc_TypeError,
436 "expecting variable with array size large "
437 "enough for fetch");
438 return -1;
439 }
440 }
441 }
442
443 // if no variable created yet, use the database metadata
444 if (!var) {
445 var = cxoVar_new(cursor, cursor->fetchArraySize, varType, size, 0,
446 objectType);
447 if (!var) {
448 Py_XDECREF(objectType);
449 return -1;
450 }
451 }
452
453 // add the variable to the fetch variables and perform define
454 Py_XDECREF(objectType);
455 PyList_SET_ITEM(cursor->fetchVariables, pos - 1, (PyObject *) var);
456 if (dpiStmt_define(cursor->handle, pos, var->handle) < 0)
457 return cxoError_raiseAndReturnInt();
458
459 }
460
461 return 0;
462 }
463
464
465 //-----------------------------------------------------------------------------
466 // cxoCursor_itemDescription()
467 // Return a tuple describing the item at the given position.
468 //-----------------------------------------------------------------------------
469 static PyObject *cxoCursor_itemDescription(cxoCursor *cursor, uint32_t pos)
470 {
471 cxoVarType *varType;
472 int displaySize, index;
473 dpiQueryInfo queryInfo;
474 PyObject *tuple, *temp;
475
476 // get information about the column position
477 if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0)
478 return NULL;
479 varType = cxoVarType_fromDataTypeInfo(&queryInfo.typeInfo);
480 if (!varType)
481 return NULL;
482
483 // set display size based on data type
484 switch (queryInfo.typeInfo.oracleTypeNum) {
485 case DPI_ORACLE_TYPE_VARCHAR:
486 case DPI_ORACLE_TYPE_NVARCHAR:
487 case DPI_ORACLE_TYPE_CHAR:
488 case DPI_ORACLE_TYPE_NCHAR:
489 case DPI_ORACLE_TYPE_ROWID:
490 displaySize = (int) queryInfo.typeInfo.sizeInChars;
491 break;
492 case DPI_ORACLE_TYPE_RAW:
493 displaySize = (int) queryInfo.typeInfo.clientSizeInBytes;
494 break;
495 case DPI_ORACLE_TYPE_NATIVE_FLOAT:
496 case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
497 case DPI_ORACLE_TYPE_NATIVE_INT:
498 case DPI_ORACLE_TYPE_NUMBER:
499 if (queryInfo.typeInfo.precision) {
500 displaySize = queryInfo.typeInfo.precision + 1;
501 if (queryInfo.typeInfo.scale > 0)
502 displaySize += queryInfo.typeInfo.scale + 1;
503 }
504 else displaySize = 127;
505 break;
506 case DPI_ORACLE_TYPE_DATE:
507 case DPI_ORACLE_TYPE_TIMESTAMP:
508 displaySize = 23;
509 break;
510 default:
511 displaySize = 0;
512 }
513
514 // create the tuple and populate it
515 tuple = PyTuple_New(7);
516 if (!tuple)
517 return NULL;
518
519 // set each of the items in the tuple
520 PyTuple_SET_ITEM(tuple, 0, cxoPyString_fromEncodedString(queryInfo.name,
521 queryInfo.nameLength, cursor->connection->encodingInfo.encoding,
522 NULL));
523 Py_INCREF(varType->pythonType);
524 PyTuple_SET_ITEM(tuple, 1, (PyObject*) varType->pythonType);
525 if (displaySize)
526 PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(displaySize));
527 else {
528 Py_INCREF(Py_None);
529 PyTuple_SET_ITEM(tuple, 2, Py_None);
530 }
531 if (queryInfo.typeInfo.clientSizeInBytes)
532 PyTuple_SET_ITEM(tuple, 3,
533 PyInt_FromLong(queryInfo.typeInfo.clientSizeInBytes));
534 else {
535 Py_INCREF(Py_None);
536 PyTuple_SET_ITEM(tuple, 3, Py_None);
537 }
538 if (queryInfo.typeInfo.precision || queryInfo.typeInfo.scale ||
539 queryInfo.typeInfo.fsPrecision) {
540 PyTuple_SET_ITEM(tuple, 4,
541 PyInt_FromLong(queryInfo.typeInfo.precision));
542 PyTuple_SET_ITEM(tuple, 5,
543 PyInt_FromLong(queryInfo.typeInfo.scale +
544 queryInfo.typeInfo.fsPrecision));
545 } else {
546 Py_INCREF(Py_None);
547 PyTuple_SET_ITEM(tuple, 4, Py_None);
548 Py_INCREF(Py_None);
549 PyTuple_SET_ITEM(tuple, 5, Py_None);
550 }
551 PyTuple_SET_ITEM(tuple, 6, PyInt_FromLong(queryInfo.nullOk != 0));
552
553 // make sure the tuple is ok
554 for (index = 0; index < 7; index++) {
555 temp = PyTuple_GET_ITEM(tuple, index);
556 if (!temp) {
557 Py_DECREF(tuple);
558 return NULL;
559 } else if (temp == Py_None)
560 Py_INCREF(temp);
561 }
562
563 return tuple;
564 }
565
566
567 //-----------------------------------------------------------------------------
568 // cxoCursor_getDescription()
569 // Return a list of 7-tuples consisting of the description of the define
570 // variables.
571 //-----------------------------------------------------------------------------
572 static PyObject *cxoCursor_getDescription(cxoCursor *cursor, void *unused)
573 {
574 uint32_t numQueryColumns, i;
575 PyObject *results, *tuple;
576
577 // make sure the cursor is open
578 if (cxoCursor_isOpen(cursor) < 0)
579 return NULL;
580
581 // determine the number of query columns; if not a query return None
582 if (!cursor->handle)
583 Py_RETURN_NONE;
584 if (dpiStmt_getNumQueryColumns(cursor->handle, &numQueryColumns) < 0)
585 return cxoError_raiseAndReturnNull();
586 if (numQueryColumns == 0)
587 Py_RETURN_NONE;
588
589 // create a list of the required length
590 results = PyList_New(numQueryColumns);
591 if (!results)
592 return NULL;
593
594 // create tuples corresponding to the select-items
595 for (i = 0; i < numQueryColumns; i++) {
596 tuple = cxoCursor_itemDescription(cursor, i + 1);
597 if (!tuple) {
598 Py_DECREF(results);
599 return NULL;
600 }
601 PyList_SET_ITEM(results, i, tuple);
602 }
603
604 return results;
605 }
606
607
608 //-----------------------------------------------------------------------------
609 // cxoCursor_close()
610 // Close the cursor. Any action taken on this cursor from this point forward
611 // results in an exception being raised.
612 //-----------------------------------------------------------------------------
613 static PyObject *cxoCursor_close(cxoCursor *cursor, PyObject *args)
614 {
615 if (cxoCursor_isOpen(cursor) < 0)
616 return NULL;
617 Py_CLEAR(cursor->bindVariables);
618 Py_CLEAR(cursor->fetchVariables);
619 if (cursor->handle) {
620 if (dpiStmt_close(cursor->handle, NULL, 0) < 0)
621 return cxoError_raiseAndReturnNull();
622 dpiStmt_release(cursor->handle);
623 cursor->handle = NULL;
624 }
625 cursor->isOpen = 0;
626
627 Py_RETURN_NONE;
628 }
629
630
631 //-----------------------------------------------------------------------------
632 // cxoCursor_setBindVariableHelper()
633 // Helper for setting a bind variable.
634 //-----------------------------------------------------------------------------
635 static int cxoCursor_setBindVariableHelper(cxoCursor *cursor,
636 unsigned numElements, unsigned arrayPos, PyObject *value,
637 cxoVar *origVar, cxoVar **newVar, int deferTypeAssignment)
638 {
639 cxoVar *varToSet;
640 int isValueVar;
641
642 // initialization
643 *newVar = NULL;
644 isValueVar = cxoVar_check(value);
645
646 // handle case where variable is already bound, either from a prior
647 // execution or a call to setinputsizes()
648 if (origVar) {
649
650 // if the value is a variable object, rebind it if necessary
651 if (isValueVar) {
652 if ( (PyObject*) origVar != value) {
653 Py_INCREF(value);
654 *newVar = (cxoVar*) value;
655 }
656
657 // otherwise, attempt to set the value, but if this fails, simply
658 // ignore the original bind variable and create a new one; this is
659 // intended for cases where the type changes between executions of a
660 // statement or where setinputsizes() has been called with the wrong
661 // type (as mandated by the DB API)
662 } else {
663 varToSet = origVar;
664
665 // first check to see if the variable transform is for None (which
666 // can happen if all of the values in a previous invocation of
667 // executemany() were None) and there is now a value; in this case,
668 // discard the original variable and have a new one created
669 if (origVar->type->transformNum == CXO_TRANSFORM_NONE &&
670 value != Py_None) {
671 origVar = NULL;
672 varToSet = NULL;
673
674 // otherwise, if the number of elements has changed, create a new
675 // variable this is only necessary for executemany() since
676 // execute() always passes a value of 1 for the number of elements
677 } else if (numElements > origVar->allocatedElements) {
678 *newVar = cxoVar_new(cursor, numElements, origVar->type,
679 origVar->size, origVar->isArray, origVar->objectType);
680 if (!*newVar)
681 return -1;
682 varToSet = *newVar;
683 }
684
685 // attempt to set the value
686 if (varToSet && cxoVar_setValue(varToSet, arrayPos, value) < 0) {
687
688 // executemany() should simply fail after the first element
689 if (arrayPos > 0)
690 return -1;
691
692 // clear the exception and try to create a new variable
693 PyErr_Clear();
694 Py_CLEAR(*newVar);
695 origVar = NULL;
696
697 }
698
699 }
700
701 }
702
703 // if no original variable used, create a new one
704 if (!origVar) {
705
706 // if the value is a variable object, bind it directly
707 if (isValueVar) {
708 Py_INCREF(value);
709 *newVar = (cxoVar*) value;
710
711 // otherwise, create a new variable, unless the value is None and
712 // we wish to defer type assignment
713 } else if (value != Py_None || !deferTypeAssignment) {
714 *newVar = cxoVar_newByValue(cursor, value, numElements);
715 if (!*newVar)
716 return -1;
717 if (cxoVar_setValue(*newVar, arrayPos, value) < 0) {
718 Py_CLEAR(*newVar);
719 return -1;
720 }
721 }
722
723 }
724
725 return 0;
726 }
727
728
729 //-----------------------------------------------------------------------------
730 // cxoCursor_setBindVariables()
731 // Create or set bind variables.
732 //-----------------------------------------------------------------------------
733 int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters,
734 unsigned numElements, unsigned arrayPos, int deferTypeAssignment)
735 {
736 uint32_t i, origBoundByPos, origNumParams, boundByPos, numParams;
737 PyObject *key, *value, *origVar;
738 cxoVar *newVar;
739 Py_ssize_t pos, temp;
740
741 // make sure positional and named binds are not being intermixed
742 origNumParams = numParams = 0;
743 boundByPos = PySequence_Check(parameters);
744 if (boundByPos) {
745 temp = PySequence_Size(parameters);
746 if (temp < 0)
747 return -1;
748 numParams = (uint32_t) temp;
749 }
750 if (cursor->bindVariables) {
751 origBoundByPos = PyList_Check(cursor->bindVariables);
752 if (boundByPos != origBoundByPos) {
753 cxoError_raiseFromString(cxoProgrammingErrorException,
754 "positional and named binds cannot be intermixed");
755 return -1;
756 }
757 if (origBoundByPos)
758 origNumParams = (uint32_t) PyList_GET_SIZE(cursor->bindVariables);
759
760 // otherwise, create the list or dictionary if needed
761 } else {
762 if (boundByPos)
763 cursor->bindVariables = PyList_New(numParams);
764 else cursor->bindVariables = PyDict_New();
765 if (!cursor->bindVariables)
766 return -1;
767 origNumParams = 0;
768 }
769
770 // handle positional binds
771 if (boundByPos) {
772 for (i = 0; i < numParams; i++) {
773 value = PySequence_GetItem(parameters, i);
774 if (!value)
775 return -1;
776 Py_DECREF(value);
777 if (i < origNumParams) {
778 origVar = PyList_GET_ITEM(cursor->bindVariables, i);
779 if (origVar == Py_None)
780 origVar = NULL;
781 } else origVar = NULL;
782 if (cxoCursor_setBindVariableHelper(cursor, numElements, arrayPos,
783 value, (cxoVar*) origVar, &newVar,
784 deferTypeAssignment) < 0)
785 return -1;
786 if (newVar) {
787 if (i < (uint32_t) PyList_GET_SIZE(cursor->bindVariables)) {
788 if (PyList_SetItem(cursor->bindVariables, i,
789 (PyObject*) newVar) < 0) {
790 Py_DECREF(newVar);
791 return -1;
792 }
793 } else {
794 if (PyList_Append(cursor->bindVariables,
795 (PyObject*) newVar) < 0) {
796 Py_DECREF(newVar);
797 return -1;
798 }
799 Py_DECREF(newVar);
800 }
801 }
802 }
803
804 // handle named binds
805 } else {
806 pos = 0;
807 while (PyDict_Next(parameters, &pos, &key, &value)) {
808 origVar = PyDict_GetItem(cursor->bindVariables, key);
809 if (cxoCursor_setBindVariableHelper(cursor, numElements, arrayPos,
810 value, (cxoVar*) origVar, &newVar,
811 deferTypeAssignment) < 0)
812 return -1;
813 if (newVar) {
814 if (PyDict_SetItem(cursor->bindVariables, key,
815 (PyObject*) newVar) < 0) {
816 Py_DECREF(newVar);
817 return -1;
818 }
819 Py_DECREF(newVar);
820 }
821 }
822 }
823
824 return 0;
825 }
826
827
828 //-----------------------------------------------------------------------------
829 // cxoCursor_performBind()
830 // Perform the binds on the cursor.
831 //-----------------------------------------------------------------------------
832 int cxoCursor_performBind(cxoCursor *cursor)
833 {
834 PyObject *key, *var;
835 Py_ssize_t pos;
836 int i;
837
838 // ensure that input sizes are reset
839 // this is done before binding is attempted so that if binding fails and
840 // a new statement is prepared, the bind variables will be reset and
841 // spurious errors will not occur
842 cursor->setInputSizes = 0;
843
844 // set values and perform binds for all bind variables
845 if (cursor->bindVariables) {
846 if (PyDict_Check(cursor->bindVariables)) {
847 pos = 0;
848 while (PyDict_Next(cursor->bindVariables, &pos, &key, &var)) {
849 if (cxoVar_bind((cxoVar*) var, cursor, key, 0) < 0)
850 return -1;
851 }
852 } else {
853 for (i = 0; i < PyList_GET_SIZE(cursor->bindVariables); i++) {
854 var = PyList_GET_ITEM(cursor->bindVariables, i);
855 if (var != Py_None) {
856 if (cxoVar_bind((cxoVar*) var, cursor, NULL,
857 i + 1) < 0)
858 return -1;
859 }
860 }
861 }
862 }
863
864 return 0;
865 }
866
867
868 //-----------------------------------------------------------------------------
869 // cxoCursor_createRow()
870 // Create an object for the row. The object created is a tuple unless a row
871 // factory function has been defined in which case it is the result of the
872 // row factory function called with the argument tuple that would otherwise be
873 // returned.
874 //-----------------------------------------------------------------------------
875 static PyObject *cxoCursor_createRow(cxoCursor *cursor, uint32_t pos)
876 {
877 PyObject *tuple, *item, *result;
878 Py_ssize_t numItems, i;
879 cxoVar *var;
880
881 // bump row count as a new row has been found
882 cursor->rowCount++;
883
884 // create a new tuple
885 numItems = PyList_GET_SIZE(cursor->fetchVariables);
886 tuple = PyTuple_New(numItems);
887 if (!tuple)
888 return NULL;
889
890 // acquire the value for each item
891 for (i = 0; i < numItems; i++) {
892 var = (cxoVar*) PyList_GET_ITEM(cursor->fetchVariables, i);
893 item = cxoVar_getSingleValue(var, var->data, pos);
894 if (!item) {
895 Py_DECREF(tuple);
896 return NULL;
897 }
898 PyTuple_SET_ITEM(tuple, i, item);
899 }
900
901 // if a row factory is defined, call it
902 if (cursor->rowFactory && cursor->rowFactory != Py_None) {
903 result = PyObject_CallObject(cursor->rowFactory, tuple);
904 Py_DECREF(tuple);
905 return result;
906 }
907
908 return tuple;
909 }
910
911
912 //-----------------------------------------------------------------------------
913 // cxoCursor_internalPrepare()
914 // Internal method for preparing a statement for execution.
915 //-----------------------------------------------------------------------------
916 static int cxoCursor_internalPrepare(cxoCursor *cursor, PyObject *statement,
917 PyObject *statementTag)
918 {
919 cxoBuffer statementBuffer, tagBuffer;
920 int status;
921
922 // make sure we don't get a situation where nothing is to be executed
923 if (statement == Py_None && !cursor->statement) {
924 cxoError_raiseFromString(cxoProgrammingErrorException,
925 "no statement specified and no prior statement prepared");
926 return -1;
927 }
928
929 // nothing to do if the statement is identical to the one already stored
930 // but go ahead and prepare anyway for create, alter and drop statments
931 if (statement == Py_None || statement == cursor->statement) {
932 if (cursor->handle && !cursor->stmtInfo.isDDL)
933 return 0;
934 statement = cursor->statement;
935 }
936
937 // keep track of the statement
938 Py_XDECREF(cursor->statement);
939 Py_INCREF(statement);
940 cursor->statement = statement;
941
942 // keep track of the tag
943 Py_XDECREF(cursor->statementTag);
944 Py_XINCREF(statementTag);
945 cursor->statementTag = statementTag;
946
947 // clear fetch and bind variables if applicable
948 Py_CLEAR(cursor->fetchVariables);
949 if (!cursor->setInputSizes)
950 Py_CLEAR(cursor->bindVariables);
951
952 // prepare statement
953 if (cxoBuffer_fromObject(&statementBuffer, statement,
954 cursor->connection->encodingInfo.encoding) < 0)
955 return -1;
956 if (cxoBuffer_fromObject(&tagBuffer, statementTag,
957 cursor->connection->encodingInfo.encoding) < 0) {
958 cxoBuffer_clear(&statementBuffer);
959 return -1;
960 }
961 Py_BEGIN_ALLOW_THREADS
962 if (cursor->handle)
963 dpiStmt_release(cursor->handle);
964 status = dpiConn_prepareStmt(cursor->connection->handle,
965 cursor->isScrollable, (const char*) statementBuffer.ptr,
966 statementBuffer.size, (const char*) tagBuffer.ptr, tagBuffer.size,
967 &cursor->handle);
968 Py_END_ALLOW_THREADS
969 cxoBuffer_clear(&statementBuffer);
970 cxoBuffer_clear(&tagBuffer);
971 if (status < 0)
972 return cxoError_raiseAndReturnInt();
973
974 // get statement information
975 if (dpiStmt_getInfo(cursor->handle, &cursor->stmtInfo) < 0)
976 return cxoError_raiseAndReturnInt();
977
978 // set the fetch array size, if applicable
979 if (cursor->stmtInfo.statementType == DPI_STMT_TYPE_SELECT) {
980 if (dpiStmt_setFetchArraySize(cursor->handle, cursor->arraySize) < 0)
981 return cxoError_raiseAndReturnInt();
982 }
983
984 // clear row factory, if applicable
985 Py_CLEAR(cursor->rowFactory);
986
987 return 0;
988 }
989
990
991 //-----------------------------------------------------------------------------
992 // cxoCursor_parse()
993 // Parse the statement without executing it. This also retrieves information
994 // about the select list for select statements.
995 //-----------------------------------------------------------------------------
996 static PyObject *cxoCursor_parse(cxoCursor *cursor, PyObject *statement)
997 {
998 uint32_t mode, numQueryColumns;
999 dpiStmtInfo stmtInfo;
1000 int status;
1001
1002 // make sure the cursor is open
1003 if (cxoCursor_isOpen(cursor) < 0)
1004 return NULL;
1005
1006 // prepare the statement and get statement information
1007 if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0)
1008 return NULL;
1009 if (dpiStmt_getInfo(cursor->handle, &stmtInfo) < 0)
1010 return cxoError_raiseAndReturnNull();
1011
1012 // parse the statement
1013 if (stmtInfo.isQuery)
1014 mode = DPI_MODE_EXEC_DESCRIBE_ONLY;
1015 else mode = DPI_MODE_EXEC_PARSE_ONLY;
1016 Py_BEGIN_ALLOW_THREADS
1017 status = dpiStmt_execute(cursor->handle, mode, &numQueryColumns);
1018 Py_END_ALLOW_THREADS
1019 if (status < 0)
1020 return cxoError_raiseAndReturnNull();
1021
1022 Py_RETURN_NONE;
1023 }
1024
1025
1026 //-----------------------------------------------------------------------------
1027 // cxoCursor_prepare()
1028 // Prepare the statement for execution.
1029 //-----------------------------------------------------------------------------
1030 static PyObject *cxoCursor_prepare(cxoCursor *cursor, PyObject *args)
1031 {
1032 PyObject *statement, *statementTag;
1033
1034 // statement text and optional tag is expected
1035 statementTag = NULL;
1036 if (!PyArg_ParseTuple(args, "O|O", &statement, &statementTag))
1037 return NULL;
1038
1039 // make sure the cursor is open
1040 if (cxoCursor_isOpen(cursor) < 0)
1041 return NULL;
1042
1043 // prepare the statement
1044 if (cxoCursor_internalPrepare(cursor, statement, statementTag) < 0)
1045 return NULL;
1046
1047 Py_RETURN_NONE;
1048 }
1049
1050
1051 //-----------------------------------------------------------------------------
1052 // cxoCursor_callCalculateSize()
1053 // Calculate the size of the statement that is to be executed.
1054 //-----------------------------------------------------------------------------
1055 static int cxoCursor_callCalculateSize(PyObject *name,
1056 cxoVar *returnValue, PyObject *listOfArguments,
1057 PyObject *keywordArguments, int *size)
1058 {
1059 Py_ssize_t numPositionalArgs, numKeywordArgs;
1060
1061 // set base size without any arguments
1062 *size = 17;
1063
1064 // add any additional space required to handle the return value
1065 if (returnValue)
1066 *size += 6;
1067
1068 // assume up to 9 characters for each positional argument
1069 // this allows up to four digits for the placeholder if the bind variale
1070 // is a boolean value (prior to Oracle 12.1)
1071 numPositionalArgs = 0;
1072 if (listOfArguments) {
1073 numPositionalArgs = PySequence_Size(listOfArguments);
1074 if (numPositionalArgs < 0)
1075 return -1;
1076 *size += (int) (numPositionalArgs * 9);
1077 }
1078
1079 // assume up to 15 characters for each keyword argument
1080 // this allows up to four digits for the placeholder if the bind variable
1081 // is a boolean value (prior to Oracle 12.1)
1082 numKeywordArgs = 0;
1083 if (keywordArguments) {
1084 numKeywordArgs = PyDict_Size(keywordArguments);
1085 if (numKeywordArgs < 0)
1086 return -1;
1087 *size += (int) (numKeywordArgs * 15);
1088 }
1089
1090 // the above assume a maximum of 10,000 arguments; check and raise an
1091 // error if the number of arguments exceeds this value; more than this
1092 // number would probably be unusable in any case!
1093 if (numPositionalArgs + numKeywordArgs > 10000) {
1094 cxoError_raiseFromString(cxoInterfaceErrorException,
1095 "too many arguments");
1096 return -1;
1097 }
1098
1099 return 0;
1100 }
1101
1102
1103 //-----------------------------------------------------------------------------
1104 // cxoCursor_callBuildStatement()
1105 // Determine the statement and the bind variables to bind to the statement
1106 // that is created for calling a stored procedure or function.
1107 //-----------------------------------------------------------------------------
1108 static int cxoCursor_callBuildStatement(PyObject *name,
1109 cxoVar *returnValue, PyObject *listOfArguments,
1110 PyObject *keywordArguments, char *statement, PyObject **statementObj,
1111 PyObject **bindVariables)
1112 {
1113 PyObject *key, *value, *formatArgs, *positionalArgs;
1114 uint32_t i, argNum, numPositionalArgs;
1115 Py_ssize_t pos;
1116 char *ptr;
1117
1118 // initialize the bind variables to the list of positional arguments
1119 if (listOfArguments)
1120 *bindVariables = PySequence_List(listOfArguments);
1121 else *bindVariables = PyList_New(0);
1122 if (!*bindVariables)
1123 return -1;
1124
1125 // insert the return variable, if applicable
1126 if (returnValue) {
1127 if (PyList_Insert(*bindVariables, 0, (PyObject*) returnValue) < 0)
1128 return -1;
1129 }
1130
1131 // initialize format arguments
1132 formatArgs = PyList_New(0);
1133 if (!formatArgs)
1134 return -1;
1135 if (PyList_Append(formatArgs, name) < 0) {
1136 Py_DECREF(formatArgs);
1137 return -1;
1138 }
1139
1140 // begin building the statement
1141 argNum = 1;
1142 strcpy(statement, "begin ");
1143 if (returnValue) {
1144 strcat(statement, ":1 := ");
1145 argNum++;
1146 }
1147 strcat(statement, "%s");
1148 ptr = statement + strlen(statement);
1149 *ptr++ = '(';
1150
1151 // include any positional arguments first
1152 if (listOfArguments) {
1153 positionalArgs = PySequence_Fast(listOfArguments,
1154 "expecting sequence of arguments");
1155 if (!positionalArgs) {
1156 Py_DECREF(formatArgs);
1157 return -1;
1158 }
1159 numPositionalArgs = (uint32_t) PySequence_Size(listOfArguments);
1160 for (i = 0; i < numPositionalArgs; i++) {
1161 if (i > 0)
1162 *ptr++ = ',';
1163 ptr += sprintf(ptr, ":%d", argNum++);
1164 if (cxoClientVersionInfo.versionNum < 12 &&
1165 PyBool_Check(PySequence_Fast_GET_ITEM(positionalArgs, i)))
1166 ptr += sprintf(ptr, " = 1");
1167 }
1168 Py_DECREF(positionalArgs);
1169 }
1170
1171 // next append any keyword arguments
1172 if (keywordArguments) {
1173 pos = 0;
1174 while (PyDict_Next(keywordArguments, &pos, &key, &value)) {
1175 if (PyList_Append(*bindVariables, value) < 0) {
1176 Py_DECREF(formatArgs);
1177 return -1;
1178 }
1179 if (PyList_Append(formatArgs, key) < 0) {
1180 Py_DECREF(formatArgs);
1181 return -1;
1182 }
1183 if ((argNum > 1 && !returnValue) || (argNum > 2 && returnValue))
1184 *ptr++ = ',';
1185 ptr += sprintf(ptr, "%%s => :%d", argNum++);
1186 if (cxoClientVersionInfo.versionNum < 12 &&
1187 PyBool_Check(value))
1188 ptr += sprintf(ptr, " = 1");
1189 }
1190 }
1191
1192 // create statement object
1193 strcpy(ptr, "); end;");
1194 *statementObj = cxoUtils_formatString(statement,
1195 PyList_AsTuple(formatArgs));
1196 Py_DECREF(formatArgs);
1197 if (!*statementObj)
1198 return -1;
1199
1200 return 0;
1201 }
1202
1203
1204 //-----------------------------------------------------------------------------
1205 // cxoCursor_call()
1206 // Call a stored procedure or function.
1207 //-----------------------------------------------------------------------------
1208 static int cxoCursor_call(cxoCursor *cursor, cxoVar *returnValue,
1209 PyObject *name, PyObject *listOfArguments, PyObject *keywordArguments)
1210 {
1211 PyObject *bindVariables, *statementObj, *results;
1212 int statementSize;
1213 char *statement;
1214
1215 // verify that the arguments are passed correctly
1216 if (listOfArguments) {
1217 if (!PySequence_Check(listOfArguments)) {
1218 PyErr_SetString(PyExc_TypeError, "arguments must be a sequence");
1219 return -1;
1220 }
1221 }
1222 if (keywordArguments) {
1223 if (!PyDict_Check(keywordArguments)) {
1224 PyErr_SetString(PyExc_TypeError,
1225 "keyword arguments must be a dictionary");
1226 return -1;
1227 }
1228 }
1229
1230 // make sure the cursor is open
1231 if (cxoCursor_isOpen(cursor) < 0)
1232 return -1;
1233
1234 // determine the statement size
1235 if (cxoCursor_callCalculateSize(name, returnValue, listOfArguments,
1236 keywordArguments, &statementSize) < 0)
1237 return -1;
1238
1239 // allocate a string for the statement
1240 statement = (char*) PyMem_Malloc(statementSize);
1241 if (!statement) {
1242 PyErr_NoMemory();
1243 return -1;
1244 }
1245
1246 // determine the statement to execute and the argument to pass
1247 bindVariables = statementObj = NULL;
1248 if (cxoCursor_callBuildStatement(name, returnValue, listOfArguments,
1249 keywordArguments, statement, &statementObj, &bindVariables) < 0) {
1250 PyMem_Free(statement);
1251 Py_XDECREF(statementObj);
1252 Py_XDECREF(bindVariables);
1253 return -1;
1254 }
1255 PyMem_Free(statement);
1256
1257 // execute the statement on the cursor
1258 results = PyObject_CallMethod( (PyObject*) cursor, "execute", "OO",
1259 statementObj, bindVariables);
1260 Py_DECREF(statementObj);
1261 Py_DECREF(bindVariables);
1262 if (!results)
1263 return -1;
1264 Py_DECREF(results);
1265
1266 return 0;
1267 }
1268
1269
1270 //-----------------------------------------------------------------------------
1271 // cxoCursor_callFunc()
1272 // Call a stored function and return the return value of the function.
1273 //-----------------------------------------------------------------------------
1274 static PyObject *cxoCursor_callFunc(cxoCursor *cursor, PyObject *args,
1275 PyObject *keywordArgs)
1276 {
1277 static char *keywordList[] = { "name", "returnType", "parameters",
1278 "keywordParameters", NULL };
1279 PyObject *listOfArguments, *keywordArguments, *returnType, *results, *name;
1280 cxoVar *var;
1281
1282 // parse arguments
1283 listOfArguments = keywordArguments = NULL;
1284 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|OO", keywordList,
1285 &name, &returnType, &listOfArguments, &keywordArguments))
1286 return NULL;
1287
1288 // create the return variable
1289 var = cxoVar_newByType(cursor, returnType, 1);
1290 if (!var)
1291 return NULL;
1292
1293 // call the function
1294 if (cxoCursor_call(cursor, var, name, listOfArguments,
1295 keywordArguments) < 0)
1296 return NULL;
1297
1298 // determine the results
1299 results = cxoVar_getValue(var, 0);
1300 Py_DECREF(var);
1301 return results;
1302 }
1303
1304
1305 //-----------------------------------------------------------------------------
1306 // cxoCursor_callProc()
1307 // Call a stored procedure and return the (possibly modified) arguments.
1308 //-----------------------------------------------------------------------------
1309 static PyObject *cxoCursor_callProc(cxoCursor *cursor, PyObject *args,
1310 PyObject *keywordArgs)
1311 {
1312 static char *keywordList[] = { "name", "parameters", "keywordParameters",
1313 NULL };
1314 PyObject *listOfArguments, *keywordArguments, *results, *var, *temp, *name;
1315 Py_ssize_t numArgs, i;
1316
1317 // parse arguments
1318 listOfArguments = keywordArguments = NULL;
1319 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OO", keywordList,
1320 &name, &listOfArguments, &keywordArguments))
1321 return NULL;
1322
1323 // call the stored procedure
1324 if (cxoCursor_call(cursor, NULL, name, listOfArguments,
1325 keywordArguments) < 0)
1326 return NULL;
1327
1328 // create the return value
1329 numArgs = PyList_GET_SIZE(cursor->bindVariables);
1330 results = PyList_New(numArgs);
1331 if (!results)
1332 return NULL;
1333 for (i = 0; i < numArgs; i++) {
1334 var = PyList_GET_ITEM(cursor->bindVariables, i);
1335 temp = cxoVar_getValue((cxoVar*) var, 0);
1336 if (!temp) {
1337 Py_DECREF(results);
1338 return NULL;
1339 }
1340 PyList_SET_ITEM(results, i, temp);
1341 }
1342
1343 return results;
1344 }
1345
1346
1347 //-----------------------------------------------------------------------------
1348 // cxoCursor_execute()
1349 // Execute the statement.
1350 //-----------------------------------------------------------------------------
1351 static PyObject *cxoCursor_execute(cxoCursor *cursor, PyObject *args,
1352 PyObject *keywordArgs)
1353 {
1354 PyObject *statement, *executeArgs;
1355 uint32_t numQueryColumns, mode;
1356 int status;
1357
1358 executeArgs = NULL;
1359 if (!PyArg_ParseTuple(args, "O|O", &statement, &executeArgs))
1360 return NULL;
1361 if (executeArgs && keywordArgs) {
1362 if (PyDict_Size(keywordArgs) == 0)
1363 keywordArgs = NULL;
1364 else return cxoError_raiseFromString(cxoInterfaceErrorException,
1365 "expecting argument or keyword arguments, not both");
1366 }
1367 if (keywordArgs)
1368 executeArgs = keywordArgs;
1369 if (executeArgs) {
1370 if (!PyDict_Check(executeArgs) && !PySequence_Check(executeArgs)) {
1371 PyErr_SetString(PyExc_TypeError,
1372 "expecting a dictionary, sequence or keyword args");
1373 return NULL;
1374 }
1375 }
1376
1377 // make sure the cursor is open
1378 if (cxoCursor_isOpen(cursor) < 0)
1379 return NULL;
1380
1381 // prepare the statement, if applicable
1382 if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0)
1383 return NULL;
1384
1385 // perform binds
1386 if (executeArgs && cxoCursor_setBindVariables(cursor, executeArgs, 1, 0,
1387 0) < 0)
1388 return NULL;
1389 if (cxoCursor_performBind(cursor) < 0)
1390 return NULL;
1391
1392 // execute the statement
1393 Py_BEGIN_ALLOW_THREADS
1394 mode = (cursor->connection->autocommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS :
1395 DPI_MODE_EXEC_DEFAULT;
1396 status = dpiStmt_execute(cursor->handle, mode, &numQueryColumns);
1397 Py_END_ALLOW_THREADS
1398 if (status < 0)
1399 return cxoError_raiseAndReturnNull();
1400
1401 // get the count of the rows affected
1402 if (dpiStmt_getRowCount(cursor->handle, &cursor->rowCount) < 0)
1403 return cxoError_raiseAndReturnNull();
1404
1405 // for queries, return the cursor for convenience
1406 if (numQueryColumns > 0) {
1407 if (cxoCursor_performDefine(cursor, numQueryColumns) < 0) {
1408 Py_CLEAR(cursor->fetchVariables);
1409 return NULL;
1410 }
1411 Py_INCREF(cursor);
1412 return (PyObject*) cursor;
1413 }
1414
1415 // for statements other than queries, simply return None
1416 Py_RETURN_NONE;
1417 }
1418
1419
1420 //-----------------------------------------------------------------------------
1421 // cxoCursor_executeMany()
1422 // Execute the statement many times. The number of times is equivalent to the
1423 // number of elements in the list of parameters, or the provided integer if no
1424 // parameters are required.
1425 //-----------------------------------------------------------------------------
1426 static PyObject *cxoCursor_executeMany(cxoCursor *cursor, PyObject *args,
1427 PyObject *keywordArgs)
1428 {
1429 static char *keywordList[] = { "statement", "parameters", "batcherrors",
1430 "arraydmlrowcounts", NULL };
1431 int arrayDMLRowCountsEnabled = 0, batchErrorsEnabled = 0;
1432 PyObject *arguments, *parameters, *statement;
1433 uint32_t mode, i, numRows;
1434 int status;
1435
1436 // validate parameters
1437 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|ii", keywordList,
1438 &statement, &parameters, &batchErrorsEnabled,
1439 &arrayDMLRowCountsEnabled))
1440 return NULL;
1441 if (!PyList_Check(parameters) && !PyInt_Check(parameters)) {
1442 PyErr_SetString(PyExc_TypeError,
1443 "parameters should be a list of sequences/dictionaries "
1444 "or an integer specifying the number of times to execute "
1445 "the statement");
1446 return NULL;
1447 }
1448
1449 // make sure the cursor is open
1450 if (cxoCursor_isOpen(cursor) < 0)
1451 return NULL;
1452
1453 // determine execution mode
1454 mode = (cursor->connection->autocommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS :
1455 DPI_MODE_EXEC_DEFAULT;
1456 if (batchErrorsEnabled)
1457 mode |= DPI_MODE_EXEC_BATCH_ERRORS;
1458 if (arrayDMLRowCountsEnabled)
1459 mode |= DPI_MODE_EXEC_ARRAY_DML_ROWCOUNTS;
1460
1461 // prepare the statement
1462 if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0)
1463 return NULL;
1464
1465 // perform binds, as required
1466 if (PyInt_Check(parameters))
1467 numRows = (uint32_t) PyInt_AsLong(parameters);
1468 else {
1469 numRows = (uint32_t) PyList_GET_SIZE(parameters);
1470 for (i = 0; i < numRows; i++) {
1471 arguments = PyList_GET_ITEM(parameters, i);
1472 if (!PyDict_Check(arguments) && !PySequence_Check(arguments))
1473 return cxoError_raiseFromString(cxoInterfaceErrorException,
1474 "expecting a list of dictionaries or sequences");
1475 if (cxoCursor_setBindVariables(cursor, arguments, numRows, i,
1476 (i < numRows - 1)) < 0)
1477 return NULL;
1478 }
1479 }
1480 if (cxoCursor_performBind(cursor) < 0)
1481 return NULL;
1482
1483 // execute the statement, but only if the number of rows is greater than
1484 // zero since Oracle raises an error otherwise
1485 if (numRows > 0) {
1486 Py_BEGIN_ALLOW_THREADS
1487 status = dpiStmt_executeMany(cursor->handle, mode, numRows);
1488 Py_END_ALLOW_THREADS
1489 if (status < 0) {
1490 cxoError_raiseAndReturnNull();
1491 dpiStmt_getRowCount(cursor->handle, &cursor->rowCount);
1492 return NULL;
1493 }
1494 if (dpiStmt_getRowCount(cursor->handle, &cursor->rowCount) < 0)
1495 return cxoError_raiseAndReturnNull();
1496 }
1497
1498 Py_RETURN_NONE;
1499 }
1500
1501
1502 //-----------------------------------------------------------------------------
1503 // cxoCursor_executeManyPrepared()
1504 // Execute the prepared statement the number of times requested. At this
1505 // point, the statement must have been already prepared and the bind variables
1506 // must have their values set.
1507 //-----------------------------------------------------------------------------
1508 static PyObject *cxoCursor_executeManyPrepared(cxoCursor *cursor,
1509 PyObject *args)
1510 {
1511 int numIters, status;
1512
1513 // expect number of times to execute the statement
1514 if (!PyArg_ParseTuple(args, "i", &numIters))
1515 return NULL;
1516
1517 // make sure the cursor is open
1518 if (cxoCursor_isOpen(cursor) < 0)
1519 return NULL;
1520
1521 // perform binds
1522 if (cxoCursor_performBind(cursor) < 0)
1523 return NULL;
1524
1525 // execute the statement
1526 Py_BEGIN_ALLOW_THREADS
1527 status = dpiStmt_executeMany(cursor->handle, DPI_MODE_EXEC_DEFAULT,
1528 numIters);
1529 Py_END_ALLOW_THREADS
1530 if (status < 0 || dpiStmt_getRowCount(cursor->handle,
1531 &cursor->rowCount) < 0)
1532 return cxoError_raiseAndReturnNull();
1533
1534 Py_RETURN_NONE;
1535 }
1536
1537
1538 //-----------------------------------------------------------------------------
1539 // cxoCursor_multiFetch()
1540 // Return a list consisting of the remaining rows up to the given row limit
1541 // (if specified).
1542 //-----------------------------------------------------------------------------
1543 static PyObject *cxoCursor_multiFetch(cxoCursor *cursor, int rowLimit)
1544 {
1545 uint32_t bufferRowIndex = 0;
1546 PyObject *results, *row;
1547 int found, rowNum;
1548
1549 // verify fetch can be performed
1550 if (cxoCursor_verifyFetch(cursor) < 0)
1551 return NULL;
1552
1553 // create an empty list
1554 results = PyList_New(0);
1555 if (!results)
1556 return NULL;
1557
1558 // fetch as many rows as possible
1559 for (rowNum = 0; rowLimit == 0 || rowNum < rowLimit; rowNum++) {
1560 if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0) {
1561 Py_DECREF(results);
1562 return NULL;
1563 }
1564 if (!found)
1565 break;
1566 row = cxoCursor_createRow(cursor, bufferRowIndex);
1567 if (!row) {
1568 Py_DECREF(results);
1569 return NULL;
1570 }
1571 if (PyList_Append(results, row) < 0) {
1572 Py_DECREF(row);
1573 Py_DECREF(results);
1574 return NULL;
1575 }
1576 Py_DECREF(row);
1577 }
1578
1579 return results;
1580 }
1581
1582
1583 //-----------------------------------------------------------------------------
1584 // cxoCursor_fetchOne()
1585 // Fetch a single row from the cursor.
1586 //-----------------------------------------------------------------------------
1587 static PyObject *cxoCursor_fetchOne(cxoCursor *cursor, PyObject *args)
1588 {
1589 uint32_t bufferRowIndex = 0;
1590 int found = 0;
1591
1592 if (cxoCursor_verifyFetch(cursor) < 0)
1593 return NULL;
1594 if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0)
1595 return NULL;
1596 if (found)
1597 return cxoCursor_createRow(cursor, bufferRowIndex);
1598
1599 Py_RETURN_NONE;
1600 }
1601
1602
1603 //-----------------------------------------------------------------------------
1604 // cxoCursor_fetchMany()
1605 // Fetch multiple rows from the cursor based on the arraysize.
1606 //-----------------------------------------------------------------------------
1607 static PyObject *cxoCursor_fetchMany(cxoCursor *cursor, PyObject *args,
1608 PyObject *keywordArgs)
1609 {
1610 static char *keywordList[] = { "numRows", NULL };
1611 int rowLimit;
1612
1613 // parse arguments -- optional rowlimit expected
1614 rowLimit = cursor->arraySize;
1615 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
1616 &rowLimit))
1617 return NULL;
1618
1619 return cxoCursor_multiFetch(cursor, rowLimit);
1620 }
1621
1622
1623 //-----------------------------------------------------------------------------
1624 // cxoCursor_fetchAll()
1625 // Fetch all remaining rows from the cursor.
1626 //-----------------------------------------------------------------------------
1627 static PyObject *cxoCursor_fetchAll(cxoCursor *cursor, PyObject *args)
1628 {
1629 return cxoCursor_multiFetch(cursor, 0);
1630 }
1631
1632
1633 //-----------------------------------------------------------------------------
1634 // cxoCursor_fetchRaw()
1635 // Perform raw fetch on the cursor; return the actual number of rows fetched.
1636 //-----------------------------------------------------------------------------
1637 static PyObject *cxoCursor_fetchRaw(cxoCursor *cursor, PyObject *args,
1638 PyObject *keywordArgs)
1639 {
1640 static char *keywordList[] = { "numRows", NULL };
1641 uint32_t numRowsToFetch, numRowsFetched, bufferRowIndex;
1642 int moreRows;
1643
1644 // expect an optional number of rows to retrieve
1645 numRowsToFetch = cursor->fetchArraySize;
1646 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
1647 &numRowsToFetch))
1648 return NULL;
1649 if (numRowsToFetch > cursor->fetchArraySize)
1650 return cxoError_raiseFromString(cxoInterfaceErrorException,
1651 "rows to fetch exceeds array size");
1652
1653 // perform the fetch
1654 if (dpiStmt_fetchRows(cursor->handle, numRowsToFetch, &bufferRowIndex,
1655 &numRowsFetched, &moreRows) < 0)
1656 return cxoError_raiseAndReturnNull();
1657 cursor->rowCount += numRowsFetched;
1658 cursor->numRowsInFetchBuffer = 0;
1659 return PyInt_FromLong(numRowsFetched);
1660 }
1661
1662
1663 //-----------------------------------------------------------------------------
1664 // cxoCursor_scroll()
1665 // Scroll the cursor using the value and mode specified.
1666 //-----------------------------------------------------------------------------
1667 static PyObject *cxoCursor_scroll(cxoCursor *cursor, PyObject *args,
1668 PyObject *keywordArgs)
1669 {
1670 static char *keywordList[] = { "value", "mode", NULL };
1671 dpiFetchMode mode;
1672 int32_t offset;
1673 char *strMode;
1674 int status;
1675
1676 // parse arguments
1677 offset = 0;
1678 strMode = NULL;
1679 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|is", keywordList,
1680 &offset, &strMode))
1681 return NULL;
1682
1683 // validate mode
1684 if (!strMode)
1685 mode = DPI_MODE_FETCH_RELATIVE;
1686 else if (strcmp(strMode, "relative") == 0)
1687 mode = DPI_MODE_FETCH_RELATIVE;
1688 else if (strcmp(strMode, "absolute") == 0)
1689 mode = DPI_MODE_FETCH_ABSOLUTE;
1690 else if (strcmp(strMode, "first") == 0)
1691 mode = DPI_MODE_FETCH_FIRST;
1692 else if (strcmp(strMode, "last") == 0)
1693 mode = DPI_MODE_FETCH_LAST;
1694 else return cxoError_raiseFromString(cxoInterfaceErrorException,
1695 "mode must be one of relative, absolute, first or last");
1696
1697 // make sure the cursor is open
1698 if (cxoCursor_isOpen(cursor) < 0)
1699 return NULL;
1700
1701 // perform scroll and get new row count and number of rows in buffer
1702 Py_BEGIN_ALLOW_THREADS
1703 status = dpiStmt_scroll(cursor->handle, mode, offset,
1704 0 - cursor->numRowsInFetchBuffer);
1705 if (status == 0)
1706 status = dpiStmt_fetchRows(cursor->handle, cursor->fetchArraySize,
1707 &cursor->fetchBufferRowIndex, &cursor->numRowsInFetchBuffer,
1708 &cursor->moreRowsToFetch);
1709 if (status == 0)
1710 status = dpiStmt_getRowCount(cursor->handle, &cursor->rowCount);
1711 Py_END_ALLOW_THREADS
1712 if (status < 0)
1713 return cxoError_raiseAndReturnNull();
1714 cursor->rowCount -= cursor->numRowsInFetchBuffer;
1715
1716 Py_RETURN_NONE;
1717 }
1718
1719
1720 //-----------------------------------------------------------------------------
1721 // cxoCursor_setInputSizes()
1722 // Set the sizes of the bind variables.
1723 //-----------------------------------------------------------------------------
1724 static PyObject *cxoCursor_setInputSizes(cxoCursor *cursor, PyObject *args,
1725 PyObject *keywordArgs)
1726 {
1727 Py_ssize_t numPositionalArgs, numKeywordArgs = 0, i;
1728 PyObject *key, *value;
1729 cxoVar *var;
1730
1731 // only expect keyword arguments or positional arguments, not both
1732 numPositionalArgs = PyTuple_Size(args);
1733 if (keywordArgs)
1734 numKeywordArgs = PyDict_Size(keywordArgs);
1735 if (numKeywordArgs > 0 && numPositionalArgs > 0)
1736 return cxoError_raiseFromString(cxoInterfaceErrorException,
1737 "expecting arguments or keyword arguments, not both");
1738
1739 // make sure the cursor is open
1740 if (cxoCursor_isOpen(cursor) < 0)
1741 return NULL;
1742
1743 // eliminate existing bind variables
1744 Py_CLEAR(cursor->bindVariables);
1745
1746 // if no values passed, do nothing further, but return an empty list or
1747 // dictionary as appropriate
1748 if (numKeywordArgs == 0 && numPositionalArgs == 0) {
1749 if (keywordArgs)
1750 return PyDict_New();
1751 return PyList_New(0);
1752 }
1753
1754 // retain bind variables
1755 cursor->setInputSizes = 1;
1756 if (numKeywordArgs > 0)
1757 cursor->bindVariables = PyDict_New();
1758 else cursor->bindVariables = PyList_New(numPositionalArgs);
1759 if (!cursor->bindVariables)
1760 return NULL;
1761
1762 // process each input
1763 if (numKeywordArgs > 0) {
1764 i = 0;
1765 while (PyDict_Next(keywordArgs, &i, &key, &value)) {
1766 var = cxoVar_newByType(cursor, value, cursor->bindArraySize);
1767 if (!var)
1768 return NULL;
1769 if (PyDict_SetItem(cursor->bindVariables, key,
1770 (PyObject*) var) < 0) {
1771 Py_DECREF(var);
1772 return NULL;
1773 }
1774 Py_DECREF(var);
1775 }
1776 } else {
1777 for (i = 0; i < numPositionalArgs; i++) {
1778 value = PyTuple_GET_ITEM(args, i);
1779 if (value == Py_None) {
1780 Py_INCREF(Py_None);
1781 PyList_SET_ITEM(cursor->bindVariables, i, Py_None);
1782 } else {
1783 var = cxoVar_newByType(cursor, value, cursor->bindArraySize);
1784 if (!var)
1785 return NULL;
1786 PyList_SET_ITEM(cursor->bindVariables, i, (PyObject*) var);
1787 }
1788 }
1789 }
1790
1791 Py_INCREF(cursor->bindVariables);
1792 return cursor->bindVariables;
1793 }
1794
1795
1796 //-----------------------------------------------------------------------------
1797 // cxoCursor_setOutputSize()
1798 // Does nothing as ODPI-C handles long columns dynamically without the need
1799 // to specify a maximum length.
1800 //-----------------------------------------------------------------------------
1801 static PyObject *cxoCursor_setOutputSize(cxoCursor *cursor, PyObject *args)
1802 {
1803 int outputSize, outputSizeColumn;
1804
1805 if (!PyArg_ParseTuple(args, "i|i", &outputSize, &outputSizeColumn))
1806 return NULL;
1807 Py_RETURN_NONE;
1808 }
1809
1810
1811 //-----------------------------------------------------------------------------
1812 // cxoCursor_var()
1813 // Create a bind variable and return it.
1814 //-----------------------------------------------------------------------------
1815 static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
1816 PyObject *keywordArgs)
1817 {
1818 static char *keywordList[] = { "type", "size", "arraysize",
1819 "inconverter", "outconverter", "typename", "encodingErrors",
1820 NULL };
1821 PyObject *inConverter, *outConverter, *typeNameObj;
1822 const char *encodingErrors;
1823 cxoObjectType *objType;
1824 cxoVarType *varType;
1825 int size, arraySize;
1826 PyObject *type;
1827 cxoVar *var;
1828
1829 // parse arguments
1830 size = 0;
1831 encodingErrors = NULL;
1832 arraySize = cursor->bindArraySize;
1833 inConverter = outConverter = typeNameObj = NULL;
1834 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|iiOOOz",
1835 keywordList, &type, &size, &arraySize, &inConverter, &outConverter,
1836 &typeNameObj, &encodingErrors))
1837 return NULL;
1838
1839 // determine the type of variable
1840 varType = cxoVarType_fromPythonType(type, &objType);
1841 if (!varType)
1842 return NULL;
1843 Py_XINCREF(objType);
1844 if (size == 0)
1845 size = varType->size;
1846 if (typeNameObj && typeNameObj != Py_None && !objType) {
1847 objType = cxoObjectType_newByName(cursor->connection, typeNameObj);
1848 if (!objType)
1849 return NULL;
1850 }
1851
1852 // create the variable
1853 var = cxoVar_new(cursor, arraySize, varType, size, 0, objType);
1854 Py_XDECREF(objType);
1855 if (!var)
1856 return NULL;
1857 Py_XINCREF(inConverter);
1858 var->inConverter = inConverter;
1859 Py_XINCREF(outConverter);
1860 var->outConverter = outConverter;
1861
1862 // assign encoding errors, if applicable
1863 if (encodingErrors) {
1864 var->encodingErrors = PyMem_Malloc(strlen(encodingErrors) + 1);
1865 if (!var->encodingErrors) {
1866 Py_DECREF(var);
1867 return NULL;
1868 }
1869 strcpy((char*) var->encodingErrors, encodingErrors);
1870 }
1871
1872 return (PyObject*) var;
1873 }
1874
1875
1876 //-----------------------------------------------------------------------------
1877 // cxoCursor_arrayVar()
1878 // Create an array bind variable and return it.
1879 //-----------------------------------------------------------------------------
1880 static PyObject *cxoCursor_arrayVar(cxoCursor *cursor, PyObject *args)
1881 {
1882 uint32_t size, numElements;
1883 PyObject *type, *value;
1884 cxoObjectType *objType;
1885 cxoVarType *varType;
1886 cxoVar *var;
1887
1888 // parse arguments
1889 size = 0;
1890 if (!PyArg_ParseTuple(args, "O!O|i", &PyType_Type, &type, &value, &size))
1891 return NULL;
1892
1893 // determine the type of variable
1894 varType = cxoVarType_fromPythonType(type, &objType);
1895 if (!varType)
1896 return NULL;
1897 if (size == 0)
1898 size = varType->size;
1899
1900 // determine the number of elements to create
1901 if (PyList_Check(value))
1902 numElements = (uint32_t) PyList_GET_SIZE(value);
1903 else if (PyInt_Check(value)) {
1904 numElements = (uint32_t) PyInt_AsLong(value);
1905 if (PyErr_Occurred())
1906 return NULL;
1907 } else {
1908 PyErr_SetString(PyExc_TypeError,
1909 "expecting integer or list of values");
1910 return NULL;
1911 }
1912
1913 // create the variable
1914 var = cxoVar_new(cursor, numElements, varType, size, 1, objType);
1915 if (!var)
1916 return NULL;
1917
1918 // set the value, if applicable
1919 if (PyList_Check(value)) {
1920 if (cxoVar_setValue(var, 0, value) < 0)
1921 return NULL;
1922 }
1923
1924 return (PyObject*) var;
1925 }
1926
1927
1928 //-----------------------------------------------------------------------------
1929 // cxoCursor_bindNames()
1930 // Return a list of bind variable names.
1931 //-----------------------------------------------------------------------------
1932 static PyObject *cxoCursor_bindNames(cxoCursor *cursor, PyObject *args)
1933 {
1934 uint32_t numBinds, *nameLengths, i;
1935 PyObject *namesList, *temp;
1936 const char **names;
1937
1938 // make sure the cursor is open
1939 if (cxoCursor_isOpen(cursor) < 0)
1940 return NULL;
1941
1942 // ensure that a statement has already been prepared
1943 if (!cursor->statement)
1944 return cxoError_raiseFromString(cxoProgrammingErrorException,
1945 "statement must be prepared first");
1946
1947 // determine the number of binds
1948 if (dpiStmt_getBindCount(cursor->handle, &numBinds) < 0)
1949 return cxoError_raiseAndReturnNull();
1950
1951 // if the number of binds is zero, nothing to do
1952 if (numBinds == 0)
1953 return PyList_New(0);
1954
1955 // allocate memory for the bind names and their lengths
1956 names = (const char**) PyMem_Malloc(numBinds * sizeof(char*));
1957 if (!names)
1958 return PyErr_NoMemory();
1959 nameLengths = (uint32_t*) PyMem_Malloc(numBinds * sizeof(uint32_t));
1960 if (!nameLengths) {
1961 PyMem_Free((void*) names);
1962 return PyErr_NoMemory();
1963 }
1964
1965 // get the bind names
1966 if (dpiStmt_getBindNames(cursor->handle, &numBinds, names,
1967 nameLengths) < 0) {
1968 PyMem_Free((void*) names);
1969 PyMem_Free(nameLengths);
1970 return cxoError_raiseAndReturnNull();
1971 }
1972
1973 // populate list with the results
1974 namesList = PyList_New(numBinds);
1975 if (namesList) {
1976 for (i = 0; i < numBinds; i++) {
1977 temp = cxoPyString_fromEncodedString(names[i], nameLengths[i],
1978 cursor->connection->encodingInfo.encoding, NULL);
1979 if (!temp) {
1980 Py_CLEAR(namesList);
1981 break;
1982 }
1983 PyList_SET_ITEM(namesList, i, temp);
1984 }
1985 }
1986 PyMem_Free((void*) names);
1987 PyMem_Free(nameLengths);
1988 return namesList;
1989 }
1990
1991
1992 //-----------------------------------------------------------------------------
1993 // cxoCursor_getIter()
1994 // Return a reference to the cursor which supports the iterator protocol.
1995 //-----------------------------------------------------------------------------
1996 static PyObject *cxoCursor_getIter(cxoCursor *cursor)
1997 {
1998 if (cxoCursor_verifyFetch(cursor) < 0)
1999 return NULL;
2000 Py_INCREF(cursor);
2001 return (PyObject*) cursor;
2002 }
2003
2004
2005 //-----------------------------------------------------------------------------
2006 // cxoCursor_getNext()
2007 // Return a reference to the cursor which supports the iterator protocol.
2008 //-----------------------------------------------------------------------------
2009 static PyObject *cxoCursor_getNext(cxoCursor *cursor)
2010 {
2011 uint32_t bufferRowIndex = 0;
2012 int found = 0;
2013
2014 if (cxoCursor_verifyFetch(cursor) < 0)
2015 return NULL;
2016 if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0)
2017 return NULL;
2018 if (found)
2019 return cxoCursor_createRow(cursor, bufferRowIndex);
2020
2021 // no more rows, return NULL without setting an exception
2022 return NULL;
2023 }
2024
2025
2026 //-----------------------------------------------------------------------------
2027 // cxoCursor_getBatchErrors()
2028 // Returns a list of batch error objects.
2029 //-----------------------------------------------------------------------------
2030 static PyObject* cxoCursor_getBatchErrors(cxoCursor *cursor)
2031 {
2032 uint32_t numErrors, i;
2033 dpiErrorInfo *errors;
2034 PyObject *result;
2035 cxoError *error;
2036
2037 // determine the number of errors
2038 if (dpiStmt_getBatchErrorCount(cursor->handle, &numErrors) < 0)
2039 return cxoError_raiseAndReturnNull();
2040 if (numErrors == 0)
2041 return PyList_New(0);
2042
2043 // allocate memory for the errors
2044 errors = PyMem_Malloc(numErrors * sizeof(dpiErrorInfo));
2045 if (!errors)
2046 return PyErr_NoMemory();
2047
2048 // get error information
2049 if (dpiStmt_getBatchErrors(cursor->handle, numErrors, errors) < 0) {
2050 PyMem_Free(errors);
2051 return cxoError_raiseAndReturnNull();
2052 }
2053
2054 // create result
2055 result = PyList_New(numErrors);
2056 if (result) {
2057 for (i = 0; i < numErrors; i++) {
2058 error = cxoError_newFromInfo(&errors[i]);
2059 if (!error) {
2060 Py_CLEAR(result);
2061 break;
2062 }
2063 PyList_SET_ITEM(result, i, (PyObject*) error);
2064 }
2065 }
2066 PyMem_Free(errors);
2067 return result;
2068 }
2069
2070
2071 //-----------------------------------------------------------------------------
2072 // cxoCursor_getArrayDMLRowCounts
2073 // Populates the array dml row count list.
2074 //-----------------------------------------------------------------------------
2075 static PyObject* cxoCursor_getArrayDMLRowCounts(cxoCursor *cursor)
2076 {
2077 PyObject *result, *element;
2078 uint32_t numRowCounts, i;
2079 uint64_t *rowCounts;
2080
2081 // get row counts from DPI
2082 if (dpiStmt_getRowCounts(cursor->handle, &numRowCounts, &rowCounts) < 0)
2083 return cxoError_raiseAndReturnNull();
2084
2085 // return array
2086 result = PyList_New(numRowCounts);
2087 if (!result)
2088 return NULL;
2089 for (i = 0; i < numRowCounts; i++) {
2090 element = PyLong_FromUnsignedLong((unsigned long) rowCounts[i]);
2091 if (!element) {
2092 Py_DECREF(result);
2093 return NULL;
2094 }
2095 PyList_SET_ITEM(result, i, element);
2096 }
2097
2098 return result;
2099 }
2100
2101
2102 //-----------------------------------------------------------------------------
2103 // cxoCursor_getImplicitResults
2104 // Return a list of cursors available implicitly after execution of a PL/SQL
2105 // block or stored procedure. If none are available, an empty list is returned.
2106 //-----------------------------------------------------------------------------
2107 static PyObject *cxoCursor_getImplicitResults(cxoCursor *cursor)
2108 {
2109 cxoCursor *childCursor;
2110 dpiStmt *childStmt;
2111 PyObject *result;
2112
2113 // make sure the cursor is open
2114 if (cxoCursor_isOpen(cursor) < 0)
2115 return NULL;
2116
2117 // make sure we have a statement executed (handle defined)
2118 if (!cursor->handle)
2119 return cxoError_raiseFromString(cxoInterfaceErrorException,
2120 "no statement executed");
2121
2122 // create result
2123 result = PyList_New(0);
2124 if (!result)
2125 return NULL;
2126 while (1) {
2127 if (dpiStmt_getImplicitResult(cursor->handle, &childStmt) < 0)
2128 return cxoError_raiseAndReturnNull();
2129 if (!childStmt)
2130 break;
2131 childCursor = (cxoCursor*) PyObject_CallMethod(
2132 (PyObject*) cursor->connection, "cursor", NULL);
2133 if (!childCursor) {
2134 dpiStmt_release(childStmt);
2135 Py_DECREF(result);
2136 return NULL;
2137 }
2138 childCursor->handle = childStmt;
2139 childCursor->fixupRefCursor = 1;
2140 if (PyList_Append(result, (PyObject*) childCursor) < 0) {
2141 Py_DECREF(result);
2142 Py_DECREF(childCursor);
2143 return NULL;
2144 }
2145 Py_DECREF(childCursor);
2146 }
2147
2148 return result;
2149 }
2150
2151
2152 //-----------------------------------------------------------------------------
2153 // cxoCursor_contextManagerEnter()
2154 // Called when the cursor is used as a context manager and simply returns it
2155 // to the caller.
2156 //-----------------------------------------------------------------------------
2157 static PyObject *cxoCursor_contextManagerEnter(cxoCursor *cursor,
2158 PyObject* args)
2159 {
2160 Py_INCREF(cursor);
2161 return (PyObject*) cursor;
2162 }
2163
2164
2165 //-----------------------------------------------------------------------------
2166 // cxoCursor_contextManagerExit()
2167 // Called when the cursor is used as a context manager and simply closes the
2168 // cursor.
2169 //-----------------------------------------------------------------------------
2170 static PyObject *cxoCursor_contextManagerExit(cxoCursor *cursor,
2171 PyObject* args)
2172 {
2173 PyObject *excType, *excValue, *excTraceback, *result;
2174
2175 if (!PyArg_ParseTuple(args, "OOO", &excType, &excValue, &excTraceback))
2176 return NULL;
2177 result = cxoCursor_close(cursor, NULL);
2178 if (!result)
2179 return NULL;
2180 Py_DECREF(result);
2181 Py_INCREF(Py_False);
2182 return Py_False;
2183 }
2184
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoDbType.c
6 // Defines the objects used for identifying all types used by the database.
7 //-----------------------------------------------------------------------------
8
9 #include "cxoModule.h"
10
11 //-----------------------------------------------------------------------------
12 // cxoDbType_free()
13 // Free the database type object.
14 //-----------------------------------------------------------------------------
15 static void cxoDbType_free(cxoDbType *dbType)
16 {
17 Py_TYPE(dbType)->tp_free((PyObject*) dbType);
18 }
19
20
21 //-----------------------------------------------------------------------------
22 // cxoDbType_repr()
23 // Return a string representation of a queue.
24 //-----------------------------------------------------------------------------
25 static PyObject *cxoDbType_repr(cxoDbType *dbType)
26 {
27 PyObject *module, *name, *dbTypeName, *result;
28
29 dbTypeName = PyUnicode_DecodeASCII(dbType->name, strlen(dbType->name),
30 NULL);
31 if (!dbTypeName)
32 return NULL;
33 if (cxoUtils_getModuleAndName(Py_TYPE(dbType), &module, &name) < 0) {
34 Py_DECREF(dbTypeName);
35 return NULL;
36 }
37 result = cxoUtils_formatString("<%s.%s %s>",
38 PyTuple_Pack(3, module, name, dbTypeName));
39 Py_DECREF(module);
40 Py_DECREF(name);
41 Py_DECREF(dbTypeName);
42 return result;
43 }
44
45
46 //-----------------------------------------------------------------------------
47 // cxoDbType_richCompare()
48 // Peforms a comparison between the database type and another Python object.
49 // Equality (and inequality) are used to match database API types with their
50 // associated database types.
51 //-----------------------------------------------------------------------------
52 static PyObject *cxoDbType_richCompare(cxoDbType* dbType, PyObject* obj,
53 int op)
54 {
55 cxoApiType *apiType;
56 int status, equal;
57
58 // only equality and inequality can be checked
59 if (op != Py_EQ && op != Py_NE) {
60 Py_INCREF(Py_NotImplemented);
61 return Py_NotImplemented;
62 }
63
64 // check for exact object
65 equal = 0;
66 if (obj == (PyObject*) dbType) {
67 equal = 1;
68
69 // check for API type
70 } else {
71 status = PyObject_IsInstance(obj, (PyObject*) &cxoPyTypeApiType);
72 if (status < 0)
73 return NULL;
74 if (status == 1) {
75 apiType = (cxoApiType*) obj;
76 status = PySequence_Contains(apiType->dbTypes, (PyObject*) dbType);
77 if (status < 0)
78 return NULL;
79 equal = (status == 1) ? 1 : 0;
80 }
81 }
82
83 // determine return value
84 if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) {
85 Py_RETURN_TRUE;
86 }
87 Py_RETURN_FALSE;
88 }
89
90
91 //-----------------------------------------------------------------------------
92 // cxoDbType_hash()
93 // Return a hash value for the instance.
94 //-----------------------------------------------------------------------------
95 static Py_hash_t cxoDbType_hash(cxoDbType *dbType)
96 {
97 return (Py_hash_t) dbType->num;
98 }
99
100
101 //-----------------------------------------------------------------------------
102 // cxoDbType_fromDataTypeInfo()
103 // Return the database type given the data type info available from ODPI-C.
104 //-----------------------------------------------------------------------------
105 cxoDbType *cxoDbType_fromDataTypeInfo(dpiDataTypeInfo *info)
106 {
107 char message[120];
108
109 switch (info->oracleTypeNum) {
110 case DPI_ORACLE_TYPE_VARCHAR:
111 return cxoDbTypeVarchar;
112 case DPI_ORACLE_TYPE_NVARCHAR:
113 return cxoDbTypeNvarchar;
114 case DPI_ORACLE_TYPE_CHAR:
115 return cxoDbTypeChar;
116 case DPI_ORACLE_TYPE_NCHAR:
117 return cxoDbTypeNchar;
118 case DPI_ORACLE_TYPE_ROWID:
119 return cxoDbTypeRowid;
120 case DPI_ORACLE_TYPE_RAW:
121 return cxoDbTypeRaw;
122 case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
123 return cxoDbTypeBinaryDouble;
124 case DPI_ORACLE_TYPE_NATIVE_FLOAT:
125 return cxoDbTypeBinaryFloat;
126 case DPI_ORACLE_TYPE_NATIVE_INT:
127 return cxoDbTypeBinaryInteger;
128 case DPI_ORACLE_TYPE_NUMBER:
129 return cxoDbTypeNumber;
130 case DPI_ORACLE_TYPE_DATE:
131 return cxoDbTypeDate;
132 case DPI_ORACLE_TYPE_TIMESTAMP:
133 return cxoDbTypeTimestamp;
134 case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
135 return cxoDbTypeTimestampTZ;
136 case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
137 return cxoDbTypeTimestampLTZ;
138 case DPI_ORACLE_TYPE_INTERVAL_DS:
139 return cxoDbTypeIntervalDS;
140 case DPI_ORACLE_TYPE_INTERVAL_YM:
141 return cxoDbTypeIntervalYM;
142 case DPI_ORACLE_TYPE_CLOB:
143 return cxoDbTypeClob;
144 case DPI_ORACLE_TYPE_NCLOB:
145 return cxoDbTypeNclob;
146 case DPI_ORACLE_TYPE_BLOB:
147 return cxoDbTypeBlob;
148 case DPI_ORACLE_TYPE_BFILE:
149 return cxoDbTypeBfile;
150 case DPI_ORACLE_TYPE_STMT:
151 return cxoDbTypeCursor;
152 case DPI_ORACLE_TYPE_OBJECT:
153 return cxoDbTypeObject;
154 case DPI_ORACLE_TYPE_LONG_VARCHAR:
155 return cxoDbTypeLong;
156 case DPI_ORACLE_TYPE_LONG_RAW:
157 return cxoDbTypeLongRaw;
158 case DPI_ORACLE_TYPE_BOOLEAN:
159 return cxoDbTypeBoolean;
160 default:
161 break;
162 }
163
164 snprintf(message, sizeof(message), "Oracle type %d not supported.",
165 info->oracleTypeNum);
166 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
167 return NULL;
168 }
169
170
171 //-----------------------------------------------------------------------------
172 // cxoDbType_fromTransformNum()
173 // Return the database type given the transformation number.
174 //-----------------------------------------------------------------------------
175 cxoDbType *cxoDbType_fromTransformNum(cxoTransformNum transformNum)
176 {
177 char message[120];
178
179 switch (transformNum) {
180 case CXO_TRANSFORM_BINARY:
181 return cxoDbTypeRaw;
182 case CXO_TRANSFORM_BFILE:
183 return cxoDbTypeBfile;
184 case CXO_TRANSFORM_BLOB:
185 return cxoDbTypeBlob;
186 case CXO_TRANSFORM_BOOLEAN:
187 return cxoDbTypeBoolean;
188 case CXO_TRANSFORM_CLOB:
189 return cxoDbTypeClob;
190 case CXO_TRANSFORM_CURSOR:
191 return cxoDbTypeCursor;
192 case CXO_TRANSFORM_DATE:
193 case CXO_TRANSFORM_DATETIME:
194 return cxoDbTypeDate;
195 case CXO_TRANSFORM_DECIMAL:
196 case CXO_TRANSFORM_FLOAT:
197 case CXO_TRANSFORM_INT:
198 return cxoDbTypeNumber;
199 case CXO_TRANSFORM_FIXED_CHAR:
200 return cxoDbTypeChar;
201 case CXO_TRANSFORM_FIXED_NCHAR:
202 return cxoDbTypeNchar;
203 case CXO_TRANSFORM_LONG_BINARY:
204 return cxoDbTypeLongRaw;
205 case CXO_TRANSFORM_LONG_STRING:
206 return cxoDbTypeLong;
207 case CXO_TRANSFORM_NATIVE_DOUBLE:
208 return cxoDbTypeBinaryDouble;
209 case CXO_TRANSFORM_NATIVE_FLOAT:
210 return cxoDbTypeBinaryFloat;
211 case CXO_TRANSFORM_NATIVE_INT:
212 return cxoDbTypeBinaryInteger;
213 case CXO_TRANSFORM_NCLOB:
214 return cxoDbTypeNclob;
215 case CXO_TRANSFORM_NSTRING:
216 return cxoDbTypeNvarchar;
217 case CXO_TRANSFORM_OBJECT:
218 return cxoDbTypeObject;
219 case CXO_TRANSFORM_ROWID:
220 return cxoDbTypeRowid;
221 case CXO_TRANSFORM_NONE:
222 case CXO_TRANSFORM_STRING:
223 return cxoDbTypeVarchar;
224 case CXO_TRANSFORM_TIMEDELTA:
225 return cxoDbTypeIntervalDS;
226 case CXO_TRANSFORM_TIMESTAMP:
227 return cxoDbTypeTimestamp;
228 case CXO_TRANSFORM_TIMESTAMP_LTZ:
229 return cxoDbTypeTimestampLTZ;
230 case CXO_TRANSFORM_TIMESTAMP_TZ:
231 return cxoDbTypeTimestampTZ;
232 case CXO_TRANSFORM_JSON:
233 return cxoDbTypeJson;
234 default:
235 break;
236 }
237
238 snprintf(message, sizeof(message), "transform %d not supported.",
239 transformNum);
240 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
241 return NULL;
242 }
243
244
245 //-----------------------------------------------------------------------------
246 // cxoDBType_reduce()
247 // Method provided for pickling/unpickling of DB types.
248 //-----------------------------------------------------------------------------
249 static PyObject *cxoDBType_reduce(cxoDbType *dbType)
250 {
251 return PyUnicode_DecodeASCII(dbType->name, strlen(dbType->name), NULL);
252 }
253
254
255 //-----------------------------------------------------------------------------
256 // declaration of methods
257 //-----------------------------------------------------------------------------
258 static PyMethodDef cxoMethods[] = {
259 { "__reduce__", (PyCFunction) cxoDBType_reduce, METH_NOARGS },
260 { NULL, NULL}
261 };
262
263
264 //-----------------------------------------------------------------------------
265 // declaration of members
266 //-----------------------------------------------------------------------------
267 static PyMemberDef cxoMembers[] = {
268 { "name", T_STRING, offsetof(cxoDbType, name), READONLY },
269 { NULL }
270 };
271
272
273 //-----------------------------------------------------------------------------
274 // Python type declaration
275 //-----------------------------------------------------------------------------
276 PyTypeObject cxoPyTypeDbType = {
277 PyVarObject_HEAD_INIT(NULL, 0)
278 .tp_name = "cx_Oracle.DbType",
279 .tp_basicsize = sizeof(cxoDbType),
280 .tp_dealloc = (destructor) cxoDbType_free,
281 .tp_repr = (reprfunc) cxoDbType_repr,
282 .tp_flags = Py_TPFLAGS_DEFAULT,
283 .tp_members = cxoMembers,
284 .tp_methods = cxoMethods,
285 .tp_richcompare = (richcmpfunc) cxoDbType_richCompare,
286 .tp_hash = (hashfunc) cxoDbType_hash
287 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // Declaration of methods used for dequeue options
18 //-----------------------------------------------------------------------------
19 static void cxoDeqOptions_free(cxoDeqOptions*);
20 static PyObject *cxoDeqOptions_getCondition(cxoDeqOptions*, void*);
21 static PyObject *cxoDeqOptions_getConsumerName(cxoDeqOptions*, void*);
22 static PyObject *cxoDeqOptions_getCorrelation(cxoDeqOptions*, void*);
23 static PyObject *cxoDeqOptions_getMode(cxoDeqOptions*, void*);
24 static PyObject *cxoDeqOptions_getMsgId(cxoDeqOptions*, void*);
25 static PyObject *cxoDeqOptions_getNavigation(cxoDeqOptions*, void*);
26 static PyObject *cxoDeqOptions_getTransformation(cxoDeqOptions*, void*);
27 static PyObject *cxoDeqOptions_getVisibility(cxoDeqOptions*, void*);
28 static PyObject *cxoDeqOptions_getWait(cxoDeqOptions*, void*);
29 static int cxoDeqOptions_setCondition(cxoDeqOptions*, PyObject*, void*);
30 static int cxoDeqOptions_setConsumerName(cxoDeqOptions*, PyObject*, void*);
31 static int cxoDeqOptions_setCorrelation(cxoDeqOptions*, PyObject*, void*);
32 static int cxoDeqOptions_setDeliveryMode(cxoDeqOptions*, PyObject*, void*);
33 static int cxoDeqOptions_setMode(cxoDeqOptions*, PyObject*, void*);
34 static int cxoDeqOptions_setMsgId(cxoDeqOptions*, PyObject*, void*);
35 static int cxoDeqOptions_setNavigation(cxoDeqOptions*, PyObject*, void*);
36 static int cxoDeqOptions_setTransformation(cxoDeqOptions*, PyObject*, void*);
37 static int cxoDeqOptions_setVisibility(cxoDeqOptions*, PyObject*, void*);
38 static int cxoDeqOptions_setWait(cxoDeqOptions*, PyObject*, void*);
39
40
41 //-----------------------------------------------------------------------------
42 // declaration of calculated members for Python type "DeqOptions"
17 // cxoDeqOptions_new()
18 // Create a new dequeue options object.
19 //-----------------------------------------------------------------------------
20 cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection,
21 dpiDeqOptions *handle)
22 {
23 cxoDeqOptions *options;
24 int status;
25
26 options = (cxoDeqOptions*)
27 cxoPyTypeDeqOptions.tp_alloc(&cxoPyTypeDeqOptions, 0);
28 if (!options)
29 return NULL;
30 if (handle) {
31 status = dpiDeqOptions_addRef(handle);
32 } else {
33 status = dpiConn_newDeqOptions(connection->handle, &handle);
34 }
35 if (status < 0) {
36 cxoError_raiseAndReturnNull();
37 Py_DECREF(options);
38 return NULL;
39 }
40 options->handle = handle;
41 options->encoding = connection->encodingInfo.encoding;
42
43 return options;
44 }
45
46
47 //-----------------------------------------------------------------------------
48 // cxoDeqOptions_free()
49 // Free the memory associated with the dequeue options object.
50 //-----------------------------------------------------------------------------
51 static void cxoDeqOptions_free(cxoDeqOptions *options)
52 {
53 if (options->handle) {
54 dpiDeqOptions_release(options->handle);
55 options->handle = NULL;
56 }
57 Py_TYPE(options)->tp_free((PyObject*) options);
58 }
59
60
61 //-----------------------------------------------------------------------------
62 // cxoDeqOptions_getAttrText()
63 // Get the value of the attribute as text.
64 //-----------------------------------------------------------------------------
65 static PyObject *cxoDeqOptions_getAttrText(cxoDeqOptions *options,
66 int (*func)(dpiDeqOptions*, const char**, uint32_t*))
67 {
68 uint32_t valueLength;
69 const char *value;
70
71 if ((*func)(options->handle, &value, &valueLength) < 0)
72 return cxoError_raiseAndReturnNull();
73 if (!value)
74 Py_RETURN_NONE;
75 return PyUnicode_Decode(value, valueLength, options->encoding, NULL);
76 }
77
78
79 //-----------------------------------------------------------------------------
80 // cxoDeqOptions_setAttrText()
81 // Set the value of the attribute as text.
82 //-----------------------------------------------------------------------------
83 static int cxoDeqOptions_setAttrText(cxoDeqOptions *options, PyObject *value,
84 int (*func)(dpiDeqOptions*, const char*, uint32_t))
85 {
86 cxoBuffer buffer;
87 int status;
88
89 if (cxoBuffer_fromObject(&buffer, value, options->encoding))
90 return -1;
91 status = (*func)(options->handle, buffer.ptr, buffer.size);
92 cxoBuffer_clear(&buffer);
93 if (status < 0)
94 return cxoError_raiseAndReturnInt();
95 return 0;
96 }
97
98
99 //-----------------------------------------------------------------------------
100 // cxoDeqOptions_getCondition()
101 // Get the value of the condition option.
102 //-----------------------------------------------------------------------------
103 static PyObject *cxoDeqOptions_getCondition(cxoDeqOptions *options,
104 void *unused)
105 {
106 return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getCondition);
107 }
108
109
110 //-----------------------------------------------------------------------------
111 // cxoDeqOptions_getConsumerName()
112 // Get the value of the consumer name option.
113 //-----------------------------------------------------------------------------
114 static PyObject *cxoDeqOptions_getConsumerName(cxoDeqOptions *options,
115 void *unused)
116 {
117 return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getConsumerName);
118 }
119
120
121 //-----------------------------------------------------------------------------
122 // cxoDeqOptions_getCorrelation()
123 // Get the value of the correlation option.
124 //-----------------------------------------------------------------------------
125 static PyObject *cxoDeqOptions_getCorrelation(cxoDeqOptions *options,
126 void *unused)
127 {
128 return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getCorrelation);
129 }
130
131
132 //-----------------------------------------------------------------------------
133 // cxoDeqOptions_getMode()
134 // Get the value of the mode option.
135 //-----------------------------------------------------------------------------
136 static PyObject *cxoDeqOptions_getMode(cxoDeqOptions *options, void *unused)
137 {
138 dpiDeqMode value;
139
140 if (dpiDeqOptions_getMode(options->handle, &value) < 0)
141 return cxoError_raiseAndReturnNull();
142 return PyLong_FromLong(value);
143 }
144
145
146 //-----------------------------------------------------------------------------
147 // cxoDeqOptions_getMsgId()
148 // Get the value of the message id option.
149 //-----------------------------------------------------------------------------
150 static PyObject *cxoDeqOptions_getMsgId(cxoDeqOptions *options, void *unused)
151 {
152 uint32_t valueLength;
153 const char *value;
154
155 if (dpiDeqOptions_getMsgId(options->handle, &value, &valueLength) < 0)
156 return cxoError_raiseAndReturnNull();
157 if (!value)
158 Py_RETURN_NONE;
159 return PyBytes_FromStringAndSize(value, valueLength);
160 }
161
162
163 //-----------------------------------------------------------------------------
164 // cxoDeqOptions_getNavigation()
165 // Get the value of the navigation option.
166 //-----------------------------------------------------------------------------
167 static PyObject *cxoDeqOptions_getNavigation(cxoDeqOptions *options,
168 void *unused)
169 {
170 dpiDeqNavigation value;
171
172 if (dpiDeqOptions_getNavigation(options->handle, &value) < 0)
173 return cxoError_raiseAndReturnNull();
174 return PyLong_FromLong(value);
175 }
176
177
178 //-----------------------------------------------------------------------------
179 // cxoDeqOptions_getTransformation()
180 // Get the value of the transformation option.
181 //-----------------------------------------------------------------------------
182 static PyObject *cxoDeqOptions_getTransformation(cxoDeqOptions *options,
183 void *unused)
184 {
185 return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getTransformation);
186 }
187
188
189 //-----------------------------------------------------------------------------
190 // cxoDeqOptions_getVisibility()
191 // Get the value of the visibility option.
192 //-----------------------------------------------------------------------------
193 static PyObject *cxoDeqOptions_getVisibility(cxoDeqOptions *options,
194 void *unused)
195 {
196 dpiVisibility value;
197
198 if (dpiDeqOptions_getVisibility(options->handle, &value) < 0)
199 return cxoError_raiseAndReturnNull();
200 return PyLong_FromLong(value);
201 }
202
203
204 //-----------------------------------------------------------------------------
205 // cxoDeqOptions_getWait()
206 // Get the value of the wait option.
207 //-----------------------------------------------------------------------------
208 static PyObject *cxoDeqOptions_getWait(cxoDeqOptions *options, void *unused)
209 {
210 uint32_t value;
211
212 if (dpiDeqOptions_getWait(options->handle, &value) < 0)
213 return cxoError_raiseAndReturnNull();
214 return PyLong_FromLong(value);
215 }
216
217
218 //-----------------------------------------------------------------------------
219 // cxoDeqOptions_setCondition()
220 // Set the value of the condition option.
221 //-----------------------------------------------------------------------------
222 static int cxoDeqOptions_setCondition(cxoDeqOptions *options,
223 PyObject *valueObj, void *unused)
224 {
225 return cxoDeqOptions_setAttrText(options, valueObj,
226 dpiDeqOptions_setCondition);
227 }
228
229
230 //-----------------------------------------------------------------------------
231 // cxoDeqOptions_setConsumerName()
232 // Set the value of the consumer name option.
233 //-----------------------------------------------------------------------------
234 static int cxoDeqOptions_setConsumerName(cxoDeqOptions *options,
235 PyObject *valueObj, void *unused)
236 {
237 return cxoDeqOptions_setAttrText(options, valueObj,
238 dpiDeqOptions_setConsumerName);
239 }
240
241
242 //-----------------------------------------------------------------------------
243 // cxoDeqOptions_setCorrelation()
244 // Set the value of the correlation option.
245 //-----------------------------------------------------------------------------
246 static int cxoDeqOptions_setCorrelation(cxoDeqOptions *options,
247 PyObject *valueObj, void *unused)
248 {
249 return cxoDeqOptions_setAttrText(options, valueObj,
250 dpiDeqOptions_setCorrelation);
251 }
252
253
254 //-----------------------------------------------------------------------------
255 // cxoDeqOptions_setDeliveryMode()
256 // Set the value of the delivery mode option.
257 //-----------------------------------------------------------------------------
258 static int cxoDeqOptions_setDeliveryMode(cxoDeqOptions *options,
259 PyObject *valueObj, void *unused)
260 {
261 dpiMessageDeliveryMode value;
262
263 value = PyLong_AsLong(valueObj);
264 if (PyErr_Occurred())
265 return -1;
266 if (dpiDeqOptions_setDeliveryMode(options->handle, value) < 0)
267 return cxoError_raiseAndReturnInt();
268 return 0;
269 }
270
271
272 //-----------------------------------------------------------------------------
273 // cxoDeqOptions_setMode()
274 // Set the value of the mode option.
275 //-----------------------------------------------------------------------------
276 static int cxoDeqOptions_setMode(cxoDeqOptions *options, PyObject *valueObj,
277 void *unused)
278 {
279 dpiDeqMode value;
280
281 value = PyLong_AsLong(valueObj);
282 if (PyErr_Occurred())
283 return -1;
284 if (dpiDeqOptions_setMode(options->handle, value) < 0)
285 return cxoError_raiseAndReturnInt();
286 return 0;
287 }
288
289
290 //-----------------------------------------------------------------------------
291 // cxoDeqOptions_setMsgId()
292 // Set the value of the message id option.
293 //-----------------------------------------------------------------------------
294 static int cxoDeqOptions_setMsgId(cxoDeqOptions *options, PyObject *valueObj,
295 void *unused)
296 {
297 Py_ssize_t valueLength;
298 char *value;
299
300 if (PyBytes_AsStringAndSize(valueObj, &value, &valueLength) < 0)
301 return -1;
302 if (dpiDeqOptions_setMsgId(options->handle, value,
303 (uint32_t) valueLength) < 0)
304 return cxoError_raiseAndReturnInt();
305 return 0;
306 }
307
308
309 //-----------------------------------------------------------------------------
310 // cxoDeqOptions_setNavigation()
311 // Set the value of the navigation option.
312 //-----------------------------------------------------------------------------
313 static int cxoDeqOptions_setNavigation(cxoDeqOptions *options,
314 PyObject *valueObj, void *unused)
315 {
316 dpiDeqNavigation value;
317
318 value = PyLong_AsLong(valueObj);
319 if (PyErr_Occurred())
320 return -1;
321 if (dpiDeqOptions_setNavigation(options->handle, value) < 0)
322 return cxoError_raiseAndReturnInt();
323 return 0;
324 }
325
326
327 //-----------------------------------------------------------------------------
328 // cxoDeqOptions_setTransformation()
329 // Set the value of the correlation option.
330 //-----------------------------------------------------------------------------
331 static int cxoDeqOptions_setTransformation(cxoDeqOptions *options,
332 PyObject *valueObj, void *unused)
333 {
334 return cxoDeqOptions_setAttrText(options, valueObj,
335 dpiDeqOptions_setTransformation);
336 }
337
338
339 //-----------------------------------------------------------------------------
340 // cxoDeqOptions_setVisibility()
341 // Set the value of the visibility option.
342 //-----------------------------------------------------------------------------
343 static int cxoDeqOptions_setVisibility(cxoDeqOptions *options,
344 PyObject *valueObj, void *unused)
345 {
346 dpiVisibility value;
347
348 value = PyLong_AsLong(valueObj);
349 if (PyErr_Occurred())
350 return -1;
351 if (dpiDeqOptions_setVisibility(options->handle, value) < 0)
352 return cxoError_raiseAndReturnInt();
353 return 0;
354 }
355
356
357 //-----------------------------------------------------------------------------
358 // cxoDeqOptions_setWait()
359 // Set the value of the wait option.
360 //-----------------------------------------------------------------------------
361 static int cxoDeqOptions_setWait(cxoDeqOptions *options, PyObject *valueObj,
362 void *unused)
363 {
364 uint32_t value;
365
366 value = PyLong_AsLong(valueObj);
367 if (PyErr_Occurred())
368 return -1;
369 if (dpiDeqOptions_setWait(options->handle, value) < 0)
370 return cxoError_raiseAndReturnInt();
371 return 0;
372 }
373
374
375 //-----------------------------------------------------------------------------
376 // declaration of calculated members for Python type
43377 //-----------------------------------------------------------------------------
44378 static PyGetSetDef cxoDeqOptionsCalcMembers[] = {
45379 { "condition", (getter) cxoDeqOptions_getCondition,
66400
67401
68402 //-----------------------------------------------------------------------------
69 // Python type declarations
403 // declaration of Python type
70404 //-----------------------------------------------------------------------------
71405 PyTypeObject cxoPyTypeDeqOptions = {
72406 PyVarObject_HEAD_INIT(NULL, 0)
73 "cx_Oracle.DeqOptions", // tp_name
74 sizeof(cxoDeqOptions), // tp_basicsize
75 0, // tp_itemsize
76 (destructor) cxoDeqOptions_free, // tp_dealloc
77 0, // tp_print
78 0, // tp_getattr
79 0, // tp_setattr
80 0, // tp_compare
81 0, // tp_repr
82 0, // tp_as_number
83 0, // tp_as_sequence
84 0, // tp_as_mapping
85 0, // tp_hash
86 0, // tp_call
87 0, // tp_str
88 0, // tp_getattro
89 0, // tp_setattro
90 0, // tp_as_buffer
91 Py_TPFLAGS_DEFAULT, // tp_flags
92 0, // tp_doc
93 0, // tp_traverse
94 0, // tp_clear
95 0, // tp_richcompare
96 0, // tp_weaklistoffset
97 0, // tp_iter
98 0, // tp_iternext
99 0, // tp_methods
100 0, // tp_members
101 cxoDeqOptionsCalcMembers, // tp_getset
102 0, // tp_base
103 0, // tp_dict
104 0, // tp_descr_get
105 0, // tp_descr_set
106 0, // tp_dictoffset
107 0, // tp_init
108 0, // tp_alloc
109 0, // tp_new
110 0, // tp_free
111 0, // tp_is_gc
112 0 // tp_bases
407 .tp_name = "cx_Oracle.DeqOptions",
408 .tp_basicsize = sizeof(cxoDeqOptions),
409 .tp_dealloc = (destructor) cxoDeqOptions_free,
410 .tp_flags = Py_TPFLAGS_DEFAULT,
411 .tp_getset = cxoDeqOptionsCalcMembers
113412 };
114
115
116 //-----------------------------------------------------------------------------
117 // cxoDeqOptions_new()
118 // Create a new dequeue options object.
119 //-----------------------------------------------------------------------------
120 cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection)
121 {
122 cxoDeqOptions *options;
123
124 options = (cxoDeqOptions*)
125 cxoPyTypeDeqOptions.tp_alloc(&cxoPyTypeDeqOptions, 0);
126 if (!options)
127 return NULL;
128 if (dpiConn_newDeqOptions(connection->handle, &options->handle) < 0) {
129 Py_DECREF(options);
130 cxoError_raiseAndReturnNull();
131 return NULL;
132 }
133 options->encoding = connection->encodingInfo.encoding;
134
135 return options;
136 }
137
138
139 //-----------------------------------------------------------------------------
140 // cxoDeqOptions_free()
141 // Free the memory associated with the dequeue options object.
142 //-----------------------------------------------------------------------------
143 static void cxoDeqOptions_free(cxoDeqOptions *options)
144 {
145 if (options->handle) {
146 dpiDeqOptions_release(options->handle);
147 options->handle = NULL;
148 }
149 Py_TYPE(options)->tp_free((PyObject*) options);
150 }
151
152
153 //-----------------------------------------------------------------------------
154 // cxoDeqOptions_getAttrText()
155 // Get the value of the attribute as text.
156 //-----------------------------------------------------------------------------
157 static PyObject *cxoDeqOptions_getAttrText(cxoDeqOptions *options,
158 int (*func)(dpiDeqOptions*, const char**, uint32_t*))
159 {
160 uint32_t valueLength;
161 const char *value;
162
163 if ((*func)(options->handle, &value, &valueLength) < 0)
164 return cxoError_raiseAndReturnNull();
165 if (!value)
166 Py_RETURN_NONE;
167 return cxoPyString_fromEncodedString(value, valueLength, options->encoding,
168 NULL);
169 }
170
171
172 //-----------------------------------------------------------------------------
173 // cxoDeqOptions_setAttrText()
174 // Set the value of the attribute as text.
175 //-----------------------------------------------------------------------------
176 static int cxoDeqOptions_setAttrText(cxoDeqOptions *options, PyObject *value,
177 int (*func)(dpiDeqOptions*, const char*, uint32_t))
178 {
179 cxoBuffer buffer;
180 int status;
181
182 if (cxoBuffer_fromObject(&buffer, value, options->encoding))
183 return -1;
184 status = (*func)(options->handle, buffer.ptr, buffer.size);
185 cxoBuffer_clear(&buffer);
186 if (status < 0)
187 return cxoError_raiseAndReturnInt();
188 return 0;
189 }
190
191
192 //-----------------------------------------------------------------------------
193 // cxoDeqOptions_getCondition()
194 // Get the value of the condition option.
195 //-----------------------------------------------------------------------------
196 static PyObject *cxoDeqOptions_getCondition(cxoDeqOptions *options,
197 void *unused)
198 {
199 return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getCondition);
200 }
201
202
203 //-----------------------------------------------------------------------------
204 // cxoDeqOptions_getConsumerName()
205 // Get the value of the consumer name option.
206 //-----------------------------------------------------------------------------
207 static PyObject *cxoDeqOptions_getConsumerName(cxoDeqOptions *options,
208 void *unused)
209 {
210 return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getConsumerName);
211 }
212
213
214 //-----------------------------------------------------------------------------
215 // cxoDeqOptions_getCorrelation()
216 // Get the value of the correlation option.
217 //-----------------------------------------------------------------------------
218 static PyObject *cxoDeqOptions_getCorrelation(cxoDeqOptions *options,
219 void *unused)
220 {
221 return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getCorrelation);
222 }
223
224
225 //-----------------------------------------------------------------------------
226 // cxoDeqOptions_getMode()
227 // Get the value of the mode option.
228 //-----------------------------------------------------------------------------
229 static PyObject *cxoDeqOptions_getMode(cxoDeqOptions *options, void *unused)
230 {
231 dpiDeqMode value;
232
233 if (dpiDeqOptions_getMode(options->handle, &value) < 0)
234 return cxoError_raiseAndReturnNull();
235 return PyInt_FromLong(value);
236 }
237
238
239 //-----------------------------------------------------------------------------
240 // cxoDeqOptions_getMsgId()
241 // Get the value of the message id option.
242 //-----------------------------------------------------------------------------
243 static PyObject *cxoDeqOptions_getMsgId(cxoDeqOptions *options, void *unused)
244 {
245 uint32_t valueLength;
246 const char *value;
247
248 if (dpiDeqOptions_getMsgId(options->handle, &value, &valueLength) < 0)
249 return cxoError_raiseAndReturnNull();
250 if (!value)
251 Py_RETURN_NONE;
252 return PyBytes_FromStringAndSize(value, valueLength);
253 }
254
255
256 //-----------------------------------------------------------------------------
257 // cxoDeqOptions_getNavigation()
258 // Get the value of the navigation option.
259 //-----------------------------------------------------------------------------
260 static PyObject *cxoDeqOptions_getNavigation(cxoDeqOptions *options,
261 void *unused)
262 {
263 dpiDeqNavigation value;
264
265 if (dpiDeqOptions_getNavigation(options->handle, &value) < 0)
266 return cxoError_raiseAndReturnNull();
267 return PyInt_FromLong(value);
268 }
269
270
271 //-----------------------------------------------------------------------------
272 // cxoDeqOptions_getTransformation()
273 // Get the value of the transformation option.
274 //-----------------------------------------------------------------------------
275 static PyObject *cxoDeqOptions_getTransformation(cxoDeqOptions *options,
276 void *unused)
277 {
278 return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getTransformation);
279 }
280
281
282 //-----------------------------------------------------------------------------
283 // cxoDeqOptions_getVisibility()
284 // Get the value of the visibility option.
285 //-----------------------------------------------------------------------------
286 static PyObject *cxoDeqOptions_getVisibility(cxoDeqOptions *options,
287 void *unused)
288 {
289 dpiVisibility value;
290
291 if (dpiDeqOptions_getVisibility(options->handle, &value) < 0)
292 return cxoError_raiseAndReturnNull();
293 return PyInt_FromLong(value);
294 }
295
296
297 //-----------------------------------------------------------------------------
298 // cxoDeqOptions_getWait()
299 // Get the value of the wait option.
300 //-----------------------------------------------------------------------------
301 static PyObject *cxoDeqOptions_getWait(cxoDeqOptions *options, void *unused)
302 {
303 uint32_t value;
304
305 if (dpiDeqOptions_getWait(options->handle, &value) < 0)
306 return cxoError_raiseAndReturnNull();
307 return PyInt_FromLong(value);
308 }
309
310
311 //-----------------------------------------------------------------------------
312 // cxoDeqOptions_setCondition()
313 // Set the value of the condition option.
314 //-----------------------------------------------------------------------------
315 static int cxoDeqOptions_setCondition(cxoDeqOptions *options,
316 PyObject *valueObj, void *unused)
317 {
318 return cxoDeqOptions_setAttrText(options, valueObj,
319 dpiDeqOptions_setCondition);
320 }
321
322
323 //-----------------------------------------------------------------------------
324 // cxoDeqOptions_setConsumerName()
325 // Set the value of the consumer name option.
326 //-----------------------------------------------------------------------------
327 static int cxoDeqOptions_setConsumerName(cxoDeqOptions *options,
328 PyObject *valueObj, void *unused)
329 {
330 return cxoDeqOptions_setAttrText(options, valueObj,
331 dpiDeqOptions_setConsumerName);
332 }
333
334
335 //-----------------------------------------------------------------------------
336 // cxoDeqOptions_setCorrelation()
337 // Set the value of the correlation option.
338 //-----------------------------------------------------------------------------
339 static int cxoDeqOptions_setCorrelation(cxoDeqOptions *options,
340 PyObject *valueObj, void *unused)
341 {
342 return cxoDeqOptions_setAttrText(options, valueObj,
343 dpiDeqOptions_setCorrelation);
344 }
345
346
347 //-----------------------------------------------------------------------------
348 // cxoDeqOptions_setDeliveryMode()
349 // Set the value of the delivery mode option.
350 //-----------------------------------------------------------------------------
351 static int cxoDeqOptions_setDeliveryMode(cxoDeqOptions *options,
352 PyObject *valueObj, void *unused)
353 {
354 dpiMessageDeliveryMode value;
355
356 value = PyInt_AsLong(valueObj);
357 if (PyErr_Occurred())
358 return -1;
359 if (dpiDeqOptions_setDeliveryMode(options->handle, value) < 0)
360 return cxoError_raiseAndReturnInt();
361 return 0;
362 }
363
364
365 //-----------------------------------------------------------------------------
366 // cxoDeqOptions_setMode()
367 // Set the value of the mode option.
368 //-----------------------------------------------------------------------------
369 static int cxoDeqOptions_setMode(cxoDeqOptions *options, PyObject *valueObj,
370 void *unused)
371 {
372 dpiDeqMode value;
373
374 value = PyInt_AsLong(valueObj);
375 if (PyErr_Occurred())
376 return -1;
377 if (dpiDeqOptions_setMode(options->handle, value) < 0)
378 return cxoError_raiseAndReturnInt();
379 return 0;
380 }
381
382
383 //-----------------------------------------------------------------------------
384 // cxoDeqOptions_setMsgId()
385 // Set the value of the message id option.
386 //-----------------------------------------------------------------------------
387 static int cxoDeqOptions_setMsgId(cxoDeqOptions *options, PyObject *valueObj,
388 void *unused)
389 {
390 Py_ssize_t valueLength;
391 char *value;
392
393 if (PyBytes_AsStringAndSize(valueObj, &value, &valueLength) < 0)
394 return -1;
395 if (dpiDeqOptions_setMsgId(options->handle, value,
396 (uint32_t) valueLength) < 0)
397 return cxoError_raiseAndReturnInt();
398 return 0;
399 }
400
401
402 //-----------------------------------------------------------------------------
403 // cxoDeqOptions_setNavigation()
404 // Set the value of the navigation option.
405 //-----------------------------------------------------------------------------
406 static int cxoDeqOptions_setNavigation(cxoDeqOptions *options,
407 PyObject *valueObj, void *unused)
408 {
409 dpiDeqNavigation value;
410
411 value = PyInt_AsLong(valueObj);
412 if (PyErr_Occurred())
413 return -1;
414 if (dpiDeqOptions_setNavigation(options->handle, value) < 0)
415 return cxoError_raiseAndReturnInt();
416 return 0;
417 }
418
419
420 //-----------------------------------------------------------------------------
421 // cxoDeqOptions_setTransformation()
422 // Set the value of the correlation option.
423 //-----------------------------------------------------------------------------
424 static int cxoDeqOptions_setTransformation(cxoDeqOptions *options,
425 PyObject *valueObj, void *unused)
426 {
427 return cxoDeqOptions_setAttrText(options, valueObj,
428 dpiDeqOptions_setTransformation);
429 }
430
431
432 //-----------------------------------------------------------------------------
433 // cxoDeqOptions_setVisibility()
434 // Set the value of the visibility option.
435 //-----------------------------------------------------------------------------
436 static int cxoDeqOptions_setVisibility(cxoDeqOptions *options,
437 PyObject *valueObj, void *unused)
438 {
439 dpiVisibility value;
440
441 value = PyInt_AsLong(valueObj);
442 if (PyErr_Occurred())
443 return -1;
444 if (dpiDeqOptions_setVisibility(options->handle, value) < 0)
445 return cxoError_raiseAndReturnInt();
446 return 0;
447 }
448
449
450 //-----------------------------------------------------------------------------
451 // cxoDeqOptions_setWait()
452 // Set the value of the wait option.
453 //-----------------------------------------------------------------------------
454 static int cxoDeqOptions_setWait(cxoDeqOptions *options, PyObject *valueObj,
455 void *unused)
456 {
457 uint32_t value;
458
459 value = PyInt_AsLong(valueObj);
460 if (PyErr_Occurred())
461 return -1;
462 if (dpiDeqOptions_setWait(options->handle, value) < 0)
463 return cxoError_raiseAndReturnInt();
464 return 0;
465 }
466
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // Declaration of methods used for enqueue options
18 //-----------------------------------------------------------------------------
19 static void cxoEnqOptions_free(cxoEnqOptions*);
20 static PyObject *cxoEnqOptions_getTransformation(cxoEnqOptions*, void*);
21 static PyObject *cxoEnqOptions_getVisibility(cxoEnqOptions*, void*);
22 static int cxoEnqOptions_setDeliveryMode(cxoEnqOptions*, PyObject*, void*);
23 static int cxoEnqOptions_setTransformation(cxoEnqOptions*, PyObject*, void*);
24 static int cxoEnqOptions_setVisibility(cxoEnqOptions*, PyObject*, void*);
25
26
27 //-----------------------------------------------------------------------------
28 // declaration of calculated members for Python type "EnqOptions"
29 //-----------------------------------------------------------------------------
30 static PyGetSetDef cxoEnqOptionsCalcMembers[] = {
31 { "deliverymode", 0, (setter) cxoEnqOptions_setDeliveryMode, 0, 0 },
32 { "transformation", (getter) cxoEnqOptions_getTransformation,
33 (setter) cxoEnqOptions_setTransformation, 0, 0 },
34 { "visibility", (getter) cxoEnqOptions_getVisibility,
35 (setter) cxoEnqOptions_setVisibility, 0, 0 },
36 { NULL }
37 };
38
39
40 //-----------------------------------------------------------------------------
41 // Python type declarations
42 //-----------------------------------------------------------------------------
43 PyTypeObject cxoPyTypeEnqOptions = {
44 PyVarObject_HEAD_INIT(NULL, 0)
45 "cx_Oracle.EnqOptions", // tp_name
46 sizeof(cxoEnqOptions), // tp_basicsize
47 0, // tp_itemsize
48 (destructor) cxoEnqOptions_free, // tp_dealloc
49 0, // tp_print
50 0, // tp_getattr
51 0, // tp_setattr
52 0, // tp_compare
53 0, // tp_repr
54 0, // tp_as_number
55 0, // tp_as_sequence
56 0, // tp_as_mapping
57 0, // tp_hash
58 0, // tp_call
59 0, // tp_str
60 0, // tp_getattro
61 0, // tp_setattro
62 0, // tp_as_buffer
63 Py_TPFLAGS_DEFAULT, // tp_flags
64 0, // tp_doc
65 0, // tp_traverse
66 0, // tp_clear
67 0, // tp_richcompare
68 0, // tp_weaklistoffset
69 0, // tp_iter
70 0, // tp_iternext
71 0, // tp_methods
72 0, // tp_members
73 cxoEnqOptionsCalcMembers, // tp_getset
74 0, // tp_base
75 0, // tp_dict
76 0, // tp_descr_get
77 0, // tp_descr_set
78 0, // tp_dictoffset
79 0, // tp_init
80 0, // tp_alloc
81 0, // tp_new
82 0, // tp_free
83 0, // tp_is_gc
84 0 // tp_bases
85 };
86
87
88 //-----------------------------------------------------------------------------
8917 // cxoEnqOptions_new()
9018 // Create a new enqueue options object.
9119 //-----------------------------------------------------------------------------
92 cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection)
20 cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection,
21 dpiEnqOptions *handle)
9322 {
94 cxoEnqOptions *self;
23 cxoEnqOptions *options;
24 int status;
9525
96 self = (cxoEnqOptions*)
26 options = (cxoEnqOptions*)
9727 cxoPyTypeEnqOptions.tp_alloc(&cxoPyTypeEnqOptions, 0);
98 if (!self)
28 if (!options)
9929 return NULL;
100 if (dpiConn_newEnqOptions(connection->handle, &self->handle) < 0) {
101 Py_DECREF(self);
30 if (handle) {
31 status = dpiEnqOptions_addRef(handle);
32 } else {
33 status = dpiConn_newEnqOptions(connection->handle, &handle);
34 }
35 if (status < 0) {
10236 cxoError_raiseAndReturnNull();
37 Py_DECREF(options);
10338 return NULL;
10439 }
105 self->encoding = connection->encodingInfo.encoding;
40 options->handle = handle;
41 options->encoding = connection->encodingInfo.encoding;
10642
107 return self;
43 return options;
10844 }
10945
11046
13773 return cxoError_raiseAndReturnNull();
13874 if (!value)
13975 Py_RETURN_NONE;
140 return cxoPyString_fromEncodedString(value, valueLength, self->encoding,
141 NULL);
76 return PyUnicode_Decode(value, valueLength, self->encoding, NULL);
14277 }
14378
14479
15287
15388 if (dpiEnqOptions_getVisibility(self->handle, &value) < 0)
15489 return cxoError_raiseAndReturnNull();
155 return PyInt_FromLong(value);
90 return PyLong_FromLong(value);
15691 }
15792
15893
165100 {
166101 dpiMessageDeliveryMode value;
167102
168 value = PyInt_AsLong(valueObj);
103 value = PyLong_AsLong(valueObj);
169104 if (PyErr_Occurred())
170105 return -1;
171106 if (dpiEnqOptions_setDeliveryMode(self->handle, value) < 0)
204139 {
205140 dpiVisibility value;
206141
207 value = PyInt_AsLong(valueObj);
142 value = PyLong_AsLong(valueObj);
208143 if (PyErr_Occurred())
209144 return -1;
210145 if (dpiEnqOptions_setVisibility(self->handle, value) < 0)
212147 return 0;
213148 }
214149
150
151 //-----------------------------------------------------------------------------
152 // declaration of calculated members for Python type
153 //-----------------------------------------------------------------------------
154 static PyGetSetDef cxoEnqOptionsCalcMembers[] = {
155 { "deliverymode", 0, (setter) cxoEnqOptions_setDeliveryMode, 0, 0 },
156 { "transformation", (getter) cxoEnqOptions_getTransformation,
157 (setter) cxoEnqOptions_setTransformation, 0, 0 },
158 { "visibility", (getter) cxoEnqOptions_getVisibility,
159 (setter) cxoEnqOptions_setVisibility, 0, 0 },
160 { NULL }
161 };
162
163
164 //-----------------------------------------------------------------------------
165 // declaration of Python type
166 //-----------------------------------------------------------------------------
167 PyTypeObject cxoPyTypeEnqOptions = {
168 PyVarObject_HEAD_INIT(NULL, 0)
169 .tp_name = "cx_Oracle.EnqOptions",
170 .tp_basicsize = sizeof(cxoEnqOptions),
171 .tp_dealloc = (destructor) cxoEnqOptions_free,
172 .tp_flags = Py_TPFLAGS_DEFAULT,
173 .tp_getset = cxoEnqOptionsCalcMembers
174 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // forward declarations
18 //-----------------------------------------------------------------------------
19 static void cxoError_free(cxoError *error);
20 static PyObject *cxoError_str(cxoError *error);
17 // cxoError_free()
18 // Deallocate the error.
19 //-----------------------------------------------------------------------------
20 static void cxoError_free(cxoError *error)
21 {
22 Py_CLEAR(error->message);
23 Py_CLEAR(error->context);
24 PyObject_Del(error);
25 }
26
27
28 //-----------------------------------------------------------------------------
29 // cxoError_new()
30 // Create a new error object. This is intended to only be used by the
31 // unpickling routine, and not by direct creation!
32 //-----------------------------------------------------------------------------
2133 static PyObject *cxoError_new(PyTypeObject *type, PyObject *args,
22 PyObject *keywordArgs);
23 static PyObject *cxoError_reduce(cxoError*);
34 PyObject *keywordArgs)
35 {
36 PyObject *message, *context;
37 int isRecoverable, code;
38 cxoError *error;
39 unsigned offset;
40
41 isRecoverable = 0;
42 if (!PyArg_ParseTuple(args, "OiIO|i", &message, &code, &offset, &context,
43 &isRecoverable))
44 return NULL;
45 error = (cxoError*) type->tp_alloc(type, 0);
46 if (!error)
47 return NULL;
48
49 error->code = code;
50 error->offset = offset;
51 error->isRecoverable = (char) isRecoverable;
52 Py_INCREF(message);
53 error->message = message;
54 Py_INCREF(context);
55 error->context = context;
56
57 return (PyObject*) error;
58 }
59
60
61 //-----------------------------------------------------------------------------
62 // cxoError_newFromInfo()
63 // Internal method for creating an error object from the DPI error
64 // information.
65 //-----------------------------------------------------------------------------
66 cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo)
67 {
68 cxoError *error;
69
70 // create error object and initialize it
71 error = (cxoError*) cxoPyTypeError.tp_alloc(&cxoPyTypeError, 0);
72 if (!error)
73 return NULL;
74 error->code = errorInfo->code;
75 error->offset = errorInfo->offset;
76 error->isRecoverable = (char) errorInfo->isRecoverable;
77
78 // create message
79 error->message = PyUnicode_Decode(errorInfo->message,
80 errorInfo->messageLength, errorInfo->encoding, NULL);
81 if (!error->message) {
82 Py_DECREF(error);
83 return NULL;
84 }
85
86 // create context composed of function name and action
87 error->context = PyUnicode_FromFormat("%s: %s", errorInfo->fnName,
88 errorInfo->action);
89 if (!error->context) {
90 Py_DECREF(error);
91 return NULL;
92 }
93
94 return error;
95 }
96
97
98 //-----------------------------------------------------------------------------
99 // cxoError_newFromString()
100 // Internal method for creating an error object from the DPI error
101 // information.
102 //-----------------------------------------------------------------------------
103 static cxoError *cxoError_newFromString(const char *message)
104 {
105 cxoError *error;
106
107 error = (cxoError*) cxoPyTypeError.tp_alloc(&cxoPyTypeError, 0);
108 if (!error)
109 return NULL;
110 Py_INCREF(Py_None);
111 error->context = Py_None;
112 error->message = PyUnicode_DecodeASCII(message, strlen(message), NULL);
113 if (!error->message) {
114 Py_DECREF(error);
115 return NULL;
116 }
117
118 return error;
119 }
120
121
122 //-----------------------------------------------------------------------------
123 // cxoError_raiseAndReturnInt()
124 // Internal method for raising an exception from an error generated from DPI.
125 // Return -1 as a convenience to the caller.
126 //-----------------------------------------------------------------------------
127 int cxoError_raiseAndReturnInt(void)
128 {
129 dpiErrorInfo errorInfo;
130
131 dpiContext_getError(cxoDpiContext, &errorInfo);
132 return cxoError_raiseFromInfo(&errorInfo);
133 }
134
135
136 //-----------------------------------------------------------------------------
137 // cxoError_raiseAndReturnNull()
138 // Internal method for raising an exception from an error generated from DPI.
139 // Return NULL as a convenience to the caller.
140 //-----------------------------------------------------------------------------
141 PyObject *cxoError_raiseAndReturnNull(void)
142 {
143 cxoError_raiseAndReturnInt();
144 return NULL;
145 }
146
147
148 //-----------------------------------------------------------------------------
149 // cxoError_raiseFromInfo()
150 // Internal method for raising an exception given an error information
151 // structure from DPI. Return -1 as a convenience to the caller.
152 //-----------------------------------------------------------------------------
153 int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo)
154 {
155 PyObject *exceptionType;
156 cxoError *error;
157
158 error = cxoError_newFromInfo(errorInfo);
159 if (!error)
160 return -1;
161 switch (errorInfo->code) {
162 case 1: // unique constraint violated
163 case 1400: // cannot insert NULL
164 case 2290: // check constraint violated
165 case 2291: // integrity constraint violated - parent key not found
166 case 2292: // integrity constraint violated - child record found
167 case 40479: // internal JSON serializer error
168 exceptionType = cxoIntegrityErrorException;
169 break;
170 case 22: // invalid session ID; access denied
171 case 378: // buffer pools cannot be created as specified
172 case 600: // internal error code
173 case 602: // internal programming exception
174 case 603: // ORACLE server session terminated by fatal error
175 case 604: // error occurred at recursive SQL level
176 case 609: // could not attach to incoming connection
177 case 1012: // not logged on
178 case 1013: // user requested cancel of current operation
179 case 1033: // ORACLE initialization or shutdown in progress
180 case 1034: // ORACLE not available
181 case 1041: // internal error. hostdef extension doesn't exist
182 case 1043: // user side memory corruption
183 case 1089: // immediate shutdown or close in progress
184 case 1090: // shutdown in progress - connection is not permitted
185 case 1092: // ORACLE instance terminated. Disconnection forced
186 case 3113: // end-of-file on communication channel
187 case 3114: // not connected to ORACLE
188 case 3122: // attempt to close ORACLE-side window on user side
189 case 3135: // connection lost contact
190 case 12153: // TNS:not connected
191 case 12203: // TNS:unable to connect to destination
192 case 12500: // TNS:listener failed to start a dedicated server process
193 case 12571: // TNS:packet writer failure
194 case 27146: // post/wait initialization failed
195 case 28511: // lost RPC connection to heterogeneous remote agent
196 exceptionType = cxoOperationalErrorException;
197 break;
198 default:
199 exceptionType = cxoDatabaseErrorException;
200 break;
201 }
202 PyErr_SetObject(exceptionType, (PyObject*) error);
203 Py_DECREF(error);
204 return -1;
205 }
206
207
208 //-----------------------------------------------------------------------------
209 // cxoError_raiseFromString()
210 // Internal method for raising an exception given an error information
211 // structure from DPI. Return -1 as a convenience to the caller.
212 //-----------------------------------------------------------------------------
213 PyObject *cxoError_raiseFromString(PyObject *exceptionType,
214 const char *message)
215 {
216 cxoError *error;
217
218 error = cxoError_newFromString(message);
219 if (!error)
220 return NULL;
221 PyErr_SetObject(exceptionType, (PyObject*) error);
222 Py_DECREF(error);
223 return NULL;
224 }
225
226
227 //-----------------------------------------------------------------------------
228 // cxoError_reduce()
229 // Method provided for pickling/unpickling of Error objects.
230 //-----------------------------------------------------------------------------
231 static PyObject *cxoError_reduce(cxoError *error)
232 {
233 return Py_BuildValue("(O(OiIO))", Py_TYPE(error), error->message,
234 error->code, error->offset, error->context);
235 }
236
237
238 //-----------------------------------------------------------------------------
239 // cxoError_str()
240 // Return a string representation of the error variable.
241 //-----------------------------------------------------------------------------
242 static PyObject *cxoError_str(cxoError *error)
243 {
244 Py_INCREF(error->message);
245 return error->message;
246 }
24247
25248
26249 //-----------------------------------------------------------------------------
50273 //-----------------------------------------------------------------------------
51274 PyTypeObject cxoPyTypeError = {
52275 PyVarObject_HEAD_INIT(NULL, 0)
53 "cx_Oracle._Error", // tp_name
54 sizeof(cxoError), // tp_basicsize
55 0, // tp_itemsize
56 (destructor) cxoError_free, // tp_dealloc
57 0, // tp_print
58 0, // tp_getattr
59 0, // tp_setattr
60 0, // tp_compare
61 0, // tp_repr
62 0, // tp_as_number
63 0, // tp_as_sequence
64 0, // tp_as_mapping
65 0, // tp_hash
66 0, // tp_call
67 (reprfunc) cxoError_str, // tp_str
68 0, // tp_getattro
69 0, // tp_setattro
70 0, // tp_as_buffer
71 Py_TPFLAGS_DEFAULT, // tp_flags
72 0, // tp_doc
73 0, // tp_traverse
74 0, // tp_clear
75 0, // tp_richcompare
76 0, // tp_weaklistoffset
77 0, // tp_iter
78 0, // tp_iternext
79 cxoErrorMethods, // tp_methods
80 cxoErrorMembers, // tp_members
81 0, // tp_getset
82 0, // tp_base
83 0, // tp_dict
84 0, // tp_descr_get
85 0, // tp_descr_set
86 0, // tp_dictoffset
87 0, // tp_init
88 0, // tp_alloc
89 cxoError_new, // tp_new
90 0, // tp_free
91 0, // tp_is_gc
92 0 // tp_bases
276 .tp_name = "cx_Oracle._Error",
277 .tp_basicsize = sizeof(cxoError),
278 .tp_dealloc = (destructor) cxoError_free,
279 .tp_str = (reprfunc) cxoError_str,
280 .tp_flags = Py_TPFLAGS_DEFAULT,
281 .tp_methods = cxoErrorMethods,
282 .tp_members = cxoErrorMembers,
283 .tp_new = cxoError_new
93284 };
94
95
96 //-----------------------------------------------------------------------------
97 // cxoError_free()
98 // Deallocate the error.
99 //-----------------------------------------------------------------------------
100 static void cxoError_free(cxoError *error)
101 {
102 Py_CLEAR(error->message);
103 Py_CLEAR(error->context);
104 PyObject_Del(error);
105 }
106
107
108 //-----------------------------------------------------------------------------
109 // cxoError_new()
110 // Create a new error object. This is intended to only be used by the
111 // unpickling routine, and not by direct creation!
112 //-----------------------------------------------------------------------------
113 static PyObject *cxoError_new(PyTypeObject *type, PyObject *args,
114 PyObject *keywordArgs)
115 {
116 PyObject *message, *context;
117 int isRecoverable, code;
118 cxoError *error;
119 unsigned offset;
120
121 isRecoverable = 0;
122 if (!PyArg_ParseTuple(args, "OiIO|i", &message, &code, &offset, &context,
123 &isRecoverable))
124 return NULL;
125 error = (cxoError*) type->tp_alloc(type, 0);
126 if (!error)
127 return NULL;
128
129 error->code = code;
130 error->offset = offset;
131 error->isRecoverable = (char) isRecoverable;
132 Py_INCREF(message);
133 error->message = message;
134 Py_INCREF(context);
135 error->context = context;
136
137 return (PyObject*) error;
138 }
139
140
141 //-----------------------------------------------------------------------------
142 // cxoError_newFromInfo()
143 // Internal method for creating an error object from the DPI error
144 // information.
145 //-----------------------------------------------------------------------------
146 cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo)
147 {
148 cxoError *error;
149
150 // create error object and initialize it
151 error = (cxoError*) cxoPyTypeError.tp_alloc(&cxoPyTypeError, 0);
152 if (!error)
153 return NULL;
154 error->code = errorInfo->code;
155 error->offset = errorInfo->offset;
156 error->isRecoverable = (char) errorInfo->isRecoverable;
157
158 // create message
159 error->message = cxoPyString_fromEncodedString(errorInfo->message,
160 errorInfo->messageLength, errorInfo->encoding, NULL);
161 if (!error->message) {
162 Py_DECREF(error);
163 return NULL;
164 }
165
166 // create context composed of function name and action
167 #if PY_MAJOR_VERSION >= 3
168 error->context = PyUnicode_FromFormat("%s: %s", errorInfo->fnName,
169 errorInfo->action);
170 #else
171 error->context = PyString_FromFormat("%s: %s", errorInfo->fnName,
172 errorInfo->action);
173 #endif
174 if (!error->context) {
175 Py_DECREF(error);
176 return NULL;
177 }
178
179 return error;
180 }
181
182
183 //-----------------------------------------------------------------------------
184 // cxoError_newFromString()
185 // Internal method for creating an error object from the DPI error
186 // information.
187 //-----------------------------------------------------------------------------
188 static cxoError *cxoError_newFromString(const char *message)
189 {
190 cxoError *error;
191
192 error = (cxoError*) cxoPyTypeError.tp_alloc(&cxoPyTypeError, 0);
193 if (!error)
194 return NULL;
195 Py_INCREF(Py_None);
196 error->context = Py_None;
197 error->message = cxoPyString_fromAscii(message);
198 if (!error->message) {
199 Py_DECREF(error);
200 return NULL;
201 }
202
203 return error;
204 }
205
206
207 //-----------------------------------------------------------------------------
208 // cxoError_raiseAndReturnInt()
209 // Internal method for raising an exception from an error generated from DPI.
210 // Return -1 as a convenience to the caller.
211 //-----------------------------------------------------------------------------
212 int cxoError_raiseAndReturnInt(void)
213 {
214 dpiErrorInfo errorInfo;
215
216 dpiContext_getError(cxoDpiContext, &errorInfo);
217 return cxoError_raiseFromInfo(&errorInfo);
218 }
219
220
221 //-----------------------------------------------------------------------------
222 // cxoError_raiseAndReturnNull()
223 // Internal method for raising an exception from an error generated from DPI.
224 // Return NULL as a convenience to the caller.
225 //-----------------------------------------------------------------------------
226 PyObject *cxoError_raiseAndReturnNull(void)
227 {
228 cxoError_raiseAndReturnInt();
229 return NULL;
230 }
231
232
233 //-----------------------------------------------------------------------------
234 // cxoError_raiseFromInfo()
235 // Internal method for raising an exception given an error information
236 // structure from DPI. Return -1 as a convenience to the caller.
237 //-----------------------------------------------------------------------------
238 int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo)
239 {
240 PyObject *exceptionType;
241 cxoError *error;
242
243 error = cxoError_newFromInfo(errorInfo);
244 if (!error)
245 return -1;
246 switch (errorInfo->code) {
247 case 1:
248 case 1400:
249 case 2290:
250 case 2291:
251 case 2292:
252 exceptionType = cxoIntegrityErrorException;
253 break;
254 case 22:
255 case 378:
256 case 602:
257 case 603:
258 case 604:
259 case 609:
260 case 1012:
261 case 1013:
262 case 1033:
263 case 1034:
264 case 1041:
265 case 1043:
266 case 1089:
267 case 1090:
268 case 1092:
269 case 3113:
270 case 3114:
271 case 3122:
272 case 3135:
273 case 12153:
274 case 12203:
275 case 12500:
276 case 12571:
277 case 27146:
278 case 28511:
279 exceptionType = cxoOperationalErrorException;
280 break;
281 default:
282 exceptionType = cxoDatabaseErrorException;
283 break;
284 }
285 PyErr_SetObject(exceptionType, (PyObject*) error);
286 Py_DECREF(error);
287 return -1;
288 }
289
290
291 //-----------------------------------------------------------------------------
292 // cxoError_raiseFromString()
293 // Internal method for raising an exception given an error information
294 // structure from DPI. Return -1 as a convenience to the caller.
295 //-----------------------------------------------------------------------------
296 PyObject *cxoError_raiseFromString(PyObject *exceptionType,
297 const char *message)
298 {
299 cxoError *error;
300
301 error = cxoError_newFromString(message);
302 if (!error)
303 return NULL;
304 PyErr_SetObject(exceptionType, (PyObject*) error);
305 Py_DECREF(error);
306 return NULL;
307 }
308
309
310 //-----------------------------------------------------------------------------
311 // cxoError_reduce()
312 // Method provided for pickling/unpickling of Error objects.
313 //-----------------------------------------------------------------------------
314 static PyObject *cxoError_reduce(cxoError *error)
315 {
316 return Py_BuildValue("(O(OiIO))", Py_TYPE(error), error->message,
317 error->code, error->offset, error->context);
318 }
319
320
321 //-----------------------------------------------------------------------------
322 // cxoError_str()
323 // Return a string representation of the error variable.
324 //-----------------------------------------------------------------------------
325 static PyObject *cxoError_str(cxoError *error)
326 {
327 Py_INCREF(error->message);
328 return error->message;
329 }
330
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //-----------------------------------------------------------------------------
33
44 //-----------------------------------------------------------------------------
88 //-----------------------------------------------------------------------------
99
1010 #include "cxoModule.h"
11
12 //-----------------------------------------------------------------------------
13 // functions for the Python type "Object"
14 //-----------------------------------------------------------------------------
15 static void cxoFuture_free(cxoFuture*);
16 static PyObject *cxoFuture_getAttr(cxoFuture*, PyObject*);
17 static int cxoFuture_setAttr(cxoFuture*, PyObject*, PyObject*);
18
19
20 //-----------------------------------------------------------------------------
21 // Python type declaration
22 //-----------------------------------------------------------------------------
23 PyTypeObject cxoPyTypeFuture = {
24 PyVarObject_HEAD_INIT(NULL, 0)
25 "cx_Oracle.__future__", // tp_name
26 sizeof(cxoFuture), // tp_basicsize
27 0, // tp_itemsize
28 (destructor) cxoFuture_free, // tp_dealloc
29 0, // tp_print
30 0, // tp_getattr
31 0, // tp_setattr
32 0, // tp_compare
33 0, // tp_repr
34 0, // tp_as_number
35 0, // tp_as_sequence
36 0, // tp_as_mapping
37 0, // tp_hash
38 0, // tp_call
39 0, // tp_str
40 (getattrofunc) cxoFuture_getAttr, // tp_getattro
41 (setattrofunc) cxoFuture_setAttr, // tp_setattro
42 0, // tp_as_buffer
43 Py_TPFLAGS_DEFAULT // tp_flags
44 };
45
4611
4712 //-----------------------------------------------------------------------------
4813 // cxoFuture_free()
7540 return 0;
7641 }
7742
43
44 //-----------------------------------------------------------------------------
45 // Python type declaration
46 //-----------------------------------------------------------------------------
47 PyTypeObject cxoPyTypeFuture = {
48 PyVarObject_HEAD_INIT(NULL, 0)
49 .tp_name = "cx_Oracle.__future__",
50 .tp_basicsize = sizeof(cxoFuture),
51 .tp_dealloc = (destructor) cxoFuture_free,
52 .tp_getattro = (getattrofunc) cxoFuture_getAttr,
53 .tp_setattro = (setattrofunc) cxoFuture_setAttr,
54 .tp_flags = Py_TPFLAGS_DEFAULT
55 };
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoJsonBuffer.c
6 // Defines buffer structure and routines for populating JSON values. These
7 // are used to translate Python objects (scalars, dictionaries and lists) into
8 // JSON values stored in the database.
9 //-----------------------------------------------------------------------------
10
11 #include "cxoModule.h"
12
13 #define CXO_JSON_ENCODING "UTF-8"
14
15 //-----------------------------------------------------------------------------
16 // cxoJsonBuffer_getBuffer()
17 // Acquire a new buffer from the array of buffers. If one is not available,
18 // more space is allocated in chunks.
19 //-----------------------------------------------------------------------------
20 static int cxoJsonBuffer_getBuffer(cxoJsonBuffer *buf, cxoBuffer **buffer)
21 {
22 cxoBuffer *tempBuffers;
23
24 if (buf->numBuffers == buf->allocatedBuffers) {
25 buf->allocatedBuffers += 16;
26 tempBuffers = PyMem_Realloc(buf->buffers,
27 buf->allocatedBuffers * sizeof(cxoBuffer));
28 if (!tempBuffers) {
29 PyErr_NoMemory();
30 return -1;
31 }
32 buf->buffers = tempBuffers;
33 }
34 *buffer = &buf->buffers[buf->numBuffers++];
35
36 return 0;
37 }
38
39
40 //-----------------------------------------------------------------------------
41 // cxoJsonBuffer_populateNode()
42 // Populate a particular node with the value of the Python object.
43 //-----------------------------------------------------------------------------
44 static int cxoJsonBuffer_populateNode(cxoJsonBuffer *buf, dpiJsonNode *node,
45 PyObject *value)
46 {
47 cxoTransformNum transformNum;
48 PyObject *childValue, *key;
49 cxoBuffer *tempBuffer;
50 Py_ssize_t pos, size;
51 dpiJsonArray *array;
52 dpiJsonObject *obj;
53 char message[250];
54 uint32_t i;
55
56 // handle NULL values
57 if (value == Py_None) {
58 node->oracleTypeNum = DPI_ORACLE_TYPE_NONE;
59 node->nativeTypeNum = DPI_NATIVE_TYPE_NULL;
60 return 0;
61 }
62
63 // handle arrays
64 if (PyList_Check(value)) {
65
66 // initialize array
67 node->oracleTypeNum = DPI_ORACLE_TYPE_JSON_ARRAY;
68 node->nativeTypeNum = DPI_NATIVE_TYPE_JSON_ARRAY;
69 array = &node->value->asJsonArray;
70 array->numElements = (uint32_t) PyList_GET_SIZE(value);
71 array->elements = PyMem_Calloc(array->numElements,
72 sizeof(dpiJsonNode));
73 array->elementValues = PyMem_Calloc(array->numElements,
74 sizeof(dpiDataBuffer));
75 if (!array->elements || !array->elementValues) {
76 PyErr_NoMemory();
77 return -1;
78 }
79
80 // process each element of the array
81 for (i = 0; i < array->numElements; i++) {
82 childValue = PyList_GET_ITEM(value, i);
83 array->elements[i].value = &array->elementValues[i];
84 if (cxoJsonBuffer_populateNode(buf, &array->elements[i],
85 childValue) < 0)
86 return -1;
87 }
88
89 return 0;
90 }
91
92 // handle dictionaries
93 if (PyDict_Check(value)) {
94
95 // initialize object
96 node->oracleTypeNum = DPI_ORACLE_TYPE_JSON_OBJECT;
97 node->nativeTypeNum = DPI_NATIVE_TYPE_JSON_OBJECT;
98 obj = &node->value->asJsonObject;
99 size = PyDict_Size(value);
100 if (size < 0)
101 return -1;
102 obj->numFields = (uint32_t) size;
103 obj->fieldNames = PyMem_Calloc(obj->numFields, sizeof(char*));
104 obj->fieldNameLengths = PyMem_Calloc(obj->numFields, sizeof(uint32_t));
105 obj->fields = PyMem_Calloc(obj->numFields, sizeof(dpiJsonNode));
106 obj->fieldValues = PyMem_Calloc(obj->numFields,
107 sizeof(dpiDataBuffer));
108 if (!obj->fieldNames || !obj->fieldNameLengths || !obj->fields ||
109 !obj->fieldValues) {
110 PyErr_NoMemory();
111 return -1;
112 }
113
114 // process each entry in the dictionary
115 i = 0;
116 pos = 0;
117 while (PyDict_Next(value, &pos, &key, &childValue)) {
118 if (cxoJsonBuffer_getBuffer(buf, &tempBuffer) < 0)
119 return -1;
120 if (cxoBuffer_fromObject(tempBuffer, key, CXO_JSON_ENCODING) < 0)
121 return -1;
122 obj->fields[i].value = &obj->fieldValues[i];
123 obj->fieldNames[i] = (char*) tempBuffer->ptr;
124 obj->fieldNameLengths[i] = tempBuffer->size;
125 if (cxoJsonBuffer_populateNode(buf, &obj->fields[i],
126 childValue) < 0)
127 return -1;
128 i++;
129 }
130
131 return 0;
132 }
133
134 // handle scalar values
135 tempBuffer = NULL;
136 transformNum = cxoTransform_getNumFromPythonValue(value, 1);
137 switch (transformNum) {
138
139 // strings and bytes must have a buffer made available for them to
140 // store a reference to the object and the actual pointer and length;
141 // numbers are converted to a string in order to prevent precision loss
142 case CXO_TRANSFORM_STRING:
143 case CXO_TRANSFORM_BINARY:
144 case CXO_TRANSFORM_INT:
145 case CXO_TRANSFORM_FLOAT:
146 case CXO_TRANSFORM_DECIMAL:
147 if (cxoJsonBuffer_getBuffer(buf, &tempBuffer) < 0)
148 return -1;
149 break;
150
151 // swap CXO_TRANSFORM_DATETIME to CXO_TRANSFORM_TIMESTAMP to preserve
152 // fractional seconds
153 case CXO_TRANSFORM_DATETIME:
154 transformNum = CXO_TRANSFORM_TIMESTAMP;
155 break;
156
157 // all other types do not need any special processing
158 case CXO_TRANSFORM_BOOLEAN:
159 case CXO_TRANSFORM_DATE:
160 case CXO_TRANSFORM_TIMEDELTA:
161 break;
162
163 // any other type is not currently supported
164 default:
165 snprintf(message, sizeof(message), "Python type %s not supported.",
166 Py_TYPE(value)->tp_name);
167 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
168 return -1;
169 }
170
171 // transform the Python value into the Oracle value
172 cxoTransform_getTypeInfo(transformNum, &node->oracleTypeNum,
173 &node->nativeTypeNum);
174 if (cxoTransform_fromPython(transformNum, &node->nativeTypeNum, value,
175 node->value, tempBuffer, CXO_JSON_ENCODING, CXO_JSON_ENCODING,
176 NULL, 0) < 0)
177 return -1;
178
179 return 0;
180 }
181
182
183 //-----------------------------------------------------------------------------
184 // cxoJsonBuffer_freeNode()
185 // Frees any arrays allocated earlier for the specified node.
186 //-----------------------------------------------------------------------------
187 static void cxoJsonBuffer_freeNode(dpiJsonNode *node)
188 {
189 dpiJsonArray *array;
190 dpiJsonObject *obj;
191 uint32_t i;
192
193 switch (node->nativeTypeNum) {
194 case DPI_NATIVE_TYPE_JSON_ARRAY:
195 array = &node->value->asJsonArray;
196 if (array->elements) {
197 for (i = 0; i < array->numElements; i++) {
198 if (array->elements[i].value)
199 cxoJsonBuffer_freeNode(&array->elements[i]);
200 }
201 PyMem_Free(array->elements);
202 array->elements = NULL;
203 }
204 if (array->elementValues) {
205 PyMem_Free(array->elementValues);
206 array->elementValues = NULL;
207 }
208 break;
209 case DPI_NATIVE_TYPE_JSON_OBJECT:
210 obj = &node->value->asJsonObject;
211 if (obj->fields) {
212 for (i = 0; i < obj->numFields; i++) {
213 if (obj->fields[i].value)
214 cxoJsonBuffer_freeNode(&obj->fields[i]);
215 }
216 PyMem_Free(obj->fields);
217 obj->fields = NULL;
218 }
219 if (obj->fieldNames) {
220 PyMem_Free(obj->fieldNames);
221 obj->fieldNames = NULL;
222 }
223 if (obj->fieldNameLengths) {
224 PyMem_Free(obj->fieldNameLengths);
225 obj->fieldNameLengths = NULL;
226 }
227 if (obj->fieldValues) {
228 PyMem_Free(obj->fieldValues);
229 obj->fieldValues = NULL;
230 }
231 break;
232 }
233 }
234
235
236 //-----------------------------------------------------------------------------
237 // cxoJsonBuffer_free()
238 // Frees any memory allocated for the JSON buffer.
239 //-----------------------------------------------------------------------------
240 void cxoJsonBuffer_free(cxoJsonBuffer *buf)
241 {
242 uint32_t i;
243
244 if (buf->buffers) {
245 for (i = 0; i < buf->numBuffers; i++)
246 cxoBuffer_clear(&buf->buffers[i]);
247 PyMem_Free(buf->buffers);
248 buf->buffers = NULL;
249 }
250 cxoJsonBuffer_freeNode(&buf->topNode);
251 }
252
253
254 //-----------------------------------------------------------------------------
255 // cxoJsonBuffer_fromObject()
256 // Populate the JSON buffer from a Python object.
257 //-----------------------------------------------------------------------------
258 int cxoJsonBuffer_fromObject(cxoJsonBuffer *buf, PyObject *obj)
259 {
260 // initialize JSON buffer structure
261 buf->topNode.value = &buf->topNodeBuffer;
262 buf->allocatedBuffers = 0;
263 buf->numBuffers = 0;
264 buf->buffers = NULL;
265
266 // populate the top level node
267 return cxoJsonBuffer_populateNode(buf, &buf->topNode, obj);
268 }
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // Declaration of external LOB functions.
18 //-----------------------------------------------------------------------------
19 static void cxoLob_free(cxoLob*);
20 static PyObject *cxoLob_str(cxoLob*);
21 static PyObject *cxoLob_size(cxoLob*, PyObject*);
22 static PyObject *cxoLob_open(cxoLob*, PyObject*);
23 static PyObject *cxoLob_close(cxoLob*, PyObject*);
24 static PyObject *cxoLob_read(cxoLob*, PyObject*, PyObject*);
25 static PyObject *cxoLob_write(cxoLob*, PyObject*, PyObject*);
26 static PyObject *cxoLob_trim(cxoLob*, PyObject*, PyObject*);
27 static PyObject *cxoLob_getChunkSize(cxoLob*, PyObject*);
28 static PyObject *cxoLob_isOpen(cxoLob*, PyObject*);
29 static PyObject *cxoLob_getFileName(cxoLob*, PyObject*);
30 static PyObject *cxoLob_setFileName(cxoLob*, PyObject*);
31 static PyObject *cxoLob_fileExists(cxoLob*, PyObject*);
32 static PyObject *cxoLob_reduce(cxoLob*);
33
34
35 //-----------------------------------------------------------------------------
36 // declaration of methods for Python type "LOB"
17 // cxoLob_new()
18 // Create a new LOB.
19 //-----------------------------------------------------------------------------
20 PyObject *cxoLob_new(cxoConnection *connection, cxoDbType *dbType,
21 dpiLob *handle)
22 {
23 cxoLob *lob;
24
25 lob = (cxoLob*) cxoPyTypeLob.tp_alloc(&cxoPyTypeLob, 0);
26 if (!lob)
27 return NULL;
28 lob->handle = handle;
29 Py_INCREF(connection);
30 lob->connection = connection;
31 Py_INCREF(dbType);
32 lob->dbType = dbType;
33 return (PyObject*) lob;
34 }
35
36
37 //-----------------------------------------------------------------------------
38 // cxoLob_free()
39 // Free a LOB.
40 //-----------------------------------------------------------------------------
41 static void cxoLob_free(cxoLob *lob)
42 {
43 if (lob->handle) {
44 dpiLob_release(lob->handle);
45 lob->handle = NULL;
46 }
47 Py_CLEAR(lob->dbType);
48 Py_CLEAR(lob->connection);
49 Py_TYPE(lob)->tp_free((PyObject*) lob);
50 }
51
52
53 //-----------------------------------------------------------------------------
54 // cxoLob_internalRead()
55 // Return a portion (or all) of the data in the LOB.
56 //-----------------------------------------------------------------------------
57 static PyObject *cxoLob_internalRead(cxoLob *lob, uint64_t offset,
58 uint64_t amount)
59 {
60 uint64_t bufferSize;
61 PyObject *result;
62 char *buffer;
63 int status;
64
65 // modify the arguments
66 if (amount == (uint64_t)(-1)) {
67 if (dpiLob_getSize(lob->handle, &amount) < 0)
68 return cxoError_raiseAndReturnNull();
69 if (amount >= offset)
70 amount = amount - offset + 1;
71 else amount = 1;
72 }
73
74 // create a buffer of the correct size
75 if (dpiLob_getBufferSize(lob->handle, amount, &bufferSize) < 0)
76 return cxoError_raiseAndReturnNull();
77 buffer = (char*) PyMem_Malloc((Py_ssize_t) bufferSize);
78 if (!buffer)
79 return PyErr_NoMemory();
80
81 // read the LOB
82 Py_BEGIN_ALLOW_THREADS
83 status = dpiLob_readBytes(lob->handle, offset, amount, buffer,
84 &bufferSize);
85 Py_END_ALLOW_THREADS
86 if (status < 0) {
87 PyMem_Free(buffer);
88 return cxoError_raiseAndReturnNull();
89 }
90
91 // return the result
92 if (lob->dbType == cxoDbTypeNclob) {
93 result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize,
94 lob->connection->encodingInfo.nencoding, NULL);
95 } else if (lob->dbType == cxoDbTypeClob) {
96 result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize,
97 lob->connection->encodingInfo.encoding, NULL);
98 } else {
99 result = PyBytes_FromStringAndSize(buffer, (Py_ssize_t) bufferSize);
100 }
101 PyMem_Free(buffer);
102 return result;
103 }
104
105
106 //-----------------------------------------------------------------------------
107 // cxoLob_internalWrite()
108 // Write the data in the Python object to the LOB.
109 //-----------------------------------------------------------------------------
110 static int cxoLob_internalWrite(cxoLob *lob, PyObject *dataObj,
111 uint64_t offset)
112 {
113 const char *encoding;
114 cxoBuffer buffer;
115 int status;
116
117 if (lob->dbType == cxoDbTypeNclob)
118 encoding = lob->connection->encodingInfo.nencoding;
119 else encoding = lob->connection->encodingInfo.encoding;
120 if (cxoBuffer_fromObject(&buffer, dataObj, encoding) < 0)
121 return -1;
122 Py_BEGIN_ALLOW_THREADS
123 status = dpiLob_writeBytes(lob->handle, offset,
124 (char*) buffer.ptr, buffer.size);
125 Py_END_ALLOW_THREADS
126 cxoBuffer_clear(&buffer);
127 if (status < 0)
128 return cxoError_raiseAndReturnInt();
129 return 0;
130 }
131
132
133 //-----------------------------------------------------------------------------
134 // cxoLob_size()
135 // Return the size of the data in the LOB.
136 //-----------------------------------------------------------------------------
137 static PyObject *cxoLob_size(cxoLob *lob, PyObject *args)
138 {
139 uint64_t length;
140
141 if (dpiLob_getSize(lob->handle, &length) < 0)
142 return cxoError_raiseAndReturnNull();
143 return PyLong_FromUnsignedLongLong(length);
144 }
145
146
147 //-----------------------------------------------------------------------------
148 // cxoLob_open()
149 // Open the LOB to speed further accesses.
150 //-----------------------------------------------------------------------------
151 static PyObject *cxoLob_open(cxoLob *lob, PyObject *args)
152 {
153 int status;
154
155 Py_BEGIN_ALLOW_THREADS
156 status = dpiLob_openResource(lob->handle);
157 Py_END_ALLOW_THREADS
158 if (status < 0)
159 return cxoError_raiseAndReturnNull();
160 Py_RETURN_NONE;
161 }
162
163
164 //-----------------------------------------------------------------------------
165 // cxoLob_close()
166 // Close the LOB.
167 //-----------------------------------------------------------------------------
168 static PyObject *cxoLob_close(cxoLob *lob, PyObject *args)
169 {
170 int status;
171
172 Py_BEGIN_ALLOW_THREADS
173 status = dpiLob_closeResource(lob->handle);
174 Py_END_ALLOW_THREADS
175 if (status < 0)
176 return cxoError_raiseAndReturnNull();
177 Py_RETURN_NONE;
178 }
179
180
181 //-----------------------------------------------------------------------------
182 // cxoLob_read()
183 // Return a portion (or all) of the data in the LOB.
184 //-----------------------------------------------------------------------------
185 static PyObject *cxoLob_read(cxoLob *lob, PyObject *args, PyObject *keywordArgs)
186 {
187 static char *keywordList[] = { "offset", "amount", NULL };
188 unsigned PY_LONG_LONG offset, amount;
189
190 // offset and amount are expected, both optional
191 offset = 1;
192 amount = (unsigned PY_LONG_LONG)(-1);
193 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|KK", keywordList,
194 &offset, &amount))
195 return NULL;
196 return cxoLob_internalRead(lob, (uint64_t) offset, (uint64_t) amount);
197 }
198
199
200 //-----------------------------------------------------------------------------
201 // cxoLob_str()
202 // Return all of the data in the LOB.
203 //-----------------------------------------------------------------------------
204 static PyObject *cxoLob_str(cxoLob *lob)
205 {
206 return cxoLob_internalRead(lob, 1, (uint64_t)(-1));
207 }
208
209
210 //-----------------------------------------------------------------------------
211 // cxoLob_write()
212 // Write a value to the LOB.
213 //-----------------------------------------------------------------------------
214 static PyObject *cxoLob_write(cxoLob *lob, PyObject *args,
215 PyObject *keywordArgs)
216 {
217 static char *keywordList[] = { "data", "offset", NULL };
218 unsigned PY_LONG_LONG offset;
219 PyObject *dataObj;
220
221 offset = 1;
222 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|K", keywordList,
223 &dataObj, &offset))
224 return NULL;
225 if (cxoLob_internalWrite(lob, dataObj, (uint64_t) offset) < 0)
226 return NULL;
227 Py_RETURN_NONE;
228 }
229
230
231 //-----------------------------------------------------------------------------
232 // cxoLob_trim()
233 // Trim the LOB to the specified length.
234 //-----------------------------------------------------------------------------
235 static PyObject *cxoLob_trim(cxoLob *lob, PyObject *args,
236 PyObject *keywordArgs)
237 {
238 static char *keywordList[] = { "newSize", NULL };
239 unsigned PY_LONG_LONG newSize;
240 int status;
241
242 newSize = 0;
243 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|K", keywordList,
244 &newSize))
245 return NULL;
246 Py_BEGIN_ALLOW_THREADS
247 status = dpiLob_trim(lob->handle, (uint64_t) newSize);
248 Py_END_ALLOW_THREADS
249 if (status < 0)
250 return cxoError_raiseAndReturnNull();
251 Py_RETURN_NONE;
252 }
253
254
255 //-----------------------------------------------------------------------------
256 // cxoLob_reduce()
257 // Method provided for pickling/unpickling of LOBs.
258 //-----------------------------------------------------------------------------
259 static PyObject *cxoLob_reduce(cxoLob *lob)
260 {
261 PyObject *result, *value;
262
263 value = cxoLob_str(lob);
264 if (!value)
265 return NULL;
266 result = Py_BuildValue("(O(O))", Py_TYPE(value), value);
267 Py_DECREF(value);
268 return result;
269 }
270
271
272 //-----------------------------------------------------------------------------
273 // cxoLob_getChunkSize()
274 // Return the chunk size that should be used when reading/writing the LOB in
275 // chunks.
276 //-----------------------------------------------------------------------------
277 static PyObject *cxoLob_getChunkSize(cxoLob *lob, PyObject *args)
278 {
279 uint32_t size;
280
281 if (dpiLob_getChunkSize(lob->handle, &size) < 0)
282 return cxoError_raiseAndReturnNull();
283 return PyLong_FromLong(size);
284 }
285
286
287 //-----------------------------------------------------------------------------
288 // cxoLob_isOpen()
289 // Return a boolean indicating if the lob is open or not.
290 //-----------------------------------------------------------------------------
291 static PyObject *cxoLob_isOpen(cxoLob *lob, PyObject *args)
292 {
293 int isOpen, status;
294
295 Py_BEGIN_ALLOW_THREADS
296 status = dpiLob_getIsResourceOpen(lob->handle, &isOpen);
297 Py_END_ALLOW_THREADS
298 if (status < 0)
299 return cxoError_raiseAndReturnNull();
300 return PyBool_FromLong(isOpen);
301 }
302
303
304 //-----------------------------------------------------------------------------
305 // cxoLob_getFileName()
306 // Return the directory alias and file name for the BFILE lob.
307 //-----------------------------------------------------------------------------
308 static PyObject *cxoLob_getFileName(cxoLob *lob, PyObject *args)
309 {
310 uint32_t directoryAliasLength, fileNameLength;
311 const char *directoryAlias, *fileName;
312 PyObject *result, *temp;
313 int status;
314
315 // get the information from the LOB
316 Py_BEGIN_ALLOW_THREADS
317 status = dpiLob_getDirectoryAndFileName(lob->handle, &directoryAlias,
318 &directoryAliasLength, &fileName, &fileNameLength);
319 Py_END_ALLOW_THREADS
320 if (status < 0)
321 return cxoError_raiseAndReturnNull();
322
323 // create the two-tuple for returning
324 result = PyTuple_New(2);
325 if (!result)
326 return NULL;
327 temp = PyUnicode_Decode(directoryAlias, directoryAliasLength,
328 lob->connection->encodingInfo.encoding, NULL);
329 if (!temp) {
330 Py_DECREF(result);
331 return NULL;
332 }
333 PyTuple_SET_ITEM(result, 0, temp);
334 temp = PyUnicode_Decode(fileName, fileNameLength,
335 lob->connection->encodingInfo.encoding, NULL);
336 if (!temp) {
337 Py_DECREF(result);
338 return NULL;
339 }
340 PyTuple_SET_ITEM(result, 1, temp);
341
342 return result;
343 }
344
345
346 //-----------------------------------------------------------------------------
347 // cxoLob_setFileName()
348 // Set the directory alias and file name for the BFILE lob.
349 //-----------------------------------------------------------------------------
350 static PyObject *cxoLob_setFileName(cxoLob *lob, PyObject *args)
351 {
352 cxoBuffer directoryAliasBuffer, fileNameBuffer;
353 PyObject *directoryAliasObj, *fileNameObj;
354 int status;
355
356 // get the directory alias and file name
357 if (!PyArg_ParseTuple(args, "OO", &directoryAliasObj, &fileNameObj))
358 return NULL;
359 if (cxoBuffer_fromObject(&directoryAliasBuffer, directoryAliasObj,
360 lob->connection->encodingInfo.encoding) < 0)
361 return NULL;
362 if (cxoBuffer_fromObject(&fileNameBuffer, fileNameObj,
363 lob->connection->encodingInfo.encoding) < 0) {
364 cxoBuffer_clear(&directoryAliasBuffer);
365 return NULL;
366 }
367
368 // perform the work
369 Py_BEGIN_ALLOW_THREADS
370 status = dpiLob_setDirectoryAndFileName(lob->handle,
371 (char*) directoryAliasBuffer.ptr, directoryAliasBuffer.size,
372 (char*) fileNameBuffer.ptr, fileNameBuffer.size);
373 Py_END_ALLOW_THREADS
374 cxoBuffer_clear(&directoryAliasBuffer);
375 cxoBuffer_clear(&fileNameBuffer);
376 if (status < 0)
377 return cxoError_raiseAndReturnNull();
378
379 Py_RETURN_NONE;
380 }
381
382
383 //-----------------------------------------------------------------------------
384 // cxoLob_fileExists()
385 // Return a boolean indicating if the BFIILE lob exists.
386 //-----------------------------------------------------------------------------
387 static PyObject *cxoLob_fileExists(cxoLob *lob, PyObject *args)
388 {
389 int status, exists;
390
391 Py_BEGIN_ALLOW_THREADS
392 status = dpiLob_getFileExists(lob->handle, &exists);
393 Py_END_ALLOW_THREADS
394 if (status < 0)
395 return cxoError_raiseAndReturnNull();
396 if (exists)
397 Py_RETURN_TRUE;
398 Py_RETURN_FALSE;
399 }
400
401
402 //-----------------------------------------------------------------------------
403 // declaration of methods
37404 //-----------------------------------------------------------------------------
38405 static PyMethodDef cxoLobMethods[] = {
39406 { "size", (PyCFunction) cxoLob_size, METH_NOARGS },
53420
54421
55422 //-----------------------------------------------------------------------------
423 // declaration of members
424 //-----------------------------------------------------------------------------
425 static PyMemberDef cxoMembers[] = {
426 { "type", T_OBJECT, offsetof(cxoLob, dbType), READONLY },
427 { NULL }
428 };
429
430
431 //-----------------------------------------------------------------------------
56432 // Python type declaration
57433 //-----------------------------------------------------------------------------
58434 PyTypeObject cxoPyTypeLob = {
59435 PyVarObject_HEAD_INIT(NULL, 0)
60 "cx_Oracle.LOB", // tp_name
61 sizeof(cxoLob), // tp_basicsize
62 0, // tp_itemsize
63 (destructor) cxoLob_free, // tp_dealloc
64 0, // tp_print
65 0, // tp_getattr
66 0, // tp_setattr
67 0, // tp_compare
68 0, // tp_repr
69 0, // tp_as_number
70 0, // tp_as_sequence
71 0, // tp_as_mapping
72 0, // tp_hash
73 0, // tp_call
74 (reprfunc) cxoLob_str, // tp_str
75 0, // tp_getattro
76 0, // tp_setattro
77 0, // tp_as_buffer
78 Py_TPFLAGS_DEFAULT, // tp_flags
79 0, // tp_doc
80 0, // tp_traverse
81 0, // tp_clear
82 0, // tp_richcompare
83 0, // tp_weaklistoffset
84 0, // tp_iter
85 0, // tp_iternext
86 cxoLobMethods, // tp_methods
87 0, // tp_members
88 0, // tp_getset
89 0, // tp_base
90 0, // tp_dict
91 0, // tp_descr_get
92 0, // tp_descr_set
93 0, // tp_dictoffset
94 0, // tp_init
95 0, // tp_alloc
96 0, // tp_new
97 0, // tp_free
98 0, // tp_is_gc
99 0 // tp_bases
436 .tp_name = "cx_Oracle.LOB",
437 .tp_basicsize = sizeof(cxoLob),
438 .tp_dealloc = (destructor) cxoLob_free,
439 .tp_str = (reprfunc) cxoLob_str,
440 .tp_flags = Py_TPFLAGS_DEFAULT,
441 .tp_methods = cxoLobMethods,
442 .tp_members = cxoMembers
100443 };
101
102
103 //-----------------------------------------------------------------------------
104 // cxoLob_new()
105 // Create a new LOB.
106 //-----------------------------------------------------------------------------
107 PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
108 dpiLob *handle)
109 {
110 cxoLob *lob;
111
112 lob = (cxoLob*) cxoPyTypeLob.tp_alloc(&cxoPyTypeLob, 0);
113 if (!lob)
114 return NULL;
115 lob->handle = handle;
116 lob->oracleTypeNum = oracleTypeNum;
117 Py_INCREF(connection);
118 lob->connection = connection;
119 return (PyObject*) lob;
120 }
121
122
123 //-----------------------------------------------------------------------------
124 // cxoLob_free()
125 // Free a LOB.
126 //-----------------------------------------------------------------------------
127 static void cxoLob_free(cxoLob *lob)
128 {
129 if (lob->handle) {
130 dpiLob_release(lob->handle);
131 lob->handle = NULL;
132 }
133 Py_CLEAR(lob->connection);
134 Py_TYPE(lob)->tp_free((PyObject*) lob);
135 }
136
137
138 //-----------------------------------------------------------------------------
139 // cxoLob_internalRead()
140 // Return a portion (or all) of the data in the LOB.
141 //-----------------------------------------------------------------------------
142 static PyObject *cxoLob_internalRead(cxoLob *lob, uint64_t offset,
143 uint64_t amount)
144 {
145 uint64_t bufferSize;
146 PyObject *result;
147 char *buffer;
148 int status;
149
150 // modify the arguments
151 if (amount == (uint64_t)(-1)) {
152 if (dpiLob_getSize(lob->handle, &amount) < 0)
153 return cxoError_raiseAndReturnNull();
154 if (amount >= offset)
155 amount = amount - offset + 1;
156 else amount = 1;
157 }
158
159 // create a buffer of the correct size
160 if (dpiLob_getBufferSize(lob->handle, amount, &bufferSize) < 0)
161 return cxoError_raiseAndReturnNull();
162 buffer = (char*) PyMem_Malloc((Py_ssize_t) bufferSize);
163 if (!buffer)
164 return PyErr_NoMemory();
165
166 // read the LOB
167 Py_BEGIN_ALLOW_THREADS
168 status = dpiLob_readBytes(lob->handle, offset, amount, buffer,
169 &bufferSize);
170 Py_END_ALLOW_THREADS
171 if (status < 0) {
172 PyMem_Free(buffer);
173 return cxoError_raiseAndReturnNull();
174 }
175
176 // return the result
177 if (lob->oracleTypeNum == DPI_ORACLE_TYPE_NCLOB)
178 result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize,
179 lob->connection->encodingInfo.nencoding, NULL);
180 else if (lob->oracleTypeNum == DPI_ORACLE_TYPE_CLOB)
181 result = cxoPyString_fromEncodedString(buffer, (Py_ssize_t) bufferSize,
182 lob->connection->encodingInfo.encoding, NULL);
183 else result = PyBytes_FromStringAndSize(buffer, (Py_ssize_t) bufferSize);
184 PyMem_Free(buffer);
185 return result;
186 }
187
188
189 //-----------------------------------------------------------------------------
190 // cxoLob_internalWrite()
191 // Write the data in the Python object to the LOB.
192 //-----------------------------------------------------------------------------
193 static int cxoLob_internalWrite(cxoLob *lob, PyObject *dataObj,
194 uint64_t offset)
195 {
196 const char *encoding;
197 cxoBuffer buffer;
198 int status;
199
200 if (lob->oracleTypeNum == DPI_ORACLE_TYPE_NCLOB)
201 encoding = lob->connection->encodingInfo.nencoding;
202 else encoding = lob->connection->encodingInfo.encoding;
203 if (cxoBuffer_fromObject(&buffer, dataObj, encoding) < 0)
204 return -1;
205 Py_BEGIN_ALLOW_THREADS
206 status = dpiLob_writeBytes(lob->handle, offset,
207 (char*) buffer.ptr, buffer.size);
208 Py_END_ALLOW_THREADS
209 cxoBuffer_clear(&buffer);
210 if (status < 0)
211 return cxoError_raiseAndReturnInt();
212 return 0;
213 }
214
215
216 //-----------------------------------------------------------------------------
217 // cxoLob_size()
218 // Return the size of the data in the LOB.
219 //-----------------------------------------------------------------------------
220 static PyObject *cxoLob_size(cxoLob *lob, PyObject *args)
221 {
222 uint64_t length;
223
224 if (dpiLob_getSize(lob->handle, &length) < 0)
225 return cxoError_raiseAndReturnNull();
226 return PyLong_FromUnsignedLongLong(length);
227 }
228
229
230 //-----------------------------------------------------------------------------
231 // cxoLob_open()
232 // Open the LOB to speed further accesses.
233 //-----------------------------------------------------------------------------
234 static PyObject *cxoLob_open(cxoLob *lob, PyObject *args)
235 {
236 int status;
237
238 Py_BEGIN_ALLOW_THREADS
239 status = dpiLob_openResource(lob->handle);
240 Py_END_ALLOW_THREADS
241 if (status < 0)
242 return cxoError_raiseAndReturnNull();
243 Py_RETURN_NONE;
244 }
245
246
247 //-----------------------------------------------------------------------------
248 // cxoLob_close()
249 // Close the LOB.
250 //-----------------------------------------------------------------------------
251 static PyObject *cxoLob_close(cxoLob *lob, PyObject *args)
252 {
253 int status;
254
255 Py_BEGIN_ALLOW_THREADS
256 status = dpiLob_closeResource(lob->handle);
257 Py_END_ALLOW_THREADS
258 if (status < 0)
259 return cxoError_raiseAndReturnNull();
260 Py_RETURN_NONE;
261 }
262
263
264 //-----------------------------------------------------------------------------
265 // cxoLob_read()
266 // Return a portion (or all) of the data in the LOB.
267 //-----------------------------------------------------------------------------
268 static PyObject *cxoLob_read(cxoLob *lob, PyObject *args, PyObject *keywordArgs)
269 {
270 static char *keywordList[] = { "offset", "amount", NULL };
271 unsigned PY_LONG_LONG offset, amount;
272
273 // offset and amount are expected, both optional
274 offset = 1;
275 amount = (unsigned PY_LONG_LONG)(-1);
276 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|KK", keywordList,
277 &offset, &amount))
278 return NULL;
279 return cxoLob_internalRead(lob, (uint64_t) offset, (uint64_t) amount);
280 }
281
282
283 //-----------------------------------------------------------------------------
284 // cxoLob_str()
285 // Return all of the data in the LOB.
286 //-----------------------------------------------------------------------------
287 static PyObject *cxoLob_str(cxoLob *lob)
288 {
289 return cxoLob_internalRead(lob, 1, (uint64_t)(-1));
290 }
291
292
293 //-----------------------------------------------------------------------------
294 // cxoLob_write()
295 // Write a value to the LOB.
296 //-----------------------------------------------------------------------------
297 static PyObject *cxoLob_write(cxoLob *lob, PyObject *args,
298 PyObject *keywordArgs)
299 {
300 static char *keywordList[] = { "data", "offset", NULL };
301 unsigned PY_LONG_LONG offset;
302 PyObject *dataObj;
303
304 offset = 1;
305 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|K", keywordList,
306 &dataObj, &offset))
307 return NULL;
308 if (cxoLob_internalWrite(lob, dataObj, (uint64_t) offset) < 0)
309 return NULL;
310 Py_RETURN_NONE;
311 }
312
313
314 //-----------------------------------------------------------------------------
315 // cxoLob_trim()
316 // Trim the LOB to the specified length.
317 //-----------------------------------------------------------------------------
318 static PyObject *cxoLob_trim(cxoLob *lob, PyObject *args,
319 PyObject *keywordArgs)
320 {
321 static char *keywordList[] = { "newSize", NULL };
322 unsigned PY_LONG_LONG newSize;
323 int status;
324
325 newSize = 0;
326 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|K", keywordList,
327 &newSize))
328 return NULL;
329 Py_BEGIN_ALLOW_THREADS
330 status = dpiLob_trim(lob->handle, (uint64_t) newSize);
331 Py_END_ALLOW_THREADS
332 if (status < 0)
333 return cxoError_raiseAndReturnNull();
334 Py_RETURN_NONE;
335 }
336
337
338 //-----------------------------------------------------------------------------
339 // cxoLob_reduce()
340 // Method provided for pickling/unpickling of LOBs.
341 //-----------------------------------------------------------------------------
342 static PyObject *cxoLob_reduce(cxoLob *lob)
343 {
344 PyObject *result, *value;
345
346 value = cxoLob_str(lob);
347 if (!value)
348 return NULL;
349 result = Py_BuildValue("(O(O))", Py_TYPE(value), value);
350 Py_DECREF(value);
351 return result;
352 }
353
354
355 //-----------------------------------------------------------------------------
356 // cxoLob_getChunkSize()
357 // Return the chunk size that should be used when reading/writing the LOB in
358 // chunks.
359 //-----------------------------------------------------------------------------
360 static PyObject *cxoLob_getChunkSize(cxoLob *lob, PyObject *args)
361 {
362 uint32_t size;
363
364 if (dpiLob_getChunkSize(lob->handle, &size) < 0)
365 return cxoError_raiseAndReturnNull();
366 return PyInt_FromLong(size);
367 }
368
369
370 //-----------------------------------------------------------------------------
371 // cxoLob_isOpen()
372 // Return a boolean indicating if the lob is open or not.
373 //-----------------------------------------------------------------------------
374 static PyObject *cxoLob_isOpen(cxoLob *lob, PyObject *args)
375 {
376 int isOpen, status;
377
378 Py_BEGIN_ALLOW_THREADS
379 status = dpiLob_getIsResourceOpen(lob->handle, &isOpen);
380 Py_END_ALLOW_THREADS
381 if (status < 0)
382 return cxoError_raiseAndReturnNull();
383 return PyBool_FromLong(isOpen);
384 }
385
386
387 //-----------------------------------------------------------------------------
388 // cxoLob_getFileName()
389 // Return the directory alias and file name for the BFILE lob.
390 //-----------------------------------------------------------------------------
391 static PyObject *cxoLob_getFileName(cxoLob *lob, PyObject *args)
392 {
393 uint32_t directoryAliasLength, fileNameLength;
394 const char *directoryAlias, *fileName;
395 PyObject *result, *temp;
396 int status;
397
398 // get the information from the LOB
399 Py_BEGIN_ALLOW_THREADS
400 status = dpiLob_getDirectoryAndFileName(lob->handle, &directoryAlias,
401 &directoryAliasLength, &fileName, &fileNameLength);
402 Py_END_ALLOW_THREADS
403 if (status < 0)
404 return cxoError_raiseAndReturnNull();
405
406 // create the two-tuple for returning
407 result = PyTuple_New(2);
408 if (!result)
409 return NULL;
410 temp = cxoPyString_fromEncodedString(directoryAlias, directoryAliasLength,
411 lob->connection->encodingInfo.encoding, NULL);
412 if (!temp) {
413 Py_DECREF(result);
414 return NULL;
415 }
416 PyTuple_SET_ITEM(result, 0, temp);
417 temp = cxoPyString_fromEncodedString(fileName, fileNameLength,
418 lob->connection->encodingInfo.encoding, NULL);
419 if (!temp) {
420 Py_DECREF(result);
421 return NULL;
422 }
423 PyTuple_SET_ITEM(result, 1, temp);
424
425 return result;
426 }
427
428
429 //-----------------------------------------------------------------------------
430 // cxoLob_setFileName()
431 // Set the directory alias and file name for the BFILE lob.
432 //-----------------------------------------------------------------------------
433 static PyObject *cxoLob_setFileName(cxoLob *lob, PyObject *args)
434 {
435 cxoBuffer directoryAliasBuffer, fileNameBuffer;
436 PyObject *directoryAliasObj, *fileNameObj;
437 int status;
438
439 // get the directory alias and file name
440 if (!PyArg_ParseTuple(args, "OO", &directoryAliasObj, &fileNameObj))
441 return NULL;
442 if (cxoBuffer_fromObject(&directoryAliasBuffer, directoryAliasObj,
443 lob->connection->encodingInfo.encoding) < 0)
444 return NULL;
445 if (cxoBuffer_fromObject(&fileNameBuffer, fileNameObj,
446 lob->connection->encodingInfo.encoding) < 0) {
447 cxoBuffer_clear(&directoryAliasBuffer);
448 return NULL;
449 }
450
451 // perform the work
452 Py_BEGIN_ALLOW_THREADS
453 status = dpiLob_setDirectoryAndFileName(lob->handle,
454 (char*) directoryAliasBuffer.ptr, directoryAliasBuffer.size,
455 (char*) fileNameBuffer.ptr, fileNameBuffer.size);
456 Py_END_ALLOW_THREADS
457 cxoBuffer_clear(&directoryAliasBuffer);
458 cxoBuffer_clear(&fileNameBuffer);
459 if (status < 0)
460 return cxoError_raiseAndReturnNull();
461
462 Py_RETURN_NONE;
463 }
464
465
466 //-----------------------------------------------------------------------------
467 // cxoLob_fileExists()
468 // Return a boolean indicating if the BFIILE lob exists.
469 //-----------------------------------------------------------------------------
470 static PyObject *cxoLob_fileExists(cxoLob *lob, PyObject *args)
471 {
472 int status, exists;
473
474 Py_BEGIN_ALLOW_THREADS
475 status = dpiLob_getFileExists(lob->handle, &exists);
476 Py_END_ALLOW_THREADS
477 if (status < 0)
478 return cxoError_raiseAndReturnNull();
479 if (exists)
480 Py_RETURN_TRUE;
481 Py_RETURN_FALSE;
482 }
483
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1818 // define macro for adding integer constants
1919 #define CXO_ADD_INT_CONSTANT(name, value) \
2020 if (PyModule_AddIntConstant(module, name, value) < 0) \
21 return NULL;
22
23 // define macro for adding Python Database API types
24 #define CXO_ADD_API_TYPE(name, transformNum, typeObj) \
25 if (cxoModule_addApiType(module, name, transformNum, typeObj) < 0) \
26 return NULL;
27
28 // define macro for adding database types
29 #define CXO_ADD_DB_TYPE(num, name, transformNum, typeObj) \
30 if (cxoModule_addDbType(module, num, name, transformNum, typeObj) < 0) \
31 return NULL;
32
33 // define macro for associating database types with Database API types
34 #define CXO_ASSOCIATE_DB_TYPE(apiType, dbType) \
35 if (PyList_Append(apiType->dbTypes, (PyObject*) dbType) < 0) \
2136 return NULL;
2237
2338 // define macro for adding type objects
4762 PyObject *cxoNotSupportedErrorException = NULL;
4863 PyObject *cxoJsonDumpFunction = NULL;
4964 PyObject *cxoJsonLoadFunction = NULL;
65
66 cxoDbType *cxoDbTypeBfile = NULL;
67 cxoDbType *cxoDbTypeBinaryDouble = NULL;
68 cxoDbType *cxoDbTypeBinaryFloat = NULL;
69 cxoDbType *cxoDbTypeBinaryInteger = NULL;
70 cxoDbType *cxoDbTypeBlob = NULL;
71 cxoDbType *cxoDbTypeBoolean = NULL;
72 cxoDbType *cxoDbTypeChar = NULL;
73 cxoDbType *cxoDbTypeClob = NULL;
74 cxoDbType *cxoDbTypeCursor = NULL;
75 cxoDbType *cxoDbTypeDate = NULL;
76 cxoDbType *cxoDbTypeIntervalDS = NULL;
77 cxoDbType *cxoDbTypeIntervalYM = NULL;
78 cxoDbType *cxoDbTypeJson = NULL;
79 cxoDbType *cxoDbTypeLong = NULL;
80 cxoDbType *cxoDbTypeLongRaw = NULL;
81 cxoDbType *cxoDbTypeNchar = NULL;
82 cxoDbType *cxoDbTypeNclob = NULL;
83 cxoDbType *cxoDbTypeNumber = NULL;
84 cxoDbType *cxoDbTypeNvarchar = NULL;
85 cxoDbType *cxoDbTypeObject = NULL;
86 cxoDbType *cxoDbTypeRaw = NULL;
87 cxoDbType *cxoDbTypeRowid = NULL;
88 cxoDbType *cxoDbTypeTimestamp = NULL;
89 cxoDbType *cxoDbTypeTimestampLTZ = NULL;
90 cxoDbType *cxoDbTypeTimestampTZ = NULL;
91 cxoDbType *cxoDbTypeVarchar = NULL;
92
93 cxoApiType *cxoApiTypeBinary = NULL;
94 cxoApiType *cxoApiTypeDatetime = NULL;
95 cxoApiType *cxoApiTypeNumber = NULL;
96 cxoApiType *cxoApiTypeRowid = NULL;
97 cxoApiType *cxoApiTypeString = NULL;
98
5099 cxoFuture *cxoFutureObj = NULL;
51100 dpiContext *cxoDpiContext = NULL;
52101 dpiVersionInfo cxoClientVersionInfo;
102
103
104 //-----------------------------------------------------------------------------
105 // cxoModule_addApiType()
106 // Create a Python Database API type and add it to the module.
107 //-----------------------------------------------------------------------------
108 static int cxoModule_addApiType(PyObject *module, const char *name,
109 cxoTransformNum defaultTransformNum, cxoApiType **apiType)
110 {
111 cxoApiType *tempApiType;
112
113 tempApiType =
114 (cxoApiType*) cxoPyTypeApiType.tp_alloc(&cxoPyTypeApiType, 0);
115 if (!tempApiType)
116 return -1;
117 tempApiType->name = name;
118 tempApiType->defaultTransformNum = defaultTransformNum;
119 tempApiType->dbTypes = PyList_New(0);
120 if (!tempApiType->dbTypes) {
121 Py_DECREF(tempApiType);
122 return -1;
123 }
124 if (PyModule_AddObject(module, name, (PyObject*) tempApiType) < 0) {
125 Py_DECREF(tempApiType);
126 return -1;
127 }
128 *apiType = tempApiType;
129 return 0;
130 }
131
132
133 //-----------------------------------------------------------------------------
134 // cxoModule_addDbType()
135 // Create a database type and add it to the module.
136 //-----------------------------------------------------------------------------
137 static int cxoModule_addDbType(PyObject *module, uint32_t num,
138 const char *name, cxoTransformNum defaultTransformNum,
139 cxoDbType **dbType)
140 {
141 cxoDbType *tempDbType;
142
143 tempDbType = (cxoDbType*) cxoPyTypeDbType.tp_alloc(&cxoPyTypeDbType, 0);
144 if (!tempDbType)
145 return -1;
146 tempDbType->num = num;
147 tempDbType->name = name;
148 tempDbType->defaultTransformNum = defaultTransformNum;
149 if (PyModule_AddObject(module, name, (PyObject*) tempDbType) < 0) {
150 Py_DECREF(tempDbType);
151 return -1;
152 }
153 *dbType = tempDbType;
154 return 0;
155 }
156
53157
54158 //-----------------------------------------------------------------------------
55159 // cxoModule_setException()
75179 static PyObject* cxoModule_makeDSN(PyObject* self, PyObject* args,
76180 PyObject* keywordArgs)
77181 {
78 static unsigned int numConnectDataArgs = 5;
182 static const unsigned int numConnectDataArgs = 5;
79183 static char *keywordList[] = { "host", "port", "sid", "service_name",
80184 "region", "sharding_key", "super_sharding_key", NULL };
81185 PyObject *result, *connectData, *hostObj, *portObj;
141245 //-----------------------------------------------------------------------------
142246 static PyObject* cxoModule_clientVersion(PyObject* self, PyObject* args)
143247 {
144 if (cxoUtils_initializeDPI() < 0)
248 if (cxoUtils_initializeDPI(NULL) < 0)
145249 return NULL;
146250 return Py_BuildValue("(iiiii)", cxoClientVersionInfo.versionNum,
147251 cxoClientVersionInfo.releaseNum, cxoClientVersionInfo.updateNum,
151255
152256
153257 //-----------------------------------------------------------------------------
258 // cxoModule_initClientLib()
259 // Initialize the client library now, rather than when the first call to
260 // get the Oracle Client library version, create a standalone connection or
261 // session pool is performed.
262 //-----------------------------------------------------------------------------
263 static PyObject* cxoModule_initClientLib(PyObject* self, PyObject* args,
264 PyObject* keywordArgs)
265 {
266 static char *keywordList[] = { "lib_dir", "config_dir", "error_url",
267 "driver_name", NULL };
268 Py_ssize_t libDirSize, configDirSize, errorUrlSize, driverNameSize;
269 dpiContextCreateParams params;
270
271 memset(&params, 0, sizeof(dpiContextCreateParams));
272 libDirSize = configDirSize = errorUrlSize = driverNameSize = 0;
273 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|z#z#z#z#",
274 keywordList, &params.oracleClientLibDir, &libDirSize,
275 &params.oracleClientConfigDir, &configDirSize,
276 &params.loadErrorUrl, &errorUrlSize, &params.defaultDriverName,
277 &driverNameSize))
278 return NULL;
279 if (libDirSize == 0)
280 params.oracleClientLibDir = NULL;
281 if (configDirSize == 0)
282 params.oracleClientConfigDir = NULL;
283 if (errorUrlSize == 0)
284 params.loadErrorUrl = NULL;
285 if (driverNameSize == 0)
286 params.defaultDriverName = NULL;
287 if (cxoUtils_initializeDPI(&params) < 0)
288 return NULL;
289
290 Py_RETURN_NONE;
291 }
292
293
294 //-----------------------------------------------------------------------------
154295 // cxoModule_time()
155296 // Returns a time value suitable for binding.
156297 //-----------------------------------------------------------------------------
193334
194335
195336 //-----------------------------------------------------------------------------
196 // Declaration of methods supported by this module
337 // Declaration of methods supported by this module
197338 //-----------------------------------------------------------------------------
198339 static PyMethodDef cxoModuleMethods[] = {
199340 { "makedsn", (PyCFunction) cxoModule_makeDSN,
204345 { "TimestampFromTicks", (PyCFunction) cxoModule_timestampFromTicks,
205346 METH_VARARGS },
206347 { "clientversion", (PyCFunction) cxoModule_clientVersion, METH_NOARGS },
348 { "init_oracle_client", (PyCFunction) cxoModule_initClientLib,
349 METH_VARARGS | METH_KEYWORDS },
207350 { NULL }
208351 };
209352
210353
211 #if PY_MAJOR_VERSION >= 3
212 //-----------------------------------------------------------------------------
213 // Declaration of module definition for Python 3.x.
354 //-----------------------------------------------------------------------------
355 // Declaration of module definition
214356 //-----------------------------------------------------------------------------
215357 static struct PyModuleDef cxoModuleDef = {
216358 PyModuleDef_HEAD_INIT,
223365 NULL, // clear
224366 NULL // free
225367 };
226 #endif
227368
228369
229370 //-----------------------------------------------------------------------------
234375 {
235376 PyObject *module;
236377
237 #ifdef WITH_THREAD
238 PyEval_InitThreads();
239 #endif
240
241378 // initialize transforms
242379 if (cxoTransform_init() < 0)
243380 return NULL;
244381
245382 // prepare the types for use by the module
246 CXO_MAKE_TYPE_READY(&cxoPyTypeBfileVar);
247 CXO_MAKE_TYPE_READY(&cxoPyTypeBinaryVar);
248 CXO_MAKE_TYPE_READY(&cxoPyTypeBlobVar);
249 CXO_MAKE_TYPE_READY(&cxoPyTypeBooleanVar);
250 CXO_MAKE_TYPE_READY(&cxoPyTypeClobVar);
383 CXO_MAKE_TYPE_READY(&cxoPyTypeApiType);
251384 CXO_MAKE_TYPE_READY(&cxoPyTypeConnection);
252385 CXO_MAKE_TYPE_READY(&cxoPyTypeCursor);
253 CXO_MAKE_TYPE_READY(&cxoPyTypeCursorVar);
254 CXO_MAKE_TYPE_READY(&cxoPyTypeDateTimeVar);
386 CXO_MAKE_TYPE_READY(&cxoPyTypeDbType);
255387 CXO_MAKE_TYPE_READY(&cxoPyTypeDeqOptions);
256388 CXO_MAKE_TYPE_READY(&cxoPyTypeEnqOptions);
257389 CXO_MAKE_TYPE_READY(&cxoPyTypeError);
258 CXO_MAKE_TYPE_READY(&cxoPyTypeFixedCharVar);
259 CXO_MAKE_TYPE_READY(&cxoPyTypeFixedNcharVar);
260390 CXO_MAKE_TYPE_READY(&cxoPyTypeFuture);
261 CXO_MAKE_TYPE_READY(&cxoPyTypeIntervalVar);
262391 CXO_MAKE_TYPE_READY(&cxoPyTypeLob);
263 CXO_MAKE_TYPE_READY(&cxoPyTypeLongBinaryVar);
264 CXO_MAKE_TYPE_READY(&cxoPyTypeLongStringVar);
265392 CXO_MAKE_TYPE_READY(&cxoPyTypeMsgProps);
266393 CXO_MAKE_TYPE_READY(&cxoPyTypeMessage);
267394 CXO_MAKE_TYPE_READY(&cxoPyTypeMessageQuery);
268395 CXO_MAKE_TYPE_READY(&cxoPyTypeMessageRow);
269396 CXO_MAKE_TYPE_READY(&cxoPyTypeMessageTable);
270 CXO_MAKE_TYPE_READY(&cxoPyTypeNativeFloatVar);
271 CXO_MAKE_TYPE_READY(&cxoPyTypeNativeIntVar);
272 CXO_MAKE_TYPE_READY(&cxoPyTypeNcharVar);
273 CXO_MAKE_TYPE_READY(&cxoPyTypeNclobVar);
274 CXO_MAKE_TYPE_READY(&cxoPyTypeNumberVar);
275397 CXO_MAKE_TYPE_READY(&cxoPyTypeObjectAttr);
276398 CXO_MAKE_TYPE_READY(&cxoPyTypeObject);
277399 CXO_MAKE_TYPE_READY(&cxoPyTypeObjectType);
278 CXO_MAKE_TYPE_READY(&cxoPyTypeObjectVar);
279 CXO_MAKE_TYPE_READY(&cxoPyTypeRowidVar);
400 CXO_MAKE_TYPE_READY(&cxoPyTypeQueue);
280401 CXO_MAKE_TYPE_READY(&cxoPyTypeSessionPool);
281402 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaCollection);
282403 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDatabase);
283404 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDoc);
284405 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDocCursor);
285406 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaOperation);
286 CXO_MAKE_TYPE_READY(&cxoPyTypeStringVar);
287407 CXO_MAKE_TYPE_READY(&cxoPyTypeSubscr);
288 CXO_MAKE_TYPE_READY(&cxoPyTypeTimestampVar);
408 CXO_MAKE_TYPE_READY(&cxoPyTypeVar);
289409
290410 // initialize module and retrieve the dictionary
291 #if PY_MAJOR_VERSION >= 3
292411 module = PyModule_Create(&cxoModuleDef);
293 #else
294 module = Py_InitModule("cx_Oracle", cxoModuleMethods);
295 #endif
296412 if (!module)
297413 return NULL;
298414
299415 // create exception object and add it to the dictionary
300416 if (cxoModule_setException(module, &cxoWarningException,
301 "Warning", CXO_BASE_EXCEPTION) < 0)
417 "Warning", NULL) < 0)
302418 return NULL;
303419 if (cxoModule_setException(module, &cxoErrorException,
304 "Error", CXO_BASE_EXCEPTION) < 0)
420 "Error", NULL) < 0)
305421 return NULL;
306422 if (cxoModule_setException(module, &cxoInterfaceErrorException,
307423 "InterfaceError", cxoErrorException) < 0)
329445 return NULL;
330446
331447 // set up the types that are available
332 #if PY_MAJOR_VERSION >= 3
448 CXO_ADD_TYPE_OBJECT("ApiType", &cxoPyTypeApiType)
333449 CXO_ADD_TYPE_OBJECT("Binary", &PyBytes_Type)
334 #else
335 CXO_ADD_TYPE_OBJECT("Binary", &PyBuffer_Type)
336 #endif
337450 CXO_ADD_TYPE_OBJECT("Connection", &cxoPyTypeConnection)
338451 CXO_ADD_TYPE_OBJECT("Cursor", &cxoPyTypeCursor)
339 CXO_ADD_TYPE_OBJECT("Timestamp", cxoPyTypeDateTime)
340452 CXO_ADD_TYPE_OBJECT("Date", cxoPyTypeDate)
341 CXO_ADD_TYPE_OBJECT("SessionPool", &cxoPyTypeSessionPool)
453 CXO_ADD_TYPE_OBJECT("DbType", &cxoPyTypeDbType)
454 CXO_ADD_TYPE_OBJECT("DeqOptions", &cxoPyTypeDeqOptions)
455 CXO_ADD_TYPE_OBJECT("EnqOptions", &cxoPyTypeEnqOptions)
342456 CXO_ADD_TYPE_OBJECT("_Error", &cxoPyTypeError)
457 CXO_ADD_TYPE_OBJECT("LOB", &cxoPyTypeLob)
458 CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps)
343459 CXO_ADD_TYPE_OBJECT("Object", &cxoPyTypeObject)
344460 CXO_ADD_TYPE_OBJECT("ObjectType", &cxoPyTypeObjectType)
345 CXO_ADD_TYPE_OBJECT("EnqOptions", &cxoPyTypeEnqOptions)
346 CXO_ADD_TYPE_OBJECT("DeqOptions", &cxoPyTypeDeqOptions)
347 CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps)
461 CXO_ADD_TYPE_OBJECT("SessionPool", &cxoPyTypeSessionPool)
348462 CXO_ADD_TYPE_OBJECT("SodaCollection", &cxoPyTypeSodaCollection)
349463 CXO_ADD_TYPE_OBJECT("SodaDatabase", &cxoPyTypeSodaDatabase)
350464 CXO_ADD_TYPE_OBJECT("SodaDoc", &cxoPyTypeSodaDoc)
351465 CXO_ADD_TYPE_OBJECT("SodaDocCursor", &cxoPyTypeSodaDocCursor)
352466 CXO_ADD_TYPE_OBJECT("SodaOperation", &cxoPyTypeSodaOperation)
467 CXO_ADD_TYPE_OBJECT("Timestamp", cxoPyTypeDateTime)
468 CXO_ADD_TYPE_OBJECT("Var", &cxoPyTypeVar)
353469
354470 // the name "connect" is required by the DB API
355471 CXO_ADD_TYPE_OBJECT("connect", &cxoPyTypeConnection)
356472
357 // create the basic data types for setting input sizes
358 CXO_ADD_TYPE_OBJECT("BINARY", &cxoPyTypeBinaryVar)
359 CXO_ADD_TYPE_OBJECT("BFILE", &cxoPyTypeBfileVar)
360 CXO_ADD_TYPE_OBJECT("BLOB", &cxoPyTypeBlobVar)
361 CXO_ADD_TYPE_OBJECT("CLOB", &cxoPyTypeClobVar)
362 CXO_ADD_TYPE_OBJECT("CURSOR", &cxoPyTypeCursorVar)
363 CXO_ADD_TYPE_OBJECT("OBJECT", &cxoPyTypeObjectVar)
364 CXO_ADD_TYPE_OBJECT("DATETIME", &cxoPyTypeDateTimeVar)
365 CXO_ADD_TYPE_OBJECT("FIXED_CHAR", &cxoPyTypeFixedCharVar)
366 CXO_ADD_TYPE_OBJECT("FIXED_NCHAR", &cxoPyTypeFixedNcharVar)
367 CXO_ADD_TYPE_OBJECT("NCHAR", &cxoPyTypeNcharVar)
368 CXO_ADD_TYPE_OBJECT("INTERVAL", &cxoPyTypeIntervalVar)
369 CXO_ADD_TYPE_OBJECT("LOB", &cxoPyTypeLob)
370 CXO_ADD_TYPE_OBJECT("LONG_BINARY", &cxoPyTypeLongBinaryVar)
371 CXO_ADD_TYPE_OBJECT("LONG_STRING", &cxoPyTypeLongStringVar)
372 CXO_ADD_TYPE_OBJECT("NCLOB", &cxoPyTypeNclobVar)
373 CXO_ADD_TYPE_OBJECT("NUMBER", &cxoPyTypeNumberVar)
374 CXO_ADD_TYPE_OBJECT("ROWID", &cxoPyTypeRowidVar)
375 CXO_ADD_TYPE_OBJECT("STRING", &cxoPyTypeStringVar)
376 CXO_ADD_TYPE_OBJECT("TIMESTAMP", &cxoPyTypeTimestampVar)
377 CXO_ADD_TYPE_OBJECT("NATIVE_INT", &cxoPyTypeNativeIntVar)
378 CXO_ADD_TYPE_OBJECT("NATIVE_FLOAT", &cxoPyTypeNativeFloatVar)
379 CXO_ADD_TYPE_OBJECT("BOOLEAN", &cxoPyTypeBooleanVar)
473 // create the database types (preferred names)
474 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BFILE, "DB_TYPE_BFILE",
475 CXO_TRANSFORM_BFILE, &cxoDbTypeBfile)
476 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_DOUBLE, "DB_TYPE_BINARY_DOUBLE",
477 CXO_TRANSFORM_NATIVE_DOUBLE, &cxoDbTypeBinaryDouble)
478 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_FLOAT, "DB_TYPE_BINARY_FLOAT",
479 CXO_TRANSFORM_NATIVE_FLOAT, &cxoDbTypeBinaryFloat)
480 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_INT, "DB_TYPE_BINARY_INTEGER",
481 CXO_TRANSFORM_NATIVE_INT, &cxoDbTypeBinaryInteger)
482 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BLOB, "DB_TYPE_BLOB",
483 CXO_TRANSFORM_BLOB, &cxoDbTypeBlob)
484 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BOOLEAN, "DB_TYPE_BOOLEAN",
485 CXO_TRANSFORM_BOOLEAN, &cxoDbTypeBoolean)
486 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_CHAR, "DB_TYPE_CHAR",
487 CXO_TRANSFORM_FIXED_CHAR, &cxoDbTypeChar)
488 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_CLOB, "DB_TYPE_CLOB",
489 CXO_TRANSFORM_CLOB, &cxoDbTypeClob)
490 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_STMT, "DB_TYPE_CURSOR",
491 CXO_TRANSFORM_CURSOR, &cxoDbTypeCursor)
492 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_DATE, "DB_TYPE_DATE",
493 CXO_TRANSFORM_DATETIME, &cxoDbTypeDate)
494 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_INTERVAL_DS, "DB_TYPE_INTERVAL_DS",
495 CXO_TRANSFORM_TIMEDELTA, &cxoDbTypeIntervalDS)
496 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_INTERVAL_YM, "DB_TYPE_INTERVAL_YM",
497 CXO_TRANSFORM_UNSUPPORTED, &cxoDbTypeIntervalYM)
498 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_JSON, "DB_TYPE_JSON",
499 CXO_TRANSFORM_JSON, &cxoDbTypeJson)
500 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_LONG_VARCHAR, "DB_TYPE_LONG",
501 CXO_TRANSFORM_LONG_STRING, &cxoDbTypeLong)
502 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_LONG_RAW, "DB_TYPE_LONG_RAW",
503 CXO_TRANSFORM_LONG_BINARY, &cxoDbTypeLongRaw)
504 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NCHAR, "DB_TYPE_NCHAR",
505 CXO_TRANSFORM_FIXED_NCHAR, &cxoDbTypeNchar)
506 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NCLOB, "DB_TYPE_NCLOB",
507 CXO_TRANSFORM_NCLOB, &cxoDbTypeNclob)
508 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NUMBER, "DB_TYPE_NUMBER",
509 CXO_TRANSFORM_FLOAT, &cxoDbTypeNumber)
510 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NVARCHAR, "DB_TYPE_NVARCHAR",
511 CXO_TRANSFORM_NSTRING, &cxoDbTypeNvarchar)
512 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_OBJECT, "DB_TYPE_OBJECT",
513 CXO_TRANSFORM_OBJECT, &cxoDbTypeObject)
514 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_RAW, "DB_TYPE_RAW",
515 CXO_TRANSFORM_BINARY, &cxoDbTypeRaw)
516 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_ROWID, "DB_TYPE_ROWID",
517 CXO_TRANSFORM_ROWID, &cxoDbTypeRowid)
518 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP, "DB_TYPE_TIMESTAMP",
519 CXO_TRANSFORM_TIMESTAMP, &cxoDbTypeTimestamp)
520 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP_LTZ, "DB_TYPE_TIMESTAMP_LTZ",
521 CXO_TRANSFORM_TIMESTAMP_LTZ, &cxoDbTypeTimestampLTZ)
522 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP_TZ, "DB_TYPE_TIMESTAMP_TZ",
523 CXO_TRANSFORM_TIMESTAMP_TZ, &cxoDbTypeTimestampTZ)
524 CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_VARCHAR, "DB_TYPE_VARCHAR",
525 CXO_TRANSFORM_STRING, &cxoDbTypeVarchar)
526
527 // create the synonyms for database types (deprecated names)
528 CXO_ADD_TYPE_OBJECT("BFILE", cxoDbTypeBfile)
529 CXO_ADD_TYPE_OBJECT("BLOB", cxoDbTypeBlob)
530 CXO_ADD_TYPE_OBJECT("CLOB", cxoDbTypeClob)
531 CXO_ADD_TYPE_OBJECT("CURSOR", cxoDbTypeCursor)
532 CXO_ADD_TYPE_OBJECT("OBJECT", cxoDbTypeObject)
533 CXO_ADD_TYPE_OBJECT("FIXED_CHAR", cxoDbTypeChar)
534 CXO_ADD_TYPE_OBJECT("FIXED_NCHAR", cxoDbTypeNchar)
535 CXO_ADD_TYPE_OBJECT("NCHAR", cxoDbTypeNvarchar)
536 CXO_ADD_TYPE_OBJECT("INTERVAL", cxoDbTypeIntervalDS)
537 CXO_ADD_TYPE_OBJECT("LONG_BINARY", cxoDbTypeLongRaw)
538 CXO_ADD_TYPE_OBJECT("LONG_STRING", cxoDbTypeLong)
539 CXO_ADD_TYPE_OBJECT("NCLOB", cxoDbTypeNclob)
540 CXO_ADD_TYPE_OBJECT("TIMESTAMP", cxoDbTypeTimestamp)
541 CXO_ADD_TYPE_OBJECT("NATIVE_INT", cxoDbTypeBinaryInteger)
542 CXO_ADD_TYPE_OBJECT("NATIVE_FLOAT", cxoDbTypeBinaryDouble)
543 CXO_ADD_TYPE_OBJECT("BOOLEAN", cxoDbTypeBoolean)
544
545 // create the Python Database API types
546 CXO_ADD_API_TYPE("BINARY", CXO_TRANSFORM_BINARY, &cxoApiTypeBinary)
547 CXO_ADD_API_TYPE("DATETIME", CXO_TRANSFORM_DATETIME, &cxoApiTypeDatetime)
548 CXO_ADD_API_TYPE("NUMBER", CXO_TRANSFORM_FLOAT, &cxoApiTypeNumber)
549 CXO_ADD_API_TYPE("ROWID", CXO_TRANSFORM_ROWID, &cxoApiTypeRowid)
550 CXO_ADD_API_TYPE("STRING", CXO_TRANSFORM_STRING, &cxoApiTypeString)
551
552 // associate the Python Database API types with the database types
553 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeBinary, cxoDbTypeLongRaw)
554 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeBinary, cxoDbTypeRaw)
555 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeDate)
556 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestamp)
557 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestampLTZ)
558 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestampTZ)
559 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryDouble)
560 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryFloat)
561 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryInteger)
562 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeNumber)
563 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeRowid, cxoDbTypeRowid)
564 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeChar)
565 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeLong)
566 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeNchar)
567 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeNvarchar)
568 CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeVarchar)
380569
381570 // create constants required by Python DB API 2.0
382571 if (PyModule_AddStringConstant(module, "apilevel", "2.0") < 0)
405594 return NULL;
406595
407596 // add constants for authorization modes
597 CXO_ADD_INT_CONSTANT("DEFAULT_AUTH", DPI_MODE_AUTH_DEFAULT)
408598 CXO_ADD_INT_CONSTANT("SYSASM", DPI_MODE_AUTH_SYSASM)
409599 CXO_ADD_INT_CONSTANT("SYSBKP", DPI_MODE_AUTH_SYSBKP)
410600 CXO_ADD_INT_CONSTANT("SYSDBA", DPI_MODE_AUTH_SYSDBA)
528718 //-----------------------------------------------------------------------------
529719 // Start routine for the module.
530720 //-----------------------------------------------------------------------------
531 #if PY_MAJOR_VERSION >= 3
532721 PyMODINIT_FUNC PyInit_cx_Oracle(void)
533722 {
534723 return cxoModule_initialize();
535724 }
536 #else
537 void initcx_Oracle(void)
538 {
539 cxoModule_initialize();
540 }
541 #endif
542
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Licensed under BSD license (see LICENSE.txt).
44 //-----------------------------------------------------------------------------
77 // cxoModule.h
88 // Include file for all cx_Oracle source files.
99 //-----------------------------------------------------------------------------
10
11 #define PY_SSIZE_T_CLEAN 1
1012
1113 #include <Python.h>
1214 #include <structmember.h>
1315 #include <time.h>
1416 #include <dpi.h>
15
16 // define integer macros/methods for Python 3.x
17 #ifndef PyInt_Check
18 #define PyInt_Check PyLong_Check
19 #define PyInt_FromLong PyLong_FromLong
20 #define PyInt_AsLong PyLong_AsLong
21 #endif
22
23 // use the bytes methods in cx_Oracle and define them as the equivalent string
24 // type methods as is done in Python 2.6
25 #ifndef PyBytes_Check
26 #define PyBytes_Type PyString_Type
27 #define PyBytes_AS_STRING PyString_AS_STRING
28 #define PyBytes_GET_SIZE PyString_GET_SIZE
29 #define PyBytes_Check PyString_Check
30 #define PyBytes_FromStringAndSize PyString_FromStringAndSize
31 #endif
32
33 // define string/binary types and methods
34 #if PY_MAJOR_VERSION >= 3
35 #define CXO_BASE_EXCEPTION NULL
36 #define cxoPyTypeBinary PyBytes_Type
37 #define cxoPyTypeString PyUnicode_Type
38 #define cxoPyString_fromAscii(str) \
39 PyUnicode_DecodeASCII(str, strlen(str), NULL)
40 #define cxoPyString_fromEncodedString(buffer, numBytes, encoding, errors) \
41 PyUnicode_Decode(buffer, numBytes, encoding, errors)
42 #else
43 #define CXO_BASE_EXCEPTION PyExc_StandardError
44 #define cxoPyTypeBinary PyBuffer_Type
45 #define cxoPyTypeString PyString_Type
46 #define cxoPyString_fromAscii(str) \
47 PyBytes_FromString(str)
48 #define cxoPyString_fromEncodedString(buffer, numBytes, encoding, errors) \
49 PyBytes_FromStringAndSize(buffer, numBytes)
50 #endif
5117
5218 // define macros to get the build version as a string and the driver name
5319 #define xstr(s) str(s)
6228 //-----------------------------------------------------------------------------
6329 // Forward Declarations
6430 //-----------------------------------------------------------------------------
31 typedef struct cxoApiType cxoApiType;
6532 typedef struct cxoBuffer cxoBuffer;
66 typedef struct cxoError cxoError;
6733 typedef struct cxoConnection cxoConnection;
6834 typedef struct cxoCursor cxoCursor;
35 typedef struct cxoDbType cxoDbType;
6936 typedef struct cxoDeqOptions cxoDeqOptions;
7037 typedef struct cxoEnqOptions cxoEnqOptions;
38 typedef struct cxoError cxoError;
7139 typedef struct cxoFuture cxoFuture;
40 typedef struct cxoJsonBuffer cxoJsonBuffer;
7241 typedef struct cxoLob cxoLob;
7342 typedef struct cxoMessage cxoMessage;
7443 typedef struct cxoMessageQuery cxoMessageQuery;
7847 typedef struct cxoObject cxoObject;
7948 typedef struct cxoObjectAttr cxoObjectAttr;
8049 typedef struct cxoObjectType cxoObjectType;
50 typedef struct cxoQueue cxoQueue;
8151 typedef struct cxoSessionPool cxoSessionPool;
8252 typedef struct cxoSodaCollection cxoSodaCollection;
8353 typedef struct cxoSodaDatabase cxoSodaDatabase;
8656 typedef struct cxoSodaOperation cxoSodaOperation;
8757 typedef struct cxoSubscr cxoSubscr;
8858 typedef struct cxoVar cxoVar;
89 typedef struct cxoVarType cxoVarType;
9059
9160
9261 //-----------------------------------------------------------------------------
10675 extern PyObject *cxoNotSupportedErrorException;
10776
10877 // type objects
109 extern PyTypeObject cxoPyTypeBfileVar;
110 extern PyTypeObject cxoPyTypeBinaryVar;
111 extern PyTypeObject cxoPyTypeBlobVar;
112 extern PyTypeObject cxoPyTypeBooleanVar;
113 extern PyTypeObject cxoPyTypeClobVar;
78 extern PyTypeObject cxoPyTypeApiType;
11479 extern PyTypeObject cxoPyTypeConnection;
11580 extern PyTypeObject cxoPyTypeCursor;
116 extern PyTypeObject cxoPyTypeCursorVar;
117 extern PyTypeObject cxoPyTypeDateTimeVar;
81 extern PyTypeObject cxoPyTypeDbType;
11882 extern PyTypeObject cxoPyTypeDeqOptions;
11983 extern PyTypeObject cxoPyTypeEnqOptions;
12084 extern PyTypeObject cxoPyTypeError;
121 extern PyTypeObject cxoPyTypeFixedCharVar;
122 extern PyTypeObject cxoPyTypeFixedNcharVar;
12385 extern PyTypeObject cxoPyTypeFuture;
124 extern PyTypeObject cxoPyTypeIntervalVar;
12586 extern PyTypeObject cxoPyTypeLob;
126 extern PyTypeObject cxoPyTypeLongBinaryVar;
127 extern PyTypeObject cxoPyTypeLongStringVar;
12887 extern PyTypeObject cxoPyTypeMsgProps;
12988 extern PyTypeObject cxoPyTypeMessage;
13089 extern PyTypeObject cxoPyTypeMessageQuery;
13190 extern PyTypeObject cxoPyTypeMessageRow;
13291 extern PyTypeObject cxoPyTypeMessageTable;
133 extern PyTypeObject cxoPyTypeNativeFloatVar;
134 extern PyTypeObject cxoPyTypeNativeIntVar;
135 extern PyTypeObject cxoPyTypeNcharVar;
136 extern PyTypeObject cxoPyTypeNclobVar;
137 extern PyTypeObject cxoPyTypeNumberVar;
13892 extern PyTypeObject cxoPyTypeObject;
13993 extern PyTypeObject cxoPyTypeObjectAttr;
14094 extern PyTypeObject cxoPyTypeObjectType;
141 extern PyTypeObject cxoPyTypeObjectVar;
142 extern PyTypeObject cxoPyTypeRowidVar;
95 extern PyTypeObject cxoPyTypeQueue;
14396 extern PyTypeObject cxoPyTypeSessionPool;
14497 extern PyTypeObject cxoPyTypeSodaCollection;
14598 extern PyTypeObject cxoPyTypeSodaDatabase;
14699 extern PyTypeObject cxoPyTypeSodaDoc;
147100 extern PyTypeObject cxoPyTypeSodaDocCursor;
148101 extern PyTypeObject cxoPyTypeSodaOperation;
149 extern PyTypeObject cxoPyTypeStringVar;
150102 extern PyTypeObject cxoPyTypeSubscr;
151 extern PyTypeObject cxoPyTypeTimestampVar;
103 extern PyTypeObject cxoPyTypeVar;
152104
153105 // datetime types
154106 extern PyTypeObject *cxoPyTypeDate;
155107 extern PyTypeObject *cxoPyTypeDateTime;
156108
109 // database types
110 extern cxoDbType *cxoDbTypeBfile;
111 extern cxoDbType *cxoDbTypeBinaryDouble;
112 extern cxoDbType *cxoDbTypeBinaryFloat;
113 extern cxoDbType *cxoDbTypeBinaryInteger;
114 extern cxoDbType *cxoDbTypeBlob;
115 extern cxoDbType *cxoDbTypeBoolean;
116 extern cxoDbType *cxoDbTypeChar;
117 extern cxoDbType *cxoDbTypeClob;
118 extern cxoDbType *cxoDbTypeCursor;
119 extern cxoDbType *cxoDbTypeDate;
120 extern cxoDbType *cxoDbTypeIntervalDS;
121 extern cxoDbType *cxoDbTypeIntervalYM;
122 extern cxoDbType *cxoDbTypeJson;
123 extern cxoDbType *cxoDbTypeLong;
124 extern cxoDbType *cxoDbTypeLongRaw;
125 extern cxoDbType *cxoDbTypeNchar;
126 extern cxoDbType *cxoDbTypeNclob;
127 extern cxoDbType *cxoDbTypeNumber;
128 extern cxoDbType *cxoDbTypeNvarchar;
129 extern cxoDbType *cxoDbTypeObject;
130 extern cxoDbType *cxoDbTypeRaw;
131 extern cxoDbType *cxoDbTypeRowid;
132 extern cxoDbType *cxoDbTypeTimestamp;
133 extern cxoDbType *cxoDbTypeTimestampLTZ;
134 extern cxoDbType *cxoDbTypeTimestampTZ;
135 extern cxoDbType *cxoDbTypeVarchar;
136
137 // database API types
138 extern cxoApiType *cxoApiTypeBinary;
139 extern cxoApiType *cxoApiTypeDatetime;
140 extern cxoApiType *cxoApiTypeNumber;
141 extern cxoApiType *cxoApiTypeRowid;
142 extern cxoApiType *cxoApiTypeString;
143
157144 // JSON dump and load functions for use with SODA
158145 extern PyObject *cxoJsonDumpFunction;
159146 extern PyObject *cxoJsonLoadFunction;
167154
168155
169156 //-----------------------------------------------------------------------------
170 // Transforms
157 // Enumerations
171158 //-----------------------------------------------------------------------------
172159 typedef enum {
173160 CXO_TRANSFORM_NONE = 0,
197184 CXO_TRANSFORM_TIMEDELTA,
198185 CXO_TRANSFORM_TIMESTAMP,
199186 CXO_TRANSFORM_TIMESTAMP_LTZ,
187 CXO_TRANSFORM_TIMESTAMP_TZ,
188 CXO_TRANSFORM_JSON,
200189 CXO_TRANSFORM_UNSUPPORTED
201190 } cxoTransformNum;
202191
192 typedef enum {
193 CXO_OCI_ATTR_TYPE_STRING = 1,
194 CXO_OCI_ATTR_TYPE_BOOLEAN = 2,
195 CXO_OCI_ATTR_TYPE_UINT8 = 8,
196 CXO_OCI_ATTR_TYPE_UINT16 = 16,
197 CXO_OCI_ATTR_TYPE_UINT32 = 32,
198 CXO_OCI_ATTR_TYPE_UINT64 = 64
199 } cxoOciAttrType;
200
203201
204202 //-----------------------------------------------------------------------------
205203 // Structures
206204 //-----------------------------------------------------------------------------
205 struct cxoApiType {
206 PyObject_HEAD
207 const char *name;
208 PyObject *dbTypes;
209 cxoTransformNum defaultTransformNum;
210 };
211
207212 struct cxoBuffer {
208213 const char *ptr;
209214 uint32_t numCharacters;
249254 uint32_t arraySize;
250255 uint32_t bindArraySize;
251256 uint32_t fetchArraySize;
257 uint32_t prefetchRows;
252258 int setInputSizes;
253259 uint64_t rowCount;
254260 uint32_t fetchBufferRowIndex;
255261 uint32_t numRowsInFetchBuffer;
256262 int moreRowsToFetch;
257 int isScrollable;
263 char isScrollable;
258264 int fixupRefCursor;
259265 int isOpen;
260266 };
261267
268 struct cxoDbType {
269 PyObject_HEAD
270 uint32_t num;
271 const char *name;
272 cxoTransformNum defaultTransformNum;
273 };
274
262275 struct cxoDeqOptions {
263276 PyObject_HEAD
264277 dpiDeqOptions *handle;
275288 PyObject_HEAD
276289 };
277290
291 struct cxoJsonBuffer {
292 dpiJsonNode topNode;
293 dpiDataBuffer topNodeBuffer;
294 uint32_t allocatedBuffers;
295 uint32_t numBuffers;
296 cxoBuffer *buffers;
297 };
298
278299 struct cxoLob {
279300 PyObject_HEAD
280301 cxoConnection *connection;
281 dpiOracleTypeNum oracleTypeNum;
302 cxoDbType *dbType;
282303 dpiLob *handle;
283304 };
284305
318339 struct cxoMsgProps {
319340 PyObject_HEAD
320341 dpiMsgProps *handle;
342 PyObject *payload;
321343 const char *encoding;
322344 };
323345
333355 dpiObjectAttr *handle;
334356 dpiOracleTypeNum oracleTypeNum;
335357 cxoTransformNum transformNum;
336 cxoObjectType *type;
358 cxoObjectType *objectType;
359 cxoDbType *dbType;
337360 };
338361
339362 struct cxoObjectType {
346369 cxoConnection *connection;
347370 dpiOracleTypeNum elementOracleTypeNum;
348371 cxoTransformNum elementTransformNum;
349 PyObject *elementType;
372 cxoObjectType *elementObjectType;
373 cxoDbType *elementDbType;
350374 char isCollection;
375 };
376
377 struct cxoQueue {
378 PyObject_HEAD
379 cxoConnection *conn;
380 dpiQueue *handle;
381 PyObject *name;
382 PyObject *deqOptions;
383 PyObject *enqOptions;
384 cxoObjectType *payloadType;
351385 };
352386
353387 struct cxoSessionPool {
438472 int isArray;
439473 int isValueSet;
440474 int getReturnedData;
441 cxoVarType *type;
442 };
443
444 struct cxoVarType {
445475 cxoTransformNum transformNum;
446 PyTypeObject *pythonType;
447 uint32_t size;
476 dpiNativeTypeNum nativeTypeNum;
477 cxoDbType *dbType;
448478 };
449479
450480
461491 int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters,
462492 unsigned numElements, unsigned arrayPos, int deferTypeAssignment);
463493
464 cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection);
465
466 cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection);
494 cxoDbType *cxoDbType_fromDataTypeInfo(dpiDataTypeInfo *info);
495 cxoDbType *cxoDbType_fromTransformNum(cxoTransformNum transformNum);
496
497 cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection,
498 dpiDeqOptions *handle);
499
500 cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection,
501 dpiEnqOptions *handle);
467502
468503 cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo);
469504 int cxoError_raiseAndReturnInt(void);
472507 PyObject *cxoError_raiseFromString(PyObject *exceptionType,
473508 const char *message);
474509
475 PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
510 void cxoJsonBuffer_free(cxoJsonBuffer *buf);
511 int cxoJsonBuffer_fromObject(cxoJsonBuffer *buf, PyObject *obj);
512
513 PyObject *cxoLob_new(cxoConnection *connection, cxoDbType *dbType,
476514 dpiLob *handle);
477515
478 cxoMsgProps *cxoMsgProps_new(cxoConnection*);
516 cxoMsgProps *cxoMsgProps_new(cxoConnection*, dpiMsgProps *handle);
479517
480518 int cxoObject_internalExtend(cxoObject *obj, PyObject *sequence);
481519 PyObject *cxoObject_new(cxoObjectType *objectType, dpiObject *handle);
488526 cxoObjectType *cxoObjectType_newByName(cxoConnection *connection,
489527 PyObject *name);
490528
529 cxoQueue *cxoQueue_new(cxoConnection *conn, dpiQueue *handle);
530
491531 cxoSodaCollection *cxoSodaCollection_new(cxoSodaDatabase *db,
492532 dpiSodaColl *handle);
493533
503543 void cxoSubscr_callback(cxoSubscr *subscr, dpiSubscrMessage *message);
504544
505545 PyObject *cxoTransform_dateFromTicks(PyObject *args);
506 int cxoTransform_fromPython(cxoTransformNum transformNum, PyObject *pyValue,
546 int cxoTransform_fromPython(cxoTransformNum transformNum,
547 dpiNativeTypeNum *nativeTypeNum, PyObject *pyValue,
507548 dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding,
508549 const char *nencoding, cxoVar *var, uint32_t arrayPos);
550 uint32_t cxoTransform_getDefaultSize(cxoTransformNum transformNum);
509551 cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info);
510 cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type);
511 cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql);
552 cxoTransformNum cxoTransform_getNumFromPythonValue(PyObject *value,
553 int plsql);
554 int cxoTransform_getNumFromType(PyObject *type, cxoTransformNum *transformNum,
555 cxoObjectType **objType);
556 int cxoTransform_getNumFromValue(PyObject *value, int *isArray,
557 Py_ssize_t *size, Py_ssize_t *numElements, int plsql,
558 cxoTransformNum *transformNum);
512559 void cxoTransform_getTypeInfo(cxoTransformNum transformNum,
513560 dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum);
514561 int cxoTransform_init(void);
517564 cxoConnection *connection, cxoObjectType *objType,
518565 dpiDataBuffer *dbValue, const char *encodingErrors);
519566
567 PyObject *cxoUtils_convertOciAttrToPythonValue(unsigned attrType,
568 dpiDataBuffer *value, uint32_t valueLength, const char *encoding);
569 int cxoUtils_convertPythonValueToOciAttr(PyObject *value, unsigned attrType,
570 cxoBuffer *buffer, dpiDataBuffer *ociBuffer, void **ociValue,
571 uint32_t *ociValueLength, const char *encoding);
520572 PyObject *cxoUtils_formatString(const char *format, PyObject *args);
521573 const char *cxoUtils_getAdjustedEncoding(const char *encoding);
522574 int cxoUtils_getBooleanValue(PyObject *obj, int defaultValue, int *value);
523575 int cxoUtils_getModuleAndName(PyTypeObject *type, PyObject **module,
524576 PyObject **name);
525 int cxoUtils_initializeDPI(void);
577 int cxoUtils_initializeDPI(dpiContextCreateParams *params);
526578 int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer);
527579 int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg,
528 cxoSodaDoc **doc);
529
530 cxoVarType *cxoVarType_fromDataTypeInfo(dpiDataTypeInfo *info);
531 cxoVarType *cxoVarType_fromPythonType(PyObject *type, cxoObjectType **objType);
532 cxoVarType *cxoVarType_fromPythonValue(PyObject *value, int *isArray,
533 Py_ssize_t *size, Py_ssize_t *numElements, int plsql);
580 dpiSodaDoc **handle);
534581
535582 int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos);
536583 int cxoVar_check(PyObject *object);
537584 PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos);
538585 PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos);
539 cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
540 Py_ssize_t size, int isArray, cxoObjectType *objType);
586 cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements,
587 cxoTransformNum transformNum, Py_ssize_t size, int isArray,
588 cxoObjectType *objType);
541589 cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
542590 uint32_t numElements);
543591 cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
544592 Py_ssize_t numElements);
545593 int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value);
546
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // Declaration of methods used for message properties
18 //-----------------------------------------------------------------------------
19 static void cxoMsgProps_free(cxoMsgProps*);
20 static PyObject *cxoMsgProps_getNumAttempts(cxoMsgProps*, void*);
21 static PyObject *cxoMsgProps_getCorrelation(cxoMsgProps*, void*);
22 static PyObject *cxoMsgProps_getDelay(cxoMsgProps*, void*);
23 static PyObject *cxoMsgProps_getDeliveryMode(cxoMsgProps*, void*);
24 static PyObject *cxoMsgProps_getEnqTime(cxoMsgProps*, void*);
25 static PyObject *cxoMsgProps_getExceptionQ(cxoMsgProps*, void*);
26 static PyObject *cxoMsgProps_getExpiration(cxoMsgProps*, void*);
27 static PyObject *cxoMsgProps_getOriginalMsgId(cxoMsgProps*, void*);
28 static PyObject *cxoMsgProps_getPriority(cxoMsgProps*, void*);
29 static PyObject *cxoMsgProps_getState(cxoMsgProps*, void*);
30 static int cxoMsgProps_setCorrelation(cxoMsgProps*, PyObject*, void*);
31 static int cxoMsgProps_setDelay(cxoMsgProps*, PyObject*, void*);
32 static int cxoMsgProps_setExceptionQ(cxoMsgProps*, PyObject*, void*);
33 static int cxoMsgProps_setExpiration(cxoMsgProps*, PyObject*, void*);
34 static int cxoMsgProps_setOriginalMsgId(cxoMsgProps*, PyObject*, void*);
35 static int cxoMsgProps_setPriority(cxoMsgProps*, PyObject*, void*);
36
37
38 //-----------------------------------------------------------------------------
39 // declaration of calculated members for Python type "MessageProperties"
40 //-----------------------------------------------------------------------------
41 static PyGetSetDef cxoMsgPropsCalcMembers[] = {
17 // cxoMsgProps_new()
18 // Create a new message properties object.
19 //-----------------------------------------------------------------------------
20 cxoMsgProps *cxoMsgProps_new(cxoConnection *connection, dpiMsgProps *handle)
21 {
22 cxoMsgProps *props;
23
24 props = (cxoMsgProps*) cxoPyTypeMsgProps.tp_alloc(&cxoPyTypeMsgProps, 0);
25 if (!props) {
26 if (handle)
27 dpiMsgProps_release(handle);
28 return NULL;
29 }
30 if (!handle && dpiConn_newMsgProps(connection->handle, &handle) < 0) {
31 Py_DECREF(props);
32 cxoError_raiseAndReturnNull();
33 return NULL;
34 }
35 props->handle = handle;
36 props->encoding = connection->encodingInfo.encoding;
37
38 return props;
39 }
40
41
42 //-----------------------------------------------------------------------------
43 // cxoMsgProps_free()
44 // Free the memory associated with the message properties object.
45 //-----------------------------------------------------------------------------
46 static void cxoMsgProps_free(cxoMsgProps *props)
47 {
48 if (props->handle) {
49 dpiMsgProps_release(props->handle);
50 props->handle = NULL;
51 }
52 Py_CLEAR(props->payload);
53 Py_TYPE(props)->tp_free((PyObject*) props);
54 }
55
56
57 //-----------------------------------------------------------------------------
58 // cxoMsgProps_getAttrInt32()
59 // Get the value of the attribute as a 32-bit integer.
60 //-----------------------------------------------------------------------------
61 static PyObject *cxoMsgProps_getAttrInt32(cxoMsgProps *props,
62 int (*func)(dpiMsgProps *props, int32_t *value))
63 {
64 int32_t value;
65
66 if ((*func)(props->handle, &value) < 0)
67 return cxoError_raiseAndReturnNull();
68 return PyLong_FromLong(value);
69 }
70
71
72 //-----------------------------------------------------------------------------
73 // cxoMsgProps_setAttrInt32()
74 // Set the value of the attribute as a 32-bit integer.
75 //-----------------------------------------------------------------------------
76 static int cxoMsgProps_setAttrInt32(cxoMsgProps *props, PyObject *valueObj,
77 int (*func)(dpiMsgProps *props, int32_t value))
78 {
79 int32_t value;
80
81 value = PyLong_AsLong(valueObj);
82 if (PyErr_Occurred())
83 return -1;
84 if ((*func)(props->handle, value) < 0)
85 return cxoError_raiseAndReturnInt();
86 return 0;
87 }
88
89
90 //-----------------------------------------------------------------------------
91 // cxoMsgProps_getNumAttempts()
92 // Get the value of the attempts property.
93 //-----------------------------------------------------------------------------
94 static PyObject *cxoMsgProps_getNumAttempts(cxoMsgProps *props, void *unused)
95 {
96 return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getNumAttempts);
97 }
98
99
100 //-----------------------------------------------------------------------------
101 // cxoMsgProps_getCorrelation()
102 // Get the value of the correlation property.
103 //-----------------------------------------------------------------------------
104 static PyObject *cxoMsgProps_getCorrelation(cxoMsgProps *props, void *unused)
105 {
106 uint32_t valueLength;
107 const char *value;
108
109 if (dpiMsgProps_getCorrelation(props->handle, &value, &valueLength) < 0)
110 return cxoError_raiseAndReturnNull();
111 if (!value)
112 Py_RETURN_NONE;
113 return PyUnicode_Decode(value, valueLength, props->encoding, NULL);
114 }
115
116
117 //-----------------------------------------------------------------------------
118 // cxoMsgProps_getDelay()
119 // Get the value of the delay property.
120 //-----------------------------------------------------------------------------
121 static PyObject *cxoMsgProps_getDelay(cxoMsgProps *props, void *unused)
122 {
123 return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getDelay);
124 }
125
126
127 //-----------------------------------------------------------------------------
128 // cxoMsgProps_getDeliveryMode()
129 // Get the value of the delivery mode property.
130 //-----------------------------------------------------------------------------
131 static PyObject *cxoMsgProps_getDeliveryMode(cxoMsgProps *props, void *unused)
132 {
133 dpiMessageDeliveryMode value;
134
135 if (dpiMsgProps_getDeliveryMode(props->handle, &value) < 0)
136 return cxoError_raiseAndReturnNull();
137 return PyLong_FromLong(value);
138 }
139
140
141 //-----------------------------------------------------------------------------
142 // cxoMsgProps_getEnqTime()
143 // Get the value of the enqueue time property.
144 //-----------------------------------------------------------------------------
145 static PyObject *cxoMsgProps_getEnqTime(cxoMsgProps *props, void *unused)
146 {
147 dpiDataBuffer buffer;
148
149 if (dpiMsgProps_getEnqTime(props->handle, &buffer.asTimestamp) < 0)
150 return cxoError_raiseAndReturnNull();
151 return cxoTransform_toPython(CXO_TRANSFORM_DATETIME, NULL, NULL, &buffer,
152 NULL);
153 }
154
155
156 //-----------------------------------------------------------------------------
157 // cxoMsgProps_getExceptionQ()
158 // Get the value of the exception queue property.
159 //-----------------------------------------------------------------------------
160 static PyObject *cxoMsgProps_getExceptionQ(cxoMsgProps *props, void *unused)
161 {
162 uint32_t valueLength;
163 const char *value;
164
165 if (dpiMsgProps_getExceptionQ(props->handle, &value, &valueLength) < 0)
166 return cxoError_raiseAndReturnNull();
167 if (!value)
168 Py_RETURN_NONE;
169 return PyUnicode_Decode(value, valueLength, props->encoding, NULL);
170 }
171
172
173 //-----------------------------------------------------------------------------
174 // cxoMsgProps_getExpiration()
175 // Get the value of the expiration property.
176 //-----------------------------------------------------------------------------
177 static PyObject *cxoMsgProps_getExpiration(cxoMsgProps *props, void *unused)
178 {
179 return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getExpiration);
180 }
181
182
183 //-----------------------------------------------------------------------------
184 // cxoMsgProps_getOriginalMsgId()
185 // Get the value of the expiration property.
186 //-----------------------------------------------------------------------------
187 static PyObject *cxoMsgProps_getOriginalMsgId(cxoMsgProps *props, void *unused)
188 {
189 uint32_t valueLength;
190 const char *value;
191
192 if (dpiMsgProps_getOriginalMsgId(props->handle, &value, &valueLength) < 0)
193 return cxoError_raiseAndReturnNull();
194 if (!value)
195 Py_RETURN_NONE;
196 return PyBytes_FromStringAndSize(value, valueLength);
197 }
198
199
200 //-----------------------------------------------------------------------------
201 // cxoMsgProps_getPriority()
202 // Get the value of the priority property.
203 //-----------------------------------------------------------------------------
204 static PyObject *cxoMsgProps_getPriority(cxoMsgProps *props, void *unused)
205 {
206 return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getPriority);
207 }
208
209
210 //-----------------------------------------------------------------------------
211 // cxoMsgProps_getState()
212 // Get the value of the state property.
213 //-----------------------------------------------------------------------------
214 static PyObject *cxoMsgProps_getState(cxoMsgProps *props, void *unused)
215 {
216 dpiMessageState value;
217
218 if (dpiMsgProps_getState(props->handle, &value) < 0)
219 return cxoError_raiseAndReturnNull();
220 return PyLong_FromLong(value);
221 }
222
223
224 //-----------------------------------------------------------------------------
225 // cxoMsgProps_setCorrelation()
226 // Set the value of the correlation property.
227 //-----------------------------------------------------------------------------
228 static int cxoMsgProps_setCorrelation(cxoMsgProps *props, PyObject *valueObj,
229 void *unused)
230 {
231 cxoBuffer buffer;
232 int status;
233
234 if (cxoBuffer_fromObject(&buffer, valueObj, props->encoding))
235 return -1;
236 status = dpiMsgProps_setCorrelation(props->handle, buffer.ptr,
237 buffer.size);
238 cxoBuffer_clear(&buffer);
239 if (status < 0)
240 return cxoError_raiseAndReturnInt();
241 return 0;
242 }
243
244
245 //-----------------------------------------------------------------------------
246 // cxoMsgProps_setDelay()
247 // Set the value of the delay property.
248 //-----------------------------------------------------------------------------
249 static int cxoMsgProps_setDelay(cxoMsgProps *props, PyObject *valueObj,
250 void *unused)
251 {
252 return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setDelay);
253 }
254
255
256 //-----------------------------------------------------------------------------
257 // cxoMsgProps_setExceptionQ()
258 // Set the value of the exception queue property.
259 //-----------------------------------------------------------------------------
260 static int cxoMsgProps_setExceptionQ(cxoMsgProps *props, PyObject *valueObj,
261 void *unused)
262 {
263 cxoBuffer buffer;
264 int status;
265
266 if (cxoBuffer_fromObject(&buffer, valueObj, props->encoding))
267 return -1;
268 status = dpiMsgProps_setExceptionQ(props->handle, buffer.ptr, buffer.size);
269 cxoBuffer_clear(&buffer);
270 if (status < 0)
271 return cxoError_raiseAndReturnInt();
272 return 0;
273 }
274
275
276 //-----------------------------------------------------------------------------
277 // cxoMsgProps_setExpiration()
278 // Set the value of the expiration property.
279 //-----------------------------------------------------------------------------
280 static int cxoMsgProps_setExpiration(cxoMsgProps *props, PyObject *valueObj,
281 void *unused)
282 {
283 return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setExpiration);
284 }
285
286
287 //-----------------------------------------------------------------------------
288 // cxoMsgProps_setOriginalMsgId()
289 // Set the value of the original message id property.
290 //-----------------------------------------------------------------------------
291 static int cxoMsgProps_setOriginalMsgId(cxoMsgProps *props, PyObject *valueObj,
292 void *unused)
293 {
294 Py_ssize_t valueLength;
295 char *value;
296
297 if (PyBytes_AsStringAndSize(valueObj, &value, &valueLength) < 0)
298 return -1;
299 if (dpiMsgProps_setOriginalMsgId(props->handle, value,
300 (uint32_t) valueLength) < 0)
301 return cxoError_raiseAndReturnInt();
302 return 0;
303 }
304
305
306 //-----------------------------------------------------------------------------
307 // cxoMsgProps_setPriority()
308 // Set the value of the expiration property.
309 //-----------------------------------------------------------------------------
310 static int cxoMsgProps_setPriority(cxoMsgProps *props, PyObject *valueObj,
311 void *unused)
312 {
313 return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setPriority);
314 }
315
316
317 //-----------------------------------------------------------------------------
318 // declaration of members
319 //-----------------------------------------------------------------------------
320 static PyMemberDef cxoMembers[] = {
321 { "payload", T_OBJECT, offsetof(cxoMsgProps, payload), 0 },
322 { NULL }
323 };
324
325
326 //-----------------------------------------------------------------------------
327 // declaration of calculated members
328 //-----------------------------------------------------------------------------
329 static PyGetSetDef cxoCalcMembers[] = {
42330 { "attempts", (getter) cxoMsgProps_getNumAttempts, 0, 0, 0 },
43331 { "correlation", (getter) cxoMsgProps_getCorrelation,
44332 (setter) cxoMsgProps_setCorrelation, 0, 0 },
64352 //-----------------------------------------------------------------------------
65353 PyTypeObject cxoPyTypeMsgProps = {
66354 PyVarObject_HEAD_INIT(NULL, 0)
67 "cx_Oracle.MessageProperties", // tp_name
68 sizeof(cxoMsgProps), // tp_basicsize
69 0, // tp_itemsize
70 (destructor) cxoMsgProps_free, // tp_dealloc
71 0, // tp_print
72 0, // tp_getattr
73 0, // tp_setattr
74 0, // tp_compare
75 0, // tp_repr
76 0, // tp_as_number
77 0, // tp_as_sequence
78 0, // tp_as_mapping
79 0, // tp_hash
80 0, // tp_call
81 0, // tp_str
82 0, // tp_getattro
83 0, // tp_setattro
84 0, // tp_as_buffer
85 Py_TPFLAGS_DEFAULT, // tp_flags
86 0, // tp_doc
87 0, // tp_traverse
88 0, // tp_clear
89 0, // tp_richcompare
90 0, // tp_weaklistoffset
91 0, // tp_iter
92 0, // tp_iternext
93 0, // tp_methods
94 0, // tp_members
95 cxoMsgPropsCalcMembers, // tp_getset
96 0, // tp_base
97 0, // tp_dict
98 0, // tp_descr_get
99 0, // tp_descr_set
100 0, // tp_dictoffset
101 0, // tp_init
102 0, // tp_alloc
103 0, // tp_new
104 0, // tp_free
105 0, // tp_is_gc
106 0 // tp_bases
355 .tp_name = "cx_Oracle.MessageProperties",
356 .tp_basicsize = sizeof(cxoMsgProps),
357 .tp_dealloc = (destructor) cxoMsgProps_free,
358 .tp_flags = Py_TPFLAGS_DEFAULT,
359 .tp_members = cxoMembers,
360 .tp_getset = cxoCalcMembers
107361 };
108
109
110 //-----------------------------------------------------------------------------
111 // cxoMsgProps_new()
112 // Create a new message properties object.
113 //-----------------------------------------------------------------------------
114 cxoMsgProps *cxoMsgProps_new(cxoConnection *connection)
115 {
116 cxoMsgProps *props;
117
118 props = (cxoMsgProps*) cxoPyTypeMsgProps.tp_alloc(&cxoPyTypeMsgProps, 0);
119 if (!props)
120 return NULL;
121 if (dpiConn_newMsgProps(connection->handle, &props->handle) < 0) {
122 Py_DECREF(props);
123 cxoError_raiseAndReturnNull();
124 return NULL;
125 }
126 props->encoding = connection->encodingInfo.encoding;
127
128 return props;
129 }
130
131
132 //-----------------------------------------------------------------------------
133 // cxoMsgProps_free()
134 // Free the memory associated with the message properties object.
135 //-----------------------------------------------------------------------------
136 static void cxoMsgProps_free(cxoMsgProps *props)
137 {
138 if (props->handle) {
139 dpiMsgProps_release(props->handle);
140 props->handle = NULL;
141 }
142 Py_TYPE(props)->tp_free((PyObject*) props);
143 }
144
145
146 //-----------------------------------------------------------------------------
147 // cxoMsgProps_getAttrInt32()
148 // Get the value of the attribute as a 32-bit integer.
149 //-----------------------------------------------------------------------------
150 static PyObject *cxoMsgProps_getAttrInt32(cxoMsgProps *props,
151 int (*func)(dpiMsgProps *props, int32_t *value))
152 {
153 int32_t value;
154
155 if ((*func)(props->handle, &value) < 0)
156 return cxoError_raiseAndReturnNull();
157 return PyInt_FromLong(value);
158 }
159
160
161 //-----------------------------------------------------------------------------
162 // cxoMsgProps_setAttrInt32()
163 // Set the value of the attribute as a 32-bit integer.
164 //-----------------------------------------------------------------------------
165 static int cxoMsgProps_setAttrInt32(cxoMsgProps *props, PyObject *valueObj,
166 int (*func)(dpiMsgProps *props, int32_t value))
167 {
168 int32_t value;
169
170 value = PyInt_AsLong(valueObj);
171 if (PyErr_Occurred())
172 return -1;
173 if ((*func)(props->handle, value) < 0)
174 return cxoError_raiseAndReturnInt();
175 return 0;
176 }
177
178
179 //-----------------------------------------------------------------------------
180 // cxoMsgProps_getNumAttempts()
181 // Get the value of the attempts property.
182 //-----------------------------------------------------------------------------
183 static PyObject *cxoMsgProps_getNumAttempts(cxoMsgProps *props, void *unused)
184 {
185 return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getNumAttempts);
186 }
187
188
189 //-----------------------------------------------------------------------------
190 // cxoMsgProps_getCorrelation()
191 // Get the value of the correlation property.
192 //-----------------------------------------------------------------------------
193 static PyObject *cxoMsgProps_getCorrelation(cxoMsgProps *props, void *unused)
194 {
195 uint32_t valueLength;
196 const char *value;
197
198 if (dpiMsgProps_getCorrelation(props->handle, &value, &valueLength) < 0)
199 return cxoError_raiseAndReturnNull();
200 if (!value)
201 Py_RETURN_NONE;
202 return cxoPyString_fromEncodedString(value, valueLength, props->encoding,
203 NULL);
204 }
205
206
207 //-----------------------------------------------------------------------------
208 // cxoMsgProps_getDelay()
209 // Get the value of the delay property.
210 //-----------------------------------------------------------------------------
211 static PyObject *cxoMsgProps_getDelay(cxoMsgProps *props, void *unused)
212 {
213 return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getDelay);
214 }
215
216
217 //-----------------------------------------------------------------------------
218 // cxoMsgProps_getDeliveryMode()
219 // Get the value of the delivery mode property.
220 //-----------------------------------------------------------------------------
221 static PyObject *cxoMsgProps_getDeliveryMode(cxoMsgProps *props, void *unused)
222 {
223 dpiMessageDeliveryMode value;
224
225 if (dpiMsgProps_getDeliveryMode(props->handle, &value) < 0)
226 return cxoError_raiseAndReturnNull();
227 return PyInt_FromLong(value);
228 }
229
230
231 //-----------------------------------------------------------------------------
232 // cxoMsgProps_getEnqTime()
233 // Get the value of the enqueue time property.
234 //-----------------------------------------------------------------------------
235 static PyObject *cxoMsgProps_getEnqTime(cxoMsgProps *props, void *unused)
236 {
237 dpiDataBuffer buffer;
238
239 if (dpiMsgProps_getEnqTime(props->handle, &buffer.asTimestamp) < 0)
240 return cxoError_raiseAndReturnNull();
241 return cxoTransform_toPython(CXO_TRANSFORM_DATETIME, NULL, NULL, &buffer,
242 NULL);
243 }
244
245
246 //-----------------------------------------------------------------------------
247 // cxoMsgProps_getExceptionQ()
248 // Get the value of the exception queue property.
249 //-----------------------------------------------------------------------------
250 static PyObject *cxoMsgProps_getExceptionQ(cxoMsgProps *props, void *unused)
251 {
252 uint32_t valueLength;
253 const char *value;
254
255 if (dpiMsgProps_getExceptionQ(props->handle, &value, &valueLength) < 0)
256 return cxoError_raiseAndReturnNull();
257 if (!value)
258 Py_RETURN_NONE;
259 return cxoPyString_fromEncodedString(value, valueLength, props->encoding,
260 NULL);
261 }
262
263
264 //-----------------------------------------------------------------------------
265 // cxoMsgProps_getExpiration()
266 // Get the value of the expiration property.
267 //-----------------------------------------------------------------------------
268 static PyObject *cxoMsgProps_getExpiration(cxoMsgProps *props, void *unused)
269 {
270 return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getExpiration);
271 }
272
273
274 //-----------------------------------------------------------------------------
275 // cxoMsgProps_getOriginalMsgId()
276 // Get the value of the expiration property.
277 //-----------------------------------------------------------------------------
278 static PyObject *cxoMsgProps_getOriginalMsgId(cxoMsgProps *props, void *unused)
279 {
280 uint32_t valueLength;
281 const char *value;
282
283 if (dpiMsgProps_getOriginalMsgId(props->handle, &value, &valueLength) < 0)
284 return cxoError_raiseAndReturnNull();
285 if (!value)
286 Py_RETURN_NONE;
287 return PyBytes_FromStringAndSize(value, valueLength);
288 }
289
290
291 //-----------------------------------------------------------------------------
292 // cxoMsgProps_getPriority()
293 // Get the value of the priority property.
294 //-----------------------------------------------------------------------------
295 static PyObject *cxoMsgProps_getPriority(cxoMsgProps *props, void *unused)
296 {
297 return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getPriority);
298 }
299
300
301 //-----------------------------------------------------------------------------
302 // cxoMsgProps_getState()
303 // Get the value of the state property.
304 //-----------------------------------------------------------------------------
305 static PyObject *cxoMsgProps_getState(cxoMsgProps *props, void *unused)
306 {
307 dpiMessageState value;
308
309 if (dpiMsgProps_getState(props->handle, &value) < 0)
310 return cxoError_raiseAndReturnNull();
311 return PyInt_FromLong(value);
312 }
313
314
315 //-----------------------------------------------------------------------------
316 // cxoMsgProps_setCorrelation()
317 // Set the value of the correlation property.
318 //-----------------------------------------------------------------------------
319 static int cxoMsgProps_setCorrelation(cxoMsgProps *props, PyObject *valueObj,
320 void *unused)
321 {
322 cxoBuffer buffer;
323 int status;
324
325 if (cxoBuffer_fromObject(&buffer, valueObj, props->encoding))
326 return -1;
327 status = dpiMsgProps_setCorrelation(props->handle, buffer.ptr,
328 buffer.size);
329 cxoBuffer_clear(&buffer);
330 if (status < 0)
331 return cxoError_raiseAndReturnInt();
332 return 0;
333 }
334
335
336 //-----------------------------------------------------------------------------
337 // cxoMsgProps_setDelay()
338 // Set the value of the delay property.
339 //-----------------------------------------------------------------------------
340 static int cxoMsgProps_setDelay(cxoMsgProps *props, PyObject *valueObj,
341 void *unused)
342 {
343 return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setDelay);
344 }
345
346
347 //-----------------------------------------------------------------------------
348 // cxoMsgProps_setExceptionQ()
349 // Set the value of the exception queue property.
350 //-----------------------------------------------------------------------------
351 static int cxoMsgProps_setExceptionQ(cxoMsgProps *props, PyObject *valueObj,
352 void *unused)
353 {
354 cxoBuffer buffer;
355 int status;
356
357 if (cxoBuffer_fromObject(&buffer, valueObj, props->encoding))
358 return -1;
359 status = dpiMsgProps_setExceptionQ(props->handle, buffer.ptr, buffer.size);
360 cxoBuffer_clear(&buffer);
361 if (status < 0)
362 return cxoError_raiseAndReturnInt();
363 return 0;
364 }
365
366
367 //-----------------------------------------------------------------------------
368 // cxoMsgProps_setExpiration()
369 // Set the value of the expiration property.
370 //-----------------------------------------------------------------------------
371 static int cxoMsgProps_setExpiration(cxoMsgProps *props, PyObject *valueObj,
372 void *unused)
373 {
374 return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setExpiration);
375 }
376
377
378 //-----------------------------------------------------------------------------
379 // cxoMsgProps_setOriginalMsgId()
380 // Set the value of the original message id property.
381 //-----------------------------------------------------------------------------
382 static int cxoMsgProps_setOriginalMsgId(cxoMsgProps *props, PyObject *valueObj,
383 void *unused)
384 {
385 Py_ssize_t valueLength;
386 char *value;
387
388 if (PyBytes_AsStringAndSize(valueObj, &value, &valueLength) < 0)
389 return -1;
390 if (dpiMsgProps_setOriginalMsgId(props->handle, value,
391 (uint32_t) valueLength) < 0)
392 return cxoError_raiseAndReturnInt();
393 return 0;
394 }
395
396
397 //-----------------------------------------------------------------------------
398 // cxoMsgProps_setPriority()
399 // Set the value of the expiration property.
400 //-----------------------------------------------------------------------------
401 static int cxoMsgProps_setPriority(cxoMsgProps *props, PyObject *valueObj,
402 void *unused)
403 {
404 return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setPriority);
405 }
406
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // functions for the Python type "Object"
18 //-----------------------------------------------------------------------------
19 static void cxoObject_free(cxoObject*);
20 static PyObject *cxoObject_getAttr(cxoObject*, PyObject*);
21 static PyObject *cxoObject_repr(cxoObject*);
22 static int cxoObject_setAttr(cxoObject*, PyObject*, PyObject*);
23 static PyObject *cxoObject_append(cxoObject*, PyObject*);
24 static PyObject *cxoObject_asDict(cxoObject*, PyObject*);
25 static PyObject *cxoObject_asList(cxoObject*, PyObject*);
26 static PyObject *cxoObject_copy(cxoObject*, PyObject*);
27 static PyObject *cxoObject_delete(cxoObject*, PyObject*);
28 static PyObject *cxoObject_exists(cxoObject*, PyObject*);
29 static PyObject *cxoObject_extend(cxoObject*, PyObject*);
30 static PyObject *cxoObject_getElement(cxoObject*, PyObject*);
31 static PyObject *cxoObject_getFirstIndex(cxoObject*, PyObject*);
32 static PyObject *cxoObject_getLastIndex(cxoObject*, PyObject*);
33 static PyObject *cxoObject_getNextIndex(cxoObject*, PyObject*);
34 static PyObject *cxoObject_getPrevIndex(cxoObject*, PyObject*);
35 static PyObject *cxoObject_getSize(cxoObject*, PyObject*);
36 static PyObject *cxoObject_setElement(cxoObject*, PyObject*);
37 static PyObject *cxoObject_trim(cxoObject*, PyObject*);
38
39
40 //-----------------------------------------------------------------------------
41 // declaration of methods for Python type "Object"
17 // cxoObject_new()
18 // Create a new object.
19 //-----------------------------------------------------------------------------
20 PyObject *cxoObject_new(cxoObjectType *objectType, dpiObject *handle)
21 {
22 cxoObject *obj;
23
24 obj = (cxoObject*) cxoPyTypeObject.tp_alloc(&cxoPyTypeObject, 0);
25 if (!obj)
26 return NULL;
27 Py_INCREF(objectType);
28 obj->objectType = objectType;
29 obj->handle = handle;
30 return (PyObject*) obj;
31 }
32
33
34 //-----------------------------------------------------------------------------
35 // cxoObject_free()
36 // Free an object.
37 //-----------------------------------------------------------------------------
38 static void cxoObject_free(cxoObject *obj)
39 {
40 if (obj->handle) {
41 dpiObject_release(obj->handle);
42 obj->handle = NULL;
43 }
44 Py_CLEAR(obj->objectType);
45 Py_TYPE(obj)->tp_free((PyObject*) obj);
46 }
47
48
49 //-----------------------------------------------------------------------------
50 // cxoObject_repr()
51 // Return a string representation of the object.
52 //-----------------------------------------------------------------------------
53 static PyObject *cxoObject_repr(cxoObject *obj)
54 {
55 PyObject *module, *name, *result;
56
57 if (cxoUtils_getModuleAndName(Py_TYPE(obj), &module, &name) < 0)
58 return NULL;
59 result = cxoUtils_formatString("<%s.%s %s.%s at %#x>",
60 Py_BuildValue("(OOOOl)", module, name, obj->objectType->schema,
61 obj->objectType->name, obj));
62 Py_DECREF(module);
63 Py_DECREF(name);
64 return result;
65 }
66
67
68 //-----------------------------------------------------------------------------
69 // cxoObject_convertFromPython()
70 // Convert a Python value to an Oracle value.
71 //-----------------------------------------------------------------------------
72 static int cxoObject_convertFromPython(cxoObject *obj, PyObject *value,
73 cxoTransformNum transformNum, dpiNativeTypeNum *nativeTypeNum,
74 dpiData *data, cxoBuffer *buffer)
75 {
76 dpiOracleTypeNum oracleTypeNum;
77
78 // None is treated as null
79 if (value == Py_None) {
80 data->isNull = 1;
81 return 0;
82 }
83
84 // convert the different Python types
85 cxoTransform_getTypeInfo(transformNum, &oracleTypeNum, nativeTypeNum);
86 if (cxoTransform_fromPython(transformNum, nativeTypeNum, value,
87 &data->value, buffer,
88 obj->objectType->connection->encodingInfo.encoding,
89 obj->objectType->connection->encodingInfo.nencoding, NULL, 0) < 0)
90 return -1;
91 data->isNull = 0;
92 return 0;
93 }
94
95
96 //-----------------------------------------------------------------------------
97 // cxoObject_convertToPython()
98 // Convert an Oracle value to a Python value.
99 //-----------------------------------------------------------------------------
100 static PyObject *cxoObject_convertToPython(cxoObject *obj,
101 cxoTransformNum transformNum, dpiData *data, cxoObjectType *objType)
102 {
103 if (data->isNull)
104 Py_RETURN_NONE;
105 return cxoTransform_toPython(transformNum, obj->objectType->connection,
106 objType, &data->value, NULL);
107 }
108
109
110 //-----------------------------------------------------------------------------
111 // cxoObject_getAttributeValue()
112 // Retrieve an attribute on the object.
113 //-----------------------------------------------------------------------------
114 static PyObject *cxoObject_getAttributeValue(cxoObject *obj,
115 cxoObjectAttr *attribute)
116 {
117 char numberAsStringBuffer[200], message[120];
118 dpiOracleTypeNum oracleTypeNum;
119 dpiNativeTypeNum nativeTypeNum;
120 dpiData data;
121
122 if (attribute->transformNum == CXO_TRANSFORM_UNSUPPORTED) {
123 snprintf(message, sizeof(message), "Oracle type %d not supported.",
124 attribute->oracleTypeNum);
125 return cxoError_raiseFromString(cxoNotSupportedErrorException,
126 message);
127 }
128 cxoTransform_getTypeInfo(attribute->transformNum, &oracleTypeNum,
129 &nativeTypeNum);
130 if (oracleTypeNum == DPI_ORACLE_TYPE_NUMBER &&
131 nativeTypeNum == DPI_NATIVE_TYPE_BYTES) {
132 data.value.asBytes.ptr = numberAsStringBuffer;
133 data.value.asBytes.length = sizeof(numberAsStringBuffer);
134 data.value.asBytes.encoding = NULL;
135 }
136 if (dpiObject_getAttributeValue(obj->handle, attribute->handle,
137 nativeTypeNum, &data) < 0)
138 return cxoError_raiseAndReturnNull();
139 return cxoObject_convertToPython(obj, attribute->transformNum, &data,
140 attribute->objectType);
141 }
142
143
144 //-----------------------------------------------------------------------------
145 // cxoObject_setAttributeValue()
146 // Set an attribute on the object.
147 //-----------------------------------------------------------------------------
148 static int cxoObject_setAttributeValue(cxoObject *obj,
149 cxoObjectAttr *attribute, PyObject *value)
150 {
151 dpiNativeTypeNum nativeTypeNum = 0;
152 cxoBuffer buffer;
153 dpiData data;
154 int status;
155
156 cxoBuffer_init(&buffer);
157 if (cxoObject_convertFromPython(obj, value, attribute->transformNum,
158 &nativeTypeNum, &data, &buffer) < 0)
159 return -1;
160 status = dpiObject_setAttributeValue(obj->handle, attribute->handle,
161 nativeTypeNum, &data);
162 cxoBuffer_clear(&buffer);
163 if (status < 0)
164 return cxoError_raiseAndReturnInt();
165 return 0;
166 }
167
168
169 //-----------------------------------------------------------------------------
170 // cxoObject_getAttr()
171 // Retrieve an attribute on an object.
172 //-----------------------------------------------------------------------------
173 static PyObject *cxoObject_getAttr(cxoObject *obj, PyObject *nameObject)
174 {
175 cxoObjectAttr *attribute;
176
177 attribute = (cxoObjectAttr*)
178 PyDict_GetItem(obj->objectType->attributesByName, nameObject);
179 if (attribute)
180 return cxoObject_getAttributeValue(obj, attribute);
181
182 return PyObject_GenericGetAttr( (PyObject*) obj, nameObject);
183 }
184
185
186 //-----------------------------------------------------------------------------
187 // cxoObject_setAttr()
188 // Set an attribute on an object.
189 //-----------------------------------------------------------------------------
190 static int cxoObject_setAttr(cxoObject *obj, PyObject *nameObject,
191 PyObject *value)
192 {
193 cxoObjectAttr *attribute;
194
195 attribute = (cxoObjectAttr*)
196 PyDict_GetItem(obj->objectType->attributesByName, nameObject);
197 if (attribute)
198 return cxoObject_setAttributeValue(obj, attribute, value);
199
200 return PyObject_GenericSetAttr( (PyObject*) obj, nameObject, value);
201 }
202
203
204 //-----------------------------------------------------------------------------
205 // cxoObject_internalAppend()
206 // Append an item to the collection.
207 //-----------------------------------------------------------------------------
208 static int cxoObject_internalAppend(cxoObject *obj, PyObject *value)
209 {
210 dpiNativeTypeNum nativeTypeNum = 0;
211 cxoBuffer buffer;
212 dpiData data;
213 int status;
214
215 cxoBuffer_init(&buffer);
216 if (cxoObject_convertFromPython(obj, value,
217 obj->objectType->elementTransformNum, &nativeTypeNum, &data,
218 &buffer) < 0)
219 return -1;
220 status = dpiObject_appendElement(obj->handle, nativeTypeNum, &data);
221 cxoBuffer_clear(&buffer);
222 if (status < 0)
223 return cxoError_raiseAndReturnInt();
224 return 0;
225 }
226
227
228 //-----------------------------------------------------------------------------
229 // cxoObject_internalExtend()
230 // Extend the collection by appending each of the items in the sequence.
231 //-----------------------------------------------------------------------------
232 int cxoObject_internalExtend(cxoObject *obj, PyObject *sequence)
233 {
234 PyObject *fastSequence, *element;
235 Py_ssize_t size, i;
236
237 fastSequence = PySequence_Fast(sequence, "expecting sequence");
238 if (!fastSequence)
239 return -1;
240 size = PySequence_Fast_GET_SIZE(fastSequence);
241 for (i = 0; i < size; i++) {
242 element = PySequence_Fast_GET_ITEM(fastSequence, i);
243 if (cxoObject_internalAppend(obj, element) < 0) {
244 Py_DECREF(fastSequence);
245 return -1;
246 }
247 }
248 Py_DECREF(fastSequence);
249
250 return 0;
251 }
252
253
254 //-----------------------------------------------------------------------------
255 // cxoObject_append()
256 // Append an item to the collection.
257 //-----------------------------------------------------------------------------
258 static PyObject *cxoObject_append(cxoObject *obj, PyObject *value)
259 {
260 if (cxoObject_internalAppend(obj, value) < 0)
261 return NULL;
262 Py_RETURN_NONE;
263 }
264
265
266 //-----------------------------------------------------------------------------
267 // cxoObject_internalGetElementByIndex()
268 // Internal method used for getting an element value for a particular index.
269 //-----------------------------------------------------------------------------
270 static PyObject *cxoObject_internalGetElementByIndex(cxoObject *obj,
271 int32_t index)
272 {
273 char numberAsStringBuffer[200], message[120];
274 dpiOracleTypeNum oracleTypeNum;
275 dpiNativeTypeNum nativeTypeNum;
276 dpiData data;
277
278 if (obj->objectType->elementTransformNum == CXO_TRANSFORM_UNSUPPORTED) {
279 snprintf(message, sizeof(message), "Oracle type %d not supported.",
280 obj->objectType->elementOracleTypeNum);
281 return cxoError_raiseFromString(cxoNotSupportedErrorException,
282 message);
283 }
284 cxoTransform_getTypeInfo(obj->objectType->elementTransformNum,
285 &oracleTypeNum, &nativeTypeNum);
286 if (oracleTypeNum == DPI_ORACLE_TYPE_NUMBER &&
287 nativeTypeNum == DPI_NATIVE_TYPE_BYTES) {
288 data.value.asBytes.ptr = numberAsStringBuffer;
289 data.value.asBytes.length = sizeof(numberAsStringBuffer);
290 data.value.asBytes.encoding = NULL;
291 }
292 if (dpiObject_getElementValueByIndex(obj->handle, index, nativeTypeNum,
293 &data) < 0)
294 return cxoError_raiseAndReturnNull();
295 return cxoObject_convertToPython(obj, obj->objectType->elementTransformNum,
296 &data, obj->objectType->elementObjectType);
297 }
298
299
300 //-----------------------------------------------------------------------------
301 // cxoObject_asDict()
302 // Returns a collection as a dictionary. If the object is not a collection,
303 // an error is returned.
304 //-----------------------------------------------------------------------------
305 static PyObject *cxoObject_asDict(cxoObject *obj, PyObject *args)
306 {
307 PyObject *dict, *key, *value;
308 int32_t index, nextIndex;
309 int exists;
310
311 // create the result dictionary
312 dict = PyDict_New();
313 if (!dict)
314 return NULL;
315
316 // populate it with each of the elements in the collection
317 if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0) {
318 Py_DECREF(dict);
319 return cxoError_raiseAndReturnNull();
320 }
321 while (exists) {
322 value = cxoObject_internalGetElementByIndex(obj, index);
323 if (!value) {
324 Py_DECREF(dict);
325 return NULL;
326 }
327 key = PyLong_FromLong(index);
328 if (!key) {
329 Py_DECREF(value);
330 Py_DECREF(dict);
331 return NULL;
332 }
333 if (PyDict_SetItem(dict, key, value) < 0) {
334 Py_DECREF(key);
335 Py_DECREF(value);
336 Py_DECREF(dict);
337 return NULL;
338 }
339 Py_DECREF(key);
340 Py_DECREF(value);
341 if (dpiObject_getNextIndex(obj->handle, index, &nextIndex,
342 &exists) < 0) {
343 Py_DECREF(dict);
344 return cxoError_raiseAndReturnNull();
345 }
346 index = nextIndex;
347 }
348
349 return dict;
350 }
351
352
353 //-----------------------------------------------------------------------------
354 // cxoObject_asList()
355 // Returns a collection as a list of elements. If the object is not a
356 // collection, an error is returned.
357 //-----------------------------------------------------------------------------
358 static PyObject *cxoObject_asList(cxoObject *obj, PyObject *args)
359 {
360 PyObject *list, *elementValue;
361 int32_t index, nextIndex;
362 int exists;
363
364 // create the result list
365 list = PyList_New(0);
366 if (!list)
367 return NULL;
368
369 // populate it with each of the elements in the list
370 if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0) {
371 Py_DECREF(list);
372 return cxoError_raiseAndReturnNull();
373 }
374 while (exists) {
375 elementValue = cxoObject_internalGetElementByIndex(obj, index);
376 if (!elementValue) {
377 Py_DECREF(list);
378 return NULL;
379 }
380 if (PyList_Append(list, elementValue) < 0) {
381 Py_DECREF(elementValue);
382 Py_DECREF(list);
383 return NULL;
384 }
385 Py_DECREF(elementValue);
386 if (dpiObject_getNextIndex(obj->handle, index, &nextIndex,
387 &exists) < 0) {
388 Py_DECREF(list);
389 return cxoError_raiseAndReturnNull();
390 }
391 index = nextIndex;
392 }
393
394 return list;
395 }
396
397
398 //-----------------------------------------------------------------------------
399 // cxoObject_copy()
400 // Return a copy of the object.
401 //-----------------------------------------------------------------------------
402 static PyObject *cxoObject_copy(cxoObject *obj, PyObject *args)
403 {
404 PyObject *copiedObj;
405 dpiObject *handle;
406
407 if (dpiObject_copy(obj->handle, &handle) < 0)
408 return cxoError_raiseAndReturnNull();
409 copiedObj = cxoObject_new(obj->objectType, handle);
410 if (!copiedObj) {
411 dpiObject_release(handle);
412 return NULL;
413 }
414 return copiedObj;
415 }
416
417
418 //-----------------------------------------------------------------------------
419 // cxoObject_delete()
420 // Delete the element at the specified index in the collection.
421 //-----------------------------------------------------------------------------
422 static PyObject *cxoObject_delete(cxoObject *obj, PyObject *args)
423 {
424 int32_t index;
425
426 if (!PyArg_ParseTuple(args, "i", &index))
427 return NULL;
428 if (dpiObject_deleteElementByIndex(obj->handle, index) < 0)
429 return cxoError_raiseAndReturnNull();
430 Py_RETURN_NONE;
431 }
432
433
434 //-----------------------------------------------------------------------------
435 // cxoObject_exists()
436 // Return true or false indicating if an element exists in the collection at
437 // the specified index.
438 //-----------------------------------------------------------------------------
439 static PyObject *cxoObject_exists(cxoObject *obj, PyObject *args)
440 {
441 int32_t index;
442 int exists;
443
444 if (!PyArg_ParseTuple(args, "i", &index))
445 return NULL;
446 if (dpiObject_getElementExistsByIndex(obj->handle, index, &exists) < 0)
447 return cxoError_raiseAndReturnNull();
448 if (exists)
449 Py_RETURN_TRUE;
450 Py_RETURN_FALSE;
451 }
452
453
454 //-----------------------------------------------------------------------------
455 // cxoObject_extend()
456 // Extend the collection by appending each of the items in the sequence.
457 //-----------------------------------------------------------------------------
458 static PyObject *cxoObject_extend(cxoObject *obj, PyObject *sequence)
459 {
460 if (cxoObject_internalExtend(obj, sequence) < 0)
461 return NULL;
462 Py_RETURN_NONE;
463 }
464
465
466 //-----------------------------------------------------------------------------
467 // cxoObject_getElement()
468 // Return the element at the given position in the collection.
469 //-----------------------------------------------------------------------------
470 static PyObject *cxoObject_getElement(cxoObject *obj, PyObject *args)
471 {
472 int32_t index;
473
474 if (!PyArg_ParseTuple(args, "i", &index))
475 return NULL;
476 return cxoObject_internalGetElementByIndex(obj, index);
477 }
478
479
480 //-----------------------------------------------------------------------------
481 // cxoObject_getFirstIndex()
482 // Return the index of the first entry in the collection.
483 //-----------------------------------------------------------------------------
484 static PyObject *cxoObject_getFirstIndex(cxoObject *obj, PyObject *args)
485 {
486 int32_t index;
487 int exists;
488
489 if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0)
490 return cxoError_raiseAndReturnNull();
491 if (exists)
492 return PyLong_FromLong(index);
493 Py_RETURN_NONE;
494 }
495
496
497 //-----------------------------------------------------------------------------
498 // cxoObject_getLastIndex()
499 // Return the index of the last entry in the collection.
500 //-----------------------------------------------------------------------------
501 static PyObject *cxoObject_getLastIndex(cxoObject *obj, PyObject *args)
502 {
503 int32_t index;
504 int exists;
505
506 if (dpiObject_getLastIndex(obj->handle, &index, &exists) < 0)
507 return cxoError_raiseAndReturnNull();
508 if (exists)
509 return PyLong_FromLong(index);
510 Py_RETURN_NONE;
511 }
512
513
514 //-----------------------------------------------------------------------------
515 // cxoObject_getNextIndex()
516 // Return the index of the next entry in the collection following the index
517 // specified. If there is no next entry, None is returned.
518 //-----------------------------------------------------------------------------
519 static PyObject *cxoObject_getNextIndex(cxoObject *obj, PyObject *args)
520 {
521 int32_t index, nextIndex;
522 int exists;
523
524 if (!PyArg_ParseTuple(args, "i", &index))
525 return NULL;
526 if (dpiObject_getNextIndex(obj->handle, index, &nextIndex, &exists) < 0)
527 return cxoError_raiseAndReturnNull();
528 if (exists)
529 return PyLong_FromLong(nextIndex);
530 Py_RETURN_NONE;
531 }
532
533
534 //-----------------------------------------------------------------------------
535 // cxoObject_getPrevIndex()
536 // Return the index of the previous entry in the collection preceding the
537 // index specified. If there is no previous entry, None is returned.
538 //-----------------------------------------------------------------------------
539 static PyObject *cxoObject_getPrevIndex(cxoObject *obj, PyObject *args)
540 {
541 int32_t index, prevIndex;
542 int exists;
543
544 if (!PyArg_ParseTuple(args, "i", &index))
545 return NULL;
546 if (dpiObject_getPrevIndex(obj->handle, index, &prevIndex, &exists) < 0)
547 return cxoError_raiseAndReturnNull();
548 if (exists)
549 return PyLong_FromLong(prevIndex);
550 Py_RETURN_NONE;
551 }
552
553
554 //-----------------------------------------------------------------------------
555 // cxoObject_getSize()
556 // Return the size of a collection. If the object is not a collection, an
557 // error is returned.
558 //-----------------------------------------------------------------------------
559 static PyObject *cxoObject_getSize(cxoObject *obj, PyObject *args)
560 {
561 int32_t size;
562
563 if (dpiObject_getSize(obj->handle, &size) < 0)
564 return cxoError_raiseAndReturnNull();
565 return PyLong_FromLong(size);
566 }
567
568
569 //-----------------------------------------------------------------------------
570 // cxoObject_setElement()
571 // Set the element at the specified location to the given value.
572 //-----------------------------------------------------------------------------
573 static PyObject *cxoObject_setElement(cxoObject *obj, PyObject *args)
574 {
575 dpiNativeTypeNum nativeTypeNum = 0;
576 cxoBuffer buffer;
577 PyObject *value;
578 int32_t index;
579 dpiData data;
580 int status;
581
582 if (!PyArg_ParseTuple(args, "iO", &index, &value))
583 return NULL;
584 cxoBuffer_init(&buffer);
585 if (cxoObject_convertFromPython(obj, value,
586 obj->objectType->elementTransformNum, &nativeTypeNum, &data,
587 &buffer) < 0)
588 return NULL;
589 status = dpiObject_setElementValueByIndex(obj->handle, index,
590 nativeTypeNum, &data);
591 cxoBuffer_clear(&buffer);
592 if (status < 0)
593 return cxoError_raiseAndReturnNull();
594 Py_RETURN_NONE;
595 }
596
597
598 //-----------------------------------------------------------------------------
599 // cxoObject_trim()
600 // Trim a number of elements from the end of the collection.
601 //-----------------------------------------------------------------------------
602 static PyObject *cxoObject_trim(cxoObject *obj, PyObject *args)
603 {
604 int32_t numToTrim;
605
606 if (!PyArg_ParseTuple(args, "i", &numToTrim))
607 return NULL;
608 if (dpiObject_trim(obj->handle, numToTrim) < 0)
609 return cxoError_raiseAndReturnNull();
610 Py_RETURN_NONE;
611 }
612
613
614 //-----------------------------------------------------------------------------
615 // declaration of methods for Python type
42616 //-----------------------------------------------------------------------------
43617 static PyMethodDef cxoObjectMethods[] = {
44618 { "append", (PyCFunction) cxoObject_append, METH_O },
61635
62636
63637 //-----------------------------------------------------------------------------
64 // Declaration of members for Python type "Object".
638 // Declaration of members for Python type
65639 //-----------------------------------------------------------------------------
66640 static PyMemberDef cxoObjectMembers[] = {
67641 { "type", T_OBJECT, offsetof(cxoObject, objectType), READONLY },
74648 //-----------------------------------------------------------------------------
75649 PyTypeObject cxoPyTypeObject = {
76650 PyVarObject_HEAD_INIT(NULL, 0)
77 "cx_Oracle.Object", // tp_name
78 sizeof(cxoObject), // tp_basicsize
79 0, // tp_itemsize
80 (destructor) cxoObject_free, // tp_dealloc
81 0, // tp_print
82 0, // tp_getattr
83 0, // tp_setattr
84 0, // tp_compare
85 (reprfunc) cxoObject_repr, // tp_repr
86 0, // tp_as_number
87 0, // tp_as_sequence
88 0, // tp_as_mapping
89 0, // tp_hash
90 0, // tp_call
91 0, // tp_str
92 (getattrofunc) cxoObject_getAttr, // tp_getattro
93 (setattrofunc) cxoObject_setAttr, // tp_setattro
94 0, // tp_as_buffer
95 Py_TPFLAGS_DEFAULT, // tp_flags
96 0, // tp_doc
97 0, // tp_traverse
98 0, // tp_clear
99 0, // tp_richcompare
100 0, // tp_weaklistoffset
101 0, // tp_iter
102 0, // tp_iternext
103 cxoObjectMethods, // tp_methods
104 cxoObjectMembers // tp_members
651 .tp_name = "cx_Oracle.Object",
652 .tp_basicsize = sizeof(cxoObject),
653 .tp_dealloc = (destructor) cxoObject_free,
654 .tp_repr = (reprfunc) cxoObject_repr,
655 .tp_getattro = (getattrofunc) cxoObject_getAttr,
656 .tp_setattro = (setattrofunc) cxoObject_setAttr,
657 .tp_flags = Py_TPFLAGS_DEFAULT,
658 .tp_methods = cxoObjectMethods,
659 .tp_members = cxoObjectMembers
105660 };
106
107
108 //-----------------------------------------------------------------------------
109 // cxoObject_new()
110 // Create a new object.
111 //-----------------------------------------------------------------------------
112 PyObject *cxoObject_new(cxoObjectType *objectType, dpiObject *handle)
113 {
114 cxoObject *obj;
115
116 obj = (cxoObject*) cxoPyTypeObject.tp_alloc(&cxoPyTypeObject, 0);
117 if (!obj)
118 return NULL;
119 Py_INCREF(objectType);
120 obj->objectType = objectType;
121 obj->handle = handle;
122 return (PyObject*) obj;
123 }
124
125
126 //-----------------------------------------------------------------------------
127 // cxoObject_free()
128 // Free an object.
129 //-----------------------------------------------------------------------------
130 static void cxoObject_free(cxoObject *obj)
131 {
132 if (obj->handle) {
133 dpiObject_release(obj->handle);
134 obj->handle = NULL;
135 }
136 Py_CLEAR(obj->objectType);
137 Py_TYPE(obj)->tp_free((PyObject*) obj);
138 }
139
140
141 //-----------------------------------------------------------------------------
142 // cxoObject_repr()
143 // Return a string representation of the object.
144 //-----------------------------------------------------------------------------
145 static PyObject *cxoObject_repr(cxoObject *obj)
146 {
147 PyObject *module, *name, *result;
148
149 if (cxoUtils_getModuleAndName(Py_TYPE(obj), &module, &name) < 0)
150 return NULL;
151 result = cxoUtils_formatString("<%s.%s %s.%s at %#x>",
152 Py_BuildValue("(OOOOl)", module, name, obj->objectType->schema,
153 obj->objectType->name, obj));
154 Py_DECREF(module);
155 Py_DECREF(name);
156 return result;
157 }
158
159
160 //-----------------------------------------------------------------------------
161 // cxoObject_convertFromPython()
162 // Convert a Python value to an Oracle value.
163 //-----------------------------------------------------------------------------
164 static int cxoObject_convertFromPython(cxoObject *obj, PyObject *value,
165 cxoTransformNum transformNum, dpiNativeTypeNum *nativeTypeNum,
166 dpiData *data, cxoBuffer *buffer)
167 {
168 dpiOracleTypeNum oracleTypeNum;
169
170 // None is treated as null
171 if (value == Py_None) {
172 data->isNull = 1;
173 return 0;
174 }
175
176 // convert the different Python types
177 cxoTransform_getTypeInfo(transformNum, &oracleTypeNum, nativeTypeNum);
178 if (cxoTransform_fromPython(transformNum, value, &data->value, buffer,
179 obj->objectType->connection->encodingInfo.encoding,
180 obj->objectType->connection->encodingInfo.nencoding, NULL, 0) < 0)
181 return -1;
182 data->isNull = 0;
183 return 0;
184 }
185
186
187 //-----------------------------------------------------------------------------
188 // cxoObject_convertToPython()
189 // Convert an Oracle value to a Python value.
190 //-----------------------------------------------------------------------------
191 static PyObject *cxoObject_convertToPython(cxoObject *obj,
192 cxoTransformNum transformNum, dpiData *data, cxoObjectType *objType)
193 {
194 if (data->isNull)
195 Py_RETURN_NONE;
196 return cxoTransform_toPython(transformNum, obj->objectType->connection,
197 objType, &data->value, NULL);
198 }
199
200
201 //-----------------------------------------------------------------------------
202 // cxoObject_getAttributeValue()
203 // Retrieve an attribute on the object.
204 //-----------------------------------------------------------------------------
205 static PyObject *cxoObject_getAttributeValue(cxoObject *obj,
206 cxoObjectAttr *attribute)
207 {
208 char numberAsStringBuffer[200], message[120];
209 dpiOracleTypeNum oracleTypeNum;
210 dpiNativeTypeNum nativeTypeNum;
211 dpiData data;
212
213 if (attribute->transformNum == CXO_TRANSFORM_UNSUPPORTED) {
214 snprintf(message, sizeof(message), "Oracle type %d not supported.",
215 attribute->oracleTypeNum);
216 return cxoError_raiseFromString(cxoNotSupportedErrorException,
217 message);
218 }
219 cxoTransform_getTypeInfo(attribute->transformNum, &oracleTypeNum,
220 &nativeTypeNum);
221 if (oracleTypeNum == DPI_ORACLE_TYPE_NUMBER &&
222 nativeTypeNum == DPI_NATIVE_TYPE_BYTES) {
223 data.value.asBytes.ptr = numberAsStringBuffer;
224 data.value.asBytes.length = sizeof(numberAsStringBuffer);
225 data.value.asBytes.encoding = NULL;
226 }
227 if (dpiObject_getAttributeValue(obj->handle, attribute->handle,
228 nativeTypeNum, &data) < 0)
229 return cxoError_raiseAndReturnNull();
230 return cxoObject_convertToPython(obj, attribute->transformNum, &data,
231 attribute->type);
232 }
233
234
235 //-----------------------------------------------------------------------------
236 // cxoObject_setAttributeValue()
237 // Set an attribute on the object.
238 //-----------------------------------------------------------------------------
239 static int cxoObject_setAttributeValue(cxoObject *obj,
240 cxoObjectAttr *attribute, PyObject *value)
241 {
242 dpiNativeTypeNum nativeTypeNum = 0;
243 cxoBuffer buffer;
244 dpiData data;
245 int status;
246
247 cxoBuffer_init(&buffer);
248 if (cxoObject_convertFromPython(obj, value, attribute->transformNum,
249 &nativeTypeNum, &data, &buffer) < 0)
250 return -1;
251 status = dpiObject_setAttributeValue(obj->handle, attribute->handle,
252 nativeTypeNum, &data);
253 cxoBuffer_clear(&buffer);
254 if (status < 0)
255 return cxoError_raiseAndReturnInt();
256 return 0;
257 }
258
259
260 //-----------------------------------------------------------------------------
261 // cxoObject_getAttr()
262 // Retrieve an attribute on an object.
263 //-----------------------------------------------------------------------------
264 static PyObject *cxoObject_getAttr(cxoObject *obj, PyObject *nameObject)
265 {
266 cxoObjectAttr *attribute;
267
268 attribute = (cxoObjectAttr*)
269 PyDict_GetItem(obj->objectType->attributesByName, nameObject);
270 if (attribute)
271 return cxoObject_getAttributeValue(obj, attribute);
272
273 return PyObject_GenericGetAttr( (PyObject*) obj, nameObject);
274 }
275
276
277 //-----------------------------------------------------------------------------
278 // cxoObject_setAttr()
279 // Set an attribute on an object.
280 //-----------------------------------------------------------------------------
281 static int cxoObject_setAttr(cxoObject *obj, PyObject *nameObject,
282 PyObject *value)
283 {
284 cxoObjectAttr *attribute;
285
286 attribute = (cxoObjectAttr*)
287 PyDict_GetItem(obj->objectType->attributesByName, nameObject);
288 if (attribute)
289 return cxoObject_setAttributeValue(obj, attribute, value);
290
291 return PyObject_GenericSetAttr( (PyObject*) obj, nameObject, value);
292 }
293
294
295 //-----------------------------------------------------------------------------
296 // cxoObject_internalAppend()
297 // Append an item to the collection.
298 //-----------------------------------------------------------------------------
299 static int cxoObject_internalAppend(cxoObject *obj, PyObject *value)
300 {
301 dpiNativeTypeNum nativeTypeNum = 0;
302 cxoBuffer buffer;
303 dpiData data;
304 int status;
305
306 cxoBuffer_init(&buffer);
307 if (cxoObject_convertFromPython(obj, value,
308 obj->objectType->elementTransformNum, &nativeTypeNum, &data,
309 &buffer) < 0)
310 return -1;
311 status = dpiObject_appendElement(obj->handle, nativeTypeNum, &data);
312 cxoBuffer_clear(&buffer);
313 if (status < 0)
314 return cxoError_raiseAndReturnInt();
315 return 0;
316 }
317
318
319 //-----------------------------------------------------------------------------
320 // cxoObject_internalExtend()
321 // Extend the collection by appending each of the items in the sequence.
322 //-----------------------------------------------------------------------------
323 int cxoObject_internalExtend(cxoObject *obj, PyObject *sequence)
324 {
325 PyObject *fastSequence, *element;
326 Py_ssize_t size, i;
327
328 fastSequence = PySequence_Fast(sequence, "expecting sequence");
329 if (!fastSequence)
330 return -1;
331 size = PySequence_Fast_GET_SIZE(fastSequence);
332 for (i = 0; i < size; i++) {
333 element = PySequence_Fast_GET_ITEM(fastSequence, i);
334 if (cxoObject_internalAppend(obj, element) < 0) {
335 Py_DECREF(fastSequence);
336 return -1;
337 }
338 }
339 Py_DECREF(fastSequence);
340
341 return 0;
342 }
343
344
345 //-----------------------------------------------------------------------------
346 // cxoObject_append()
347 // Append an item to the collection.
348 //-----------------------------------------------------------------------------
349 static PyObject *cxoObject_append(cxoObject *obj, PyObject *value)
350 {
351 if (cxoObject_internalAppend(obj, value) < 0)
352 return NULL;
353 Py_RETURN_NONE;
354 }
355
356
357 //-----------------------------------------------------------------------------
358 // cxoObject_internalGetElementByIndex()
359 // Internal method used for getting an element value for a particular index.
360 //-----------------------------------------------------------------------------
361 static PyObject *cxoObject_internalGetElementByIndex(cxoObject *obj,
362 int32_t index)
363 {
364 char numberAsStringBuffer[200], message[120];
365 dpiOracleTypeNum oracleTypeNum;
366 dpiNativeTypeNum nativeTypeNum;
367 dpiData data;
368
369 if (obj->objectType->elementTransformNum == CXO_TRANSFORM_UNSUPPORTED) {
370 snprintf(message, sizeof(message), "Oracle type %d not supported.",
371 obj->objectType->elementOracleTypeNum);
372 return cxoError_raiseFromString(cxoNotSupportedErrorException,
373 message);
374 }
375 cxoTransform_getTypeInfo(obj->objectType->elementTransformNum,
376 &oracleTypeNum, &nativeTypeNum);
377 if (oracleTypeNum == DPI_ORACLE_TYPE_NUMBER &&
378 nativeTypeNum == DPI_NATIVE_TYPE_BYTES) {
379 data.value.asBytes.ptr = numberAsStringBuffer;
380 data.value.asBytes.length = sizeof(numberAsStringBuffer);
381 data.value.asBytes.encoding = NULL;
382 }
383 if (dpiObject_getElementValueByIndex(obj->handle, index, nativeTypeNum,
384 &data) < 0)
385 return cxoError_raiseAndReturnNull();
386 return cxoObject_convertToPython(obj, obj->objectType->elementTransformNum,
387 &data, (cxoObjectType*) obj->objectType->elementType);
388 }
389
390
391 //-----------------------------------------------------------------------------
392 // cxoObject_asDict()
393 // Returns a collection as a dictionary. If the object is not a collection,
394 // an error is returned.
395 //-----------------------------------------------------------------------------
396 static PyObject *cxoObject_asDict(cxoObject *obj, PyObject *args)
397 {
398 PyObject *dict, *key, *value;
399 int32_t index, nextIndex;
400 int exists;
401
402 // create the result dictionary
403 dict = PyDict_New();
404 if (!dict)
405 return NULL;
406
407 // populate it with each of the elements in the collection
408 if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0) {
409 Py_DECREF(dict);
410 return cxoError_raiseAndReturnNull();
411 }
412 while (exists) {
413 value = cxoObject_internalGetElementByIndex(obj, index);
414 if (!value) {
415 Py_DECREF(dict);
416 return NULL;
417 }
418 key = PyInt_FromLong(index);
419 if (!key) {
420 Py_DECREF(value);
421 Py_DECREF(dict);
422 return NULL;
423 }
424 if (PyDict_SetItem(dict, key, value) < 0) {
425 Py_DECREF(key);
426 Py_DECREF(value);
427 Py_DECREF(dict);
428 return NULL;
429 }
430 Py_DECREF(key);
431 Py_DECREF(value);
432 if (dpiObject_getNextIndex(obj->handle, index, &nextIndex,
433 &exists) < 0) {
434 Py_DECREF(dict);
435 return cxoError_raiseAndReturnNull();
436 }
437 index = nextIndex;
438 }
439
440 return dict;
441 }
442
443
444 //-----------------------------------------------------------------------------
445 // cxoObject_asList()
446 // Returns a collection as a list of elements. If the object is not a
447 // collection, an error is returned.
448 //-----------------------------------------------------------------------------
449 static PyObject *cxoObject_asList(cxoObject *obj, PyObject *args)
450 {
451 PyObject *list, *elementValue;
452 int32_t index, nextIndex;
453 int exists;
454
455 // create the result list
456 list = PyList_New(0);
457 if (!list)
458 return NULL;
459
460 // populate it with each of the elements in the list
461 if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0) {
462 Py_DECREF(list);
463 return cxoError_raiseAndReturnNull();
464 }
465 while (exists) {
466 elementValue = cxoObject_internalGetElementByIndex(obj, index);
467 if (!elementValue) {
468 Py_DECREF(list);
469 return NULL;
470 }
471 if (PyList_Append(list, elementValue) < 0) {
472 Py_DECREF(elementValue);
473 Py_DECREF(list);
474 return NULL;
475 }
476 Py_DECREF(elementValue);
477 if (dpiObject_getNextIndex(obj->handle, index, &nextIndex,
478 &exists) < 0) {
479 Py_DECREF(list);
480 return cxoError_raiseAndReturnNull();
481 }
482 index = nextIndex;
483 }
484
485 return list;
486 }
487
488
489 //-----------------------------------------------------------------------------
490 // cxoObject_copy()
491 // Return a copy of the object.
492 //-----------------------------------------------------------------------------
493 static PyObject *cxoObject_copy(cxoObject *obj, PyObject *args)
494 {
495 PyObject *copiedObj;
496 dpiObject *handle;
497
498 if (dpiObject_copy(obj->handle, &handle) < 0)
499 return cxoError_raiseAndReturnNull();
500 copiedObj = cxoObject_new(obj->objectType, handle);
501 if (!copiedObj) {
502 dpiObject_release(handle);
503 return NULL;
504 }
505 return copiedObj;
506 }
507
508
509 //-----------------------------------------------------------------------------
510 // cxoObject_delete()
511 // Delete the element at the specified index in the collection.
512 //-----------------------------------------------------------------------------
513 static PyObject *cxoObject_delete(cxoObject *obj, PyObject *args)
514 {
515 int32_t index;
516
517 if (!PyArg_ParseTuple(args, "i", &index))
518 return NULL;
519 if (dpiObject_deleteElementByIndex(obj->handle, index) < 0)
520 return cxoError_raiseAndReturnNull();
521 Py_RETURN_NONE;
522 }
523
524
525 //-----------------------------------------------------------------------------
526 // cxoObject_exists()
527 // Return true or false indicating if an element exists in the collection at
528 // the specified index.
529 //-----------------------------------------------------------------------------
530 static PyObject *cxoObject_exists(cxoObject *obj, PyObject *args)
531 {
532 int32_t index;
533 int exists;
534
535 if (!PyArg_ParseTuple(args, "i", &index))
536 return NULL;
537 if (dpiObject_getElementExistsByIndex(obj->handle, index, &exists) < 0)
538 return cxoError_raiseAndReturnNull();
539 if (exists)
540 Py_RETURN_TRUE;
541 Py_RETURN_FALSE;
542 }
543
544
545 //-----------------------------------------------------------------------------
546 // cxoObject_extend()
547 // Extend the collection by appending each of the items in the sequence.
548 //-----------------------------------------------------------------------------
549 static PyObject *cxoObject_extend(cxoObject *obj, PyObject *sequence)
550 {
551 if (cxoObject_internalExtend(obj, sequence) < 0)
552 return NULL;
553 Py_RETURN_NONE;
554 }
555
556
557 //-----------------------------------------------------------------------------
558 // cxoObject_getElement()
559 // Return the element at the given position in the collection.
560 //-----------------------------------------------------------------------------
561 static PyObject *cxoObject_getElement(cxoObject *obj, PyObject *args)
562 {
563 int32_t index;
564
565 if (!PyArg_ParseTuple(args, "i", &index))
566 return NULL;
567 return cxoObject_internalGetElementByIndex(obj, index);
568 }
569
570
571 //-----------------------------------------------------------------------------
572 // cxoObject_getFirstIndex()
573 // Return the index of the first entry in the collection.
574 //-----------------------------------------------------------------------------
575 static PyObject *cxoObject_getFirstIndex(cxoObject *obj, PyObject *args)
576 {
577 int32_t index;
578 int exists;
579
580 if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0)
581 return cxoError_raiseAndReturnNull();
582 if (exists)
583 return PyInt_FromLong(index);
584 Py_RETURN_NONE;
585 }
586
587
588 //-----------------------------------------------------------------------------
589 // cxoObject_getLastIndex()
590 // Return the index of the last entry in the collection.
591 //-----------------------------------------------------------------------------
592 static PyObject *cxoObject_getLastIndex(cxoObject *obj, PyObject *args)
593 {
594 int32_t index;
595 int exists;
596
597 if (dpiObject_getLastIndex(obj->handle, &index, &exists) < 0)
598 return cxoError_raiseAndReturnNull();
599 if (exists)
600 return PyInt_FromLong(index);
601 Py_RETURN_NONE;
602 }
603
604
605 //-----------------------------------------------------------------------------
606 // cxoObject_getNextIndex()
607 // Return the index of the next entry in the collection following the index
608 // specified. If there is no next entry, None is returned.
609 //-----------------------------------------------------------------------------
610 static PyObject *cxoObject_getNextIndex(cxoObject *obj, PyObject *args)
611 {
612 int32_t index, nextIndex;
613 int exists;
614
615 if (!PyArg_ParseTuple(args, "i", &index))
616 return NULL;
617 if (dpiObject_getNextIndex(obj->handle, index, &nextIndex, &exists) < 0)
618 return cxoError_raiseAndReturnNull();
619 if (exists)
620 return PyInt_FromLong(nextIndex);
621 Py_RETURN_NONE;
622 }
623
624
625 //-----------------------------------------------------------------------------
626 // cxoObject_getPrevIndex()
627 // Return the index of the previous entry in the collection preceding the
628 // index specified. If there is no previous entry, None is returned.
629 //-----------------------------------------------------------------------------
630 static PyObject *cxoObject_getPrevIndex(cxoObject *obj, PyObject *args)
631 {
632 int32_t index, prevIndex;
633 int exists;
634
635 if (!PyArg_ParseTuple(args, "i", &index))
636 return NULL;
637 if (dpiObject_getPrevIndex(obj->handle, index, &prevIndex, &exists) < 0)
638 return cxoError_raiseAndReturnNull();
639 if (exists)
640 return PyInt_FromLong(prevIndex);
641 Py_RETURN_NONE;
642 }
643
644
645 //-----------------------------------------------------------------------------
646 // cxoObject_getSize()
647 // Return the size of a collection. If the object is not a collection, an
648 // error is returned.
649 //-----------------------------------------------------------------------------
650 static PyObject *cxoObject_getSize(cxoObject *obj, PyObject *args)
651 {
652 int32_t size;
653
654 if (dpiObject_getSize(obj->handle, &size) < 0)
655 return cxoError_raiseAndReturnNull();
656 return PyInt_FromLong(size);
657 }
658
659
660 //-----------------------------------------------------------------------------
661 // cxoObject_setElement()
662 // Set the element at the specified location to the given value.
663 //-----------------------------------------------------------------------------
664 static PyObject *cxoObject_setElement(cxoObject *obj, PyObject *args)
665 {
666 dpiNativeTypeNum nativeTypeNum = 0;
667 cxoBuffer buffer;
668 PyObject *value;
669 int32_t index;
670 dpiData data;
671 int status;
672
673 if (!PyArg_ParseTuple(args, "iO", &index, &value))
674 return NULL;
675 cxoBuffer_init(&buffer);
676 if (cxoObject_convertFromPython(obj, value,
677 obj->objectType->elementTransformNum, &nativeTypeNum, &data,
678 &buffer) < 0)
679 return NULL;
680 status = dpiObject_setElementValueByIndex(obj->handle, index,
681 nativeTypeNum, &data);
682 cxoBuffer_clear(&buffer);
683 if (status < 0)
684 return cxoError_raiseAndReturnNull();
685 Py_RETURN_NONE;
686 }
687
688
689 //-----------------------------------------------------------------------------
690 // cxoObject_trim()
691 // Trim a number of elements from the end of the collection.
692 //-----------------------------------------------------------------------------
693 static PyObject *cxoObject_trim(cxoObject *obj, PyObject *args)
694 {
695 int32_t numToTrim;
696
697 if (!PyArg_ParseTuple(args, "i", &numToTrim))
698 return NULL;
699 if (dpiObject_trim(obj->handle, numToTrim) < 0)
700 return cxoError_raiseAndReturnNull();
701 Py_RETURN_NONE;
702 }
703
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //-----------------------------------------------------------------------------
33
44 //-----------------------------------------------------------------------------
77 //-----------------------------------------------------------------------------
88
99 #include "cxoModule.h"
10
11 //-----------------------------------------------------------------------------
12 // Declaration of functions
13 //-----------------------------------------------------------------------------
14 static void cxoObjectAttr_free(cxoObjectAttr*);
15 static PyObject *cxoObjectAttr_repr(cxoObjectAttr*);
16
17
18 //-----------------------------------------------------------------------------
19 // declaration of members for Python type "ObjectAttribute"
20 //-----------------------------------------------------------------------------
21 static PyMemberDef cxoObjectAttrMembers[] = {
22 { "name", T_OBJECT, offsetof(cxoObjectAttr, name), READONLY },
23 { NULL }
24 };
25
26
27 //-----------------------------------------------------------------------------
28 // Python type declaration
29 //-----------------------------------------------------------------------------
30 PyTypeObject cxoPyTypeObjectAttr = {
31 PyVarObject_HEAD_INIT(NULL, 0)
32 "cx_Oracle.ObjectAttribute", // tp_name
33 sizeof(cxoObjectAttr), // tp_basicsize
34 0, // tp_itemsize
35 (destructor) cxoObjectAttr_free, // tp_dealloc
36 0, // tp_print
37 0, // tp_getattr
38 0, // tp_setattr
39 0, // tp_compare
40 (reprfunc) cxoObjectAttr_repr, // tp_repr
41 0, // tp_as_number
42 0, // tp_as_sequence
43 0, // tp_as_mapping
44 0, // tp_hash
45 0, // tp_call
46 0, // tp_str
47 0, // tp_getattro
48 0, // tp_setattro
49 0, // tp_as_buffer
50 Py_TPFLAGS_DEFAULT, // tp_flags
51 0, // tp_doc
52 0, // tp_traverse
53 0, // tp_clear
54 0, // tp_richcompare
55 0, // tp_weaklistoffset
56 0, // tp_iter
57 0, // tp_iternext
58 0, // tp_methods
59 cxoObjectAttrMembers, // tp_members
60 0, // tp_getset
61 0, // tp_base
62 0, // tp_dict
63 0, // tp_descr_get
64 0, // tp_descr_set
65 0, // tp_dictoffset
66 0, // tp_init
67 0, // tp_alloc
68 0, // tp_new
69 0, // tp_free
70 0, // tp_is_gc
71 0 // tp_bases
72 };
73
7410
7511 //-----------------------------------------------------------------------------
7612 // cxoObjectAttr_initialize()
8420 if (dpiObjectAttr_getInfo(attr->handle, &info) < 0)
8521 return cxoError_raiseAndReturnInt();
8622 attr->transformNum = cxoTransform_getNumFromDataTypeInfo(&info.typeInfo);
23 attr->dbType = cxoDbType_fromTransformNum(attr->transformNum);
24 if (!attr->dbType)
25 return -1;
26 Py_INCREF(attr->dbType);
8727 attr->oracleTypeNum = info.typeInfo.oracleTypeNum;
88 attr->name = cxoPyString_fromEncodedString(info.name, info.nameLength,
28 attr->name = PyUnicode_Decode(info.name, info.nameLength,
8929 connection->encodingInfo.encoding, NULL);
9030 if (!attr->name)
9131 return -1;
9232 if (info.typeInfo.objectType) {
93 attr->type = cxoObjectType_new(connection,
33 attr->objectType = cxoObjectType_new(connection,
9434 info.typeInfo.objectType);
95 if (!attr->type)
35 if (!attr->objectType)
9636 return -1;
9737 }
9838
13676 attr->handle = NULL;
13777 }
13878 Py_CLEAR(attr->name);
139 Py_CLEAR(attr->type);
79 Py_CLEAR(attr->objectType);
80 Py_CLEAR(attr->dbType);
14081 Py_TYPE(attr)->tp_free((PyObject*) attr);
82 }
83
84
85 //-----------------------------------------------------------------------------
86 // cxoObjectAttr_getType()
87 // Return the type associated with the attribute. This is either an object
88 // type or one of the database type constants.
89 //-----------------------------------------------------------------------------
90 static PyObject *cxoObjectAttr_getType(cxoObjectAttr *attr, void *unused)
91 {
92 if (attr->objectType) {
93 Py_INCREF(attr->objectType);
94 return (PyObject*) attr->objectType;
95 }
96
97 Py_INCREF(attr->dbType);
98 return (PyObject*) attr->dbType;
14199 }
142100
143101
158116 return result;
159117 }
160118
119
120 //-----------------------------------------------------------------------------
121 // declaration of members
122 //-----------------------------------------------------------------------------
123 static PyMemberDef cxoMembers[] = {
124 { "name", T_OBJECT, offsetof(cxoObjectAttr, name), READONLY },
125 { NULL }
126 };
127
128
129 //-----------------------------------------------------------------------------
130 // declaration of calculated members
131 //-----------------------------------------------------------------------------
132 static PyGetSetDef cxoCalcMembers[] = {
133 { "type", (getter) cxoObjectAttr_getType, 0, 0, 0 },
134 { NULL }
135 };
136
137
138 //-----------------------------------------------------------------------------
139 // Python type declaration
140 //-----------------------------------------------------------------------------
141 PyTypeObject cxoPyTypeObjectAttr = {
142 PyVarObject_HEAD_INIT(NULL, 0)
143 .tp_name = "cx_Oracle.ObjectAttribute",
144 .tp_basicsize = sizeof(cxoObjectAttr),
145 .tp_dealloc = (destructor) cxoObjectAttr_free,
146 .tp_repr = (reprfunc) cxoObjectAttr_repr,
147 .tp_flags = Py_TPFLAGS_DEFAULT,
148 .tp_members = cxoMembers,
149 .tp_getset = cxoCalcMembers
150 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // Declaration of functions
18 //-----------------------------------------------------------------------------
19 static void cxoObjectType_free(cxoObjectType*);
20 static PyObject *cxoObjectType_repr(cxoObjectType*);
21 static PyObject *cxoObjectType_newObject(cxoObjectType*, PyObject*, PyObject*);
22
23
24 //-----------------------------------------------------------------------------
25 // declaration of methods for Python type "ObjectType"
26 //-----------------------------------------------------------------------------
27 static PyMethodDef cxoObjectTypeMethods[] = {
28 { "newobject", (PyCFunction) cxoObjectType_newObject,
29 METH_VARARGS | METH_KEYWORDS },
30 { NULL }
31 };
32
33
34 //-----------------------------------------------------------------------------
35 // declaration of members for Python type "ObjectType"
36 //-----------------------------------------------------------------------------
37 static PyMemberDef cxoObjectTypeMembers[] = {
38 { "schema", T_OBJECT, offsetof(cxoObjectType, schema), READONLY },
39 { "name", T_OBJECT, offsetof(cxoObjectType, name), READONLY },
40 { "attributes", T_OBJECT, offsetof(cxoObjectType, attributes), READONLY },
41 { "elementType", T_OBJECT, offsetof(cxoObjectType, elementType),
42 READONLY },
43 { "iscollection", T_BOOL, offsetof(cxoObjectType, isCollection),
44 READONLY },
45 { NULL }
46 };
47
48
49 //-----------------------------------------------------------------------------
50 // Python type declarations
51 //-----------------------------------------------------------------------------
52 PyTypeObject cxoPyTypeObjectType = {
53 PyVarObject_HEAD_INIT(NULL, 0)
54 "cx_Oracle.ObjectType", // tp_name
55 sizeof(cxoObjectType), // tp_basicsize
56 0, // tp_itemsize
57 (destructor) cxoObjectType_free, // tp_dealloc
58 0, // tp_print
59 0, // tp_getattr
60 0, // tp_setattr
61 0, // tp_compare
62 (reprfunc) cxoObjectType_repr, // tp_repr
63 0, // tp_as_number
64 0, // tp_as_sequence
65 0, // tp_as_mapping
66 0, // tp_hash
67 (ternaryfunc) cxoObjectType_newObject, // tp_call
68 0, // tp_str
69 0, // tp_getattro
70 0, // tp_setattro
71 0, // tp_as_buffer
72 Py_TPFLAGS_DEFAULT, // tp_flags
73 0, // tp_doc
74 0, // tp_traverse
75 0, // tp_clear
76 0, // tp_richcompare
77 0, // tp_weaklistoffset
78 0, // tp_iter
79 0, // tp_iternext
80 cxoObjectTypeMethods, // tp_methods
81 cxoObjectTypeMembers, // tp_members
82 0, // tp_getset
83 0, // tp_base
84 0, // tp_dict
85 0, // tp_descr_get
86 0, // tp_descr_set
87 0, // tp_dictoffset
88 0, // tp_init
89 0, // tp_alloc
90 0, // tp_new
91 0, // tp_free
92 0, // tp_is_gc
93 0 // tp_bases
94 };
95
96
97 //-----------------------------------------------------------------------------
9817 // cxoObjectType_initialize()
9918 // Initialize the object type with the information that is required.
10019 //-----------------------------------------------------------------------------
11130 return cxoError_raiseAndReturnInt();
11231 Py_INCREF(connection);
11332 objType->connection = connection;
114 objType->schema = cxoPyString_fromEncodedString(info.schema,
115 info.schemaLength, connection->encodingInfo.encoding, NULL);
33 objType->schema = PyUnicode_Decode(info.schema, info.schemaLength,
34 connection->encodingInfo.encoding, NULL);
11635 if (!objType->schema)
11736 return -1;
118 objType->name = cxoPyString_fromEncodedString(info.name, info.nameLength,
37 objType->name = PyUnicode_Decode(info.name, info.nameLength,
11938 connection->encodingInfo.encoding, NULL);
12039 if (!objType->name)
12140 return -1;
12241 objType->isCollection = info.isCollection;
123 objType->elementOracleTypeNum = info.elementTypeInfo.oracleTypeNum;
124 objType->elementTransformNum =
125 cxoTransform_getNumFromDataTypeInfo(&info.elementTypeInfo);
126 if (info.elementTypeInfo.objectType) {
127 objType->elementType = (PyObject*) cxoObjectType_new(connection,
128 info.elementTypeInfo.objectType);
129 if (!objType->elementType)
42 if (info.isCollection) {
43 objType->elementOracleTypeNum = info.elementTypeInfo.oracleTypeNum;
44 objType->elementTransformNum =
45 cxoTransform_getNumFromDataTypeInfo(&info.elementTypeInfo);
46 objType->elementDbType =
47 cxoDbType_fromTransformNum(objType->elementTransformNum);
48 if (!objType->elementDbType)
13049 return -1;
50 Py_INCREF(objType->elementDbType);
51 if (info.elementTypeInfo.objectType) {
52 objType->elementObjectType = cxoObjectType_new(connection,
53 info.elementTypeInfo.objectType);
54 if (!objType->elementObjectType)
55 return -1;
56 }
13157 }
13258
13359 // allocate the attribute list (temporary and permanent) and dictionary
15985 }
16086 PyList_SET_ITEM(objType->attributes, i, (PyObject*) attr);
16187 if (PyDict_SetItem(objType->attributesByName, attr->name,
162 (PyObject*) attr) < 0)
88 (PyObject*) attr) < 0) {
89 PyMem_Free(attributes);
16390 return -1;
91 }
16492 }
16593 PyMem_Free(attributes);
16694 return 0;
236164 Py_CLEAR(objType->name);
237165 Py_CLEAR(objType->attributes);
238166 Py_CLEAR(objType->attributesByName);
239 Py_CLEAR(objType->elementType);
167 Py_CLEAR(objType->elementObjectType);
168 Py_CLEAR(objType->elementDbType);
240169 Py_TYPE(objType)->tp_free((PyObject*) objType);
170 }
171
172
173 //-----------------------------------------------------------------------------
174 // cxoObjectType_getElementType()
175 // Return the element type associated with a collection. This is either an
176 // object type or one of the database type constants. If the object type is not
177 // a collection, None is returned.
178 //-----------------------------------------------------------------------------
179 static PyObject *cxoObjectType_getElementType(cxoObjectType *type,
180 void *unused)
181 {
182 if (type->elementObjectType) {
183 Py_INCREF(type->elementObjectType);
184 return (PyObject*) type->elementObjectType;
185 }
186 if (type->elementDbType) {
187 Py_INCREF(type->elementDbType);
188 return (PyObject*) type->elementDbType;
189 }
190
191 Py_RETURN_NONE;
241192 }
242193
243194
260211
261212
262213 //-----------------------------------------------------------------------------
214 // cxoObjectType_richCompare()
215 // Peforms a comparison between the object type and another Python object.
216 // Equality (and inequality) are suppported to match object types; no other
217 // operations are supported.
218 //-----------------------------------------------------------------------------
219 static PyObject *cxoObjectType_richCompare(cxoObjectType* objType,
220 PyObject* otherObj, int op)
221 {
222 cxoObjectType *otherObjType;
223 int status, equal = 0;
224
225 // only equality and inequality can be checked
226 if (op != Py_EQ && op != Py_NE) {
227 Py_INCREF(Py_NotImplemented);
228 return Py_NotImplemented;
229 }
230
231 // check to see if the other object is an object type, too
232 status = PyObject_IsInstance(otherObj, (PyObject*) &cxoPyTypeObjectType);
233 if (status < 0)
234 return NULL;
235 if (status == 1) {
236 otherObjType = (cxoObjectType*) otherObj;
237 if (otherObjType->connection == objType->connection ||
238 otherObjType->connection->sessionPool ==
239 objType->connection->sessionPool) {
240 equal = PyObject_RichCompareBool(otherObjType->schema,
241 objType->schema, Py_EQ);
242 if (equal < 0)
243 return NULL;
244 if (equal) {
245 equal = PyObject_RichCompareBool(otherObjType->name,
246 objType->name, Py_EQ);
247 if (equal < 0)
248 return NULL;
249 }
250 }
251 }
252
253 // determine return value
254 if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) {
255 Py_RETURN_TRUE;
256 }
257 Py_RETURN_FALSE;
258 }
259
260
261 //-----------------------------------------------------------------------------
263262 // cxoObjectType_newObject()
264263 // Factory function for creating objects of the type which can be bound.
265264 //-----------------------------------------------------------------------------
299298 return (PyObject*) obj;
300299 }
301300
301
302 //-----------------------------------------------------------------------------
303 // declaration of methods
304 //-----------------------------------------------------------------------------
305 static PyMethodDef cxoMethods[] = {
306 { "newobject", (PyCFunction) cxoObjectType_newObject,
307 METH_VARARGS | METH_KEYWORDS },
308 { NULL }
309 };
310
311
312 //-----------------------------------------------------------------------------
313 // declaration of members
314 //-----------------------------------------------------------------------------
315 static PyMemberDef cxoMembers[] = {
316 { "schema", T_OBJECT, offsetof(cxoObjectType, schema), READONLY },
317 { "name", T_OBJECT, offsetof(cxoObjectType, name), READONLY },
318 { "attributes", T_OBJECT, offsetof(cxoObjectType, attributes), READONLY },
319 { "iscollection", T_BOOL, offsetof(cxoObjectType, isCollection),
320 READONLY },
321 { NULL }
322 };
323
324
325 //-----------------------------------------------------------------------------
326 // declaration of calculated members
327 //-----------------------------------------------------------------------------
328 static PyGetSetDef cxoCalcMembers[] = {
329 { "element_type", (getter) cxoObjectType_getElementType, 0, 0, 0 },
330 { NULL }
331 };
332
333
334 //-----------------------------------------------------------------------------
335 // Python type declarations
336 //-----------------------------------------------------------------------------
337 PyTypeObject cxoPyTypeObjectType = {
338 PyVarObject_HEAD_INIT(NULL, 0)
339 .tp_name = "cx_Oracle.ObjectType",
340 .tp_basicsize = sizeof(cxoObjectType),
341 .tp_dealloc = (destructor) cxoObjectType_free,
342 .tp_repr = (reprfunc) cxoObjectType_repr,
343 .tp_call = (ternaryfunc) cxoObjectType_newObject,
344 .tp_flags = Py_TPFLAGS_DEFAULT,
345 .tp_methods = cxoMethods,
346 .tp_members = cxoMembers,
347 .tp_getset = cxoCalcMembers,
348 .tp_richcompare = (richcmpfunc) cxoObjectType_richCompare
349 };
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoQueue.c
6 // Defines the routines for handling queues (advanced queuing). These queues
7 // permit sending and receiving messages defined by the database.
8 //-----------------------------------------------------------------------------
9
10 #include "cxoModule.h"
11
12 //-----------------------------------------------------------------------------
13 // cxoQueue_new()
14 // Create a new queue (advanced queuing).
15 //-----------------------------------------------------------------------------
16 cxoQueue *cxoQueue_new(cxoConnection *conn, dpiQueue *handle)
17 {
18 dpiDeqOptions *deqOptions;
19 dpiEnqOptions *enqOptions;
20 cxoQueue *queue;
21
22 // create queue and populate basic attributes
23 queue = (cxoQueue*) cxoPyTypeQueue.tp_alloc(&cxoPyTypeQueue, 0);
24 if (!queue) {
25 dpiQueue_release(handle);
26 return NULL;
27 }
28 Py_INCREF(conn);
29 queue->conn = conn;
30 queue->handle = handle;
31
32 // get dequeue options
33 if (dpiQueue_getDeqOptions(queue->handle, &deqOptions) < 0) {
34 cxoError_raiseAndReturnNull();
35 Py_DECREF(queue);
36 return NULL;
37 }
38 queue->deqOptions = (PyObject*) cxoDeqOptions_new(conn, deqOptions);
39 if (!queue->deqOptions) {
40 Py_DECREF(queue);
41 return NULL;
42 }
43
44 // get enqueue options
45 if (dpiQueue_getEnqOptions(queue->handle, &enqOptions) < 0) {
46 cxoError_raiseAndReturnNull();
47 Py_DECREF(queue);
48 return NULL;
49 }
50 queue->enqOptions = (PyObject*) cxoEnqOptions_new(conn, enqOptions);
51 if (!queue->enqOptions) {
52 Py_DECREF(queue);
53 return NULL;
54 }
55
56 return queue;
57 }
58
59
60 //-----------------------------------------------------------------------------
61 // cxoQueue_free()
62 // Free the memory associated with a queue.
63 //-----------------------------------------------------------------------------
64 static void cxoQueue_free(cxoQueue *queue)
65 {
66 if (queue->handle) {
67 dpiQueue_release(queue->handle);
68 queue->handle = NULL;
69 }
70 Py_CLEAR(queue->conn);
71 Py_CLEAR(queue->name);
72 Py_CLEAR(queue->payloadType);
73 Py_CLEAR(queue->deqOptions);
74 Py_CLEAR(queue->enqOptions);
75 Py_TYPE(queue)->tp_free((PyObject*) queue);
76 }
77
78
79 //-----------------------------------------------------------------------------
80 // cxoQueue_repr()
81 // Return a string representation of a queue.
82 //-----------------------------------------------------------------------------
83 static PyObject *cxoQueue_repr(cxoQueue *queue)
84 {
85 PyObject *module, *name, *result;
86
87 if (cxoUtils_getModuleAndName(Py_TYPE(queue), &module, &name) < 0)
88 return NULL;
89 result = cxoUtils_formatString("<%s.%s %r>",
90 PyTuple_Pack(3, module, name, queue->name));
91 Py_DECREF(module);
92 Py_DECREF(name);
93 return result;
94 }
95
96
97 //-----------------------------------------------------------------------------
98 // cxoQueue_deqHelper()
99 // Helper for dequeuing messages from a queue.
100 //-----------------------------------------------------------------------------
101 int cxoQueue_deqHelper(cxoQueue *queue, uint32_t *numProps,
102 cxoMsgProps **props)
103 {
104 uint32_t bufferLength, i, j;
105 dpiMsgProps **handles;
106 dpiObject *objHandle;
107 const char *buffer;
108 cxoMsgProps *temp;
109 cxoObject *obj;
110 int ok, status;
111
112 // use the same array to store the intermediate values provided by ODPI-C;
113 // by doing so there is no need to allocate an additional array and any
114 // values created by this helper routine are cleaned up on error
115 handles = (dpiMsgProps**) props;
116
117 // perform dequeue
118 Py_BEGIN_ALLOW_THREADS
119 status = dpiQueue_deqMany(queue->handle, numProps, handles);
120 Py_END_ALLOW_THREADS
121 if (status < 0)
122 return cxoError_raiseAndReturnInt();
123
124 // create objects that are returned to the user
125 for (i = 0; i < *numProps; i++) {
126
127 // create message property object
128 temp = cxoMsgProps_new(queue->conn, handles[i]);
129 ok = (temp) ? 1 : 0;
130 props[i] = temp;
131
132 // get payload from ODPI-C message property
133 if (ok && dpiMsgProps_getPayload(temp->handle, &objHandle, &buffer,
134 &bufferLength) < 0) {
135 cxoError_raiseAndReturnInt();
136 ok = 0;
137 }
138
139 // store payload on cx_Oracle message property
140 if (ok && objHandle) {
141 obj = (cxoObject*) cxoObject_new(queue->payloadType, objHandle);
142 if (obj && dpiObject_addRef(objHandle) < 0) {
143 cxoError_raiseAndReturnInt();
144 obj->handle = NULL;
145 Py_CLEAR(obj);
146 ok = 0;
147 }
148 temp->payload = (PyObject*) obj;
149 } else if (ok) {
150 temp->payload = PyBytes_FromStringAndSize(buffer, bufferLength);
151 }
152
153 // if an error occurred, do some cleanup
154 if (!ok || !temp->payload) {
155 Py_XDECREF(temp);
156 for (j = 0; j < i; j++)
157 Py_DECREF(props[j]);
158 for (j = i + 1; j < *numProps; j++)
159 dpiMsgProps_release(handles[j]);
160 return -1;
161 }
162
163 }
164
165 return 0;
166 }
167
168
169 //-----------------------------------------------------------------------------
170 // cxoQueue_enqHelper()
171 // Helper for enqueuing messages from a queue.
172 //-----------------------------------------------------------------------------
173 int cxoQueue_enqHelper(cxoQueue *queue, uint32_t numProps,
174 cxoMsgProps **props)
175 {
176 dpiMsgProps **handles, *tempHandle;
177 cxoBuffer buffer;
178 cxoObject *obj;
179 uint32_t i;
180 int status;
181
182 // use the same array to store the intermediate values required by ODPI-C;
183 // by doing so there is no need to allocate an additional array
184 handles = (dpiMsgProps**) props;
185
186 // process array
187 for (i = 0; i < numProps; i++) {
188
189 // verify that the message property object has a payload
190 if (!props[i]->payload || props[i]->payload == Py_None) {
191 cxoError_raiseFromString(cxoProgrammingErrorException,
192 "message has no payload");
193 return -1;
194 }
195
196 // transfer payload to message properties object
197 tempHandle = props[i]->handle;
198 if (PyObject_IsInstance(props[i]->payload,
199 (PyObject*) &cxoPyTypeObject)) {
200 obj = (cxoObject*) props[i]->payload;
201 if (dpiMsgProps_setPayloadObject(props[i]->handle,
202 obj->handle) < 0)
203 return cxoError_raiseAndReturnInt();
204 } else {
205 if (cxoBuffer_fromObject(&buffer, props[i]->payload,
206 props[i]->encoding) < 0)
207 return -1;
208 status = dpiMsgProps_setPayloadBytes(props[i]->handle, buffer.ptr,
209 buffer.size);
210 cxoBuffer_clear(&buffer);
211 if (status < 0)
212 return cxoError_raiseAndReturnInt();
213 }
214 handles[i] = tempHandle;
215
216 }
217
218 // perform enqueue
219 Py_BEGIN_ALLOW_THREADS
220 status = dpiQueue_enqMany(queue->handle, numProps, handles);
221 Py_END_ALLOW_THREADS
222 if (status < 0)
223 return cxoError_raiseAndReturnInt();
224
225 return 0;
226 }
227
228
229 //-----------------------------------------------------------------------------
230 // cxoQueue_deqMany()
231 // Dequeue a single message to the queue.
232 //-----------------------------------------------------------------------------
233 static PyObject *cxoQueue_deqMany(cxoQueue *queue, PyObject *args)
234 {
235 unsigned int numPropsFromPython;
236 uint32_t numProps, i;
237 cxoMsgProps **props;
238 PyObject *result;
239
240 if (!PyArg_ParseTuple(args, "I", &numPropsFromPython))
241 return NULL;
242 numProps = (uint32_t) numPropsFromPython;
243 props = PyMem_Malloc(numProps * sizeof(cxoMsgProps*));
244 if (!props)
245 return NULL;
246 if (cxoQueue_deqHelper(queue, &numProps, props) < 0) {
247 PyMem_Free(props);
248 return NULL;
249 }
250 result = PyList_New(numProps);
251 if (!result) {
252 for (i = 0; i < numProps; i++)
253 Py_DECREF(props[i]);
254 PyMem_Free(props);
255 return NULL;
256 }
257 for (i = 0; i < numProps; i++)
258 PyList_SET_ITEM(result, i, (PyObject*) props[i]);
259 PyMem_Free(props);
260 return result;
261 }
262
263
264 //-----------------------------------------------------------------------------
265 // cxoQueue_deqOne()
266 // Dequeue a single message to the queue.
267 //-----------------------------------------------------------------------------
268 static PyObject *cxoQueue_deqOne(cxoQueue *queue, PyObject *args)
269 {
270 uint32_t numProps = 1;
271 cxoMsgProps *props;
272
273 if (cxoQueue_deqHelper(queue, &numProps, &props) < 0)
274 return NULL;
275 if (numProps > 0)
276 return (PyObject*) props;
277 Py_RETURN_NONE;
278 }
279
280
281 //-----------------------------------------------------------------------------
282 // cxoQueue_enqMany()
283 // Enqueue multiple messages to the queue.
284 //-----------------------------------------------------------------------------
285 static PyObject *cxoQueue_enqMany(cxoQueue *queue, PyObject *args)
286 {
287 PyObject *seq, *seqCheck, *temp;
288 Py_ssize_t seqLength, i;
289 cxoMsgProps **props;
290 int status;
291
292 // validate arguments
293 if (!PyArg_ParseTuple(args, "O", &seqCheck))
294 return NULL;
295 seq = PySequence_Fast(seqCheck, "expecting sequence");
296 if (!seq)
297 return NULL;
298
299 // zero messages means nothing to do
300 seqLength = PySequence_Length(seq);
301 if (seqLength == 0) {
302 Py_DECREF(seq);
303 Py_RETURN_NONE;
304 }
305
306 // populate array of properties
307 props = PyMem_Malloc(seqLength * sizeof(cxoMsgProps*));
308 if (!props) {
309 PyErr_NoMemory();
310 Py_DECREF(seq);
311 return NULL;
312 }
313 for (i = 0; i < seqLength; i++) {
314 temp = PySequence_Fast_GET_ITEM(seq, i);
315 if (Py_TYPE(temp) != &cxoPyTypeMsgProps) {
316 Py_DECREF(seq);
317 PyMem_Free(props);
318 PyErr_SetString(PyExc_TypeError,
319 "expecting sequence of message property objects");
320 return NULL;
321 }
322 props[i] = (cxoMsgProps*) temp;
323 }
324
325 // perform enqueue
326 status = cxoQueue_enqHelper(queue, (uint32_t) seqLength, props);
327 Py_DECREF(seq);
328 PyMem_Free(props);
329 if (status < 0)
330 return NULL;
331
332 Py_RETURN_NONE;
333 }
334
335
336 //-----------------------------------------------------------------------------
337 // cxoQueue_enqOne()
338 // Enqueue a single message to the queue.
339 //-----------------------------------------------------------------------------
340 static PyObject *cxoQueue_enqOne(cxoQueue *queue, PyObject *args)
341 {
342 cxoMsgProps *props;
343
344 if (!PyArg_ParseTuple(args, "O!", &cxoPyTypeMsgProps, &props))
345 return NULL;
346 if (cxoQueue_enqHelper(queue, 1, &props) < 0)
347 return NULL;
348
349 Py_RETURN_NONE;
350 }
351
352
353 //-----------------------------------------------------------------------------
354 // declaration of methods
355 //-----------------------------------------------------------------------------
356 static PyMethodDef cxoMethods[] = {
357 { "deqMany", (PyCFunction) cxoQueue_deqMany, METH_VARARGS },
358 { "deqOne", (PyCFunction) cxoQueue_deqOne, METH_NOARGS },
359 { "enqMany", (PyCFunction) cxoQueue_enqMany, METH_VARARGS },
360 { "enqOne", (PyCFunction) cxoQueue_enqOne, METH_VARARGS },
361 { NULL }
362 };
363
364
365 //-----------------------------------------------------------------------------
366 // declaration of members
367 //-----------------------------------------------------------------------------
368 static PyMemberDef cxoMembers[] = {
369 { "connection", T_OBJECT, offsetof(cxoQueue, conn), READONLY },
370 { "deqOptions", T_OBJECT, offsetof(cxoQueue, deqOptions), READONLY },
371 { "enqOptions", T_OBJECT, offsetof(cxoQueue, enqOptions), READONLY },
372 { "name", T_OBJECT, offsetof(cxoQueue, name), READONLY },
373 { "payloadType", T_OBJECT, offsetof(cxoQueue, payloadType), READONLY },
374 { NULL }
375 };
376
377
378 //-----------------------------------------------------------------------------
379 // Python type declarations
380 //-----------------------------------------------------------------------------
381 PyTypeObject cxoPyTypeQueue = {
382 PyVarObject_HEAD_INIT(NULL, 0)
383 .tp_name = "cx_Oracle.Queue",
384 .tp_basicsize = sizeof(cxoQueue),
385 .tp_dealloc = (destructor) cxoQueue_free,
386 .tp_repr = (reprfunc) cxoQueue_repr,
387 .tp_flags = Py_TPFLAGS_DEFAULT,
388 .tp_methods = cxoMethods,
389 .tp_members = cxoMembers
390 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // functions for the Python type "SessionPool"
18 //-----------------------------------------------------------------------------
19 static PyObject *cxoSessionPool_new(PyTypeObject*, PyObject*, PyObject*);
20 static int cxoSessionPool_init(cxoSessionPool*, PyObject*, PyObject*);
21 static void cxoSessionPool_free(cxoSessionPool*);
22 static PyObject *cxoSessionPool_acquire(cxoSessionPool*, PyObject*, PyObject*);
23 static PyObject *cxoSessionPool_close(cxoSessionPool*, PyObject*, PyObject*);
24 static PyObject *cxoSessionPool_drop(cxoSessionPool*, PyObject*);
25 static PyObject *cxoSessionPool_release(cxoSessionPool*, PyObject*, PyObject*);
26 static PyObject *cxoSessionPool_getBusyCount(cxoSessionPool*, void*);
27 static PyObject *cxoSessionPool_getGetMode(cxoSessionPool*, void*);
28 static PyObject *cxoSessionPool_getMaxLifetimeSession(cxoSessionPool*, void*);
29 static PyObject *cxoSessionPool_getOpenCount(cxoSessionPool*, void*);
30 static PyObject *cxoSessionPool_getStmtCacheSize(cxoSessionPool*, void*);
31 static PyObject *cxoSessionPool_getTimeout(cxoSessionPool*, void*);
32 static PyObject *cxoSessionPool_getWaitTimeout(cxoSessionPool*, void*);
33 static int cxoSessionPool_setGetMode(cxoSessionPool*, PyObject*, void*);
34 static int cxoSessionPool_setMaxLifetimeSession(cxoSessionPool*, PyObject*,
35 void*);
36 static int cxoSessionPool_setStmtCacheSize(cxoSessionPool*, PyObject*, void*);
37 static int cxoSessionPool_setTimeout(cxoSessionPool*, PyObject*, void*);
38 static int cxoSessionPool_setWaitTimeout(cxoSessionPool*, PyObject*, void*);
39
40
41 //-----------------------------------------------------------------------------
42 // declaration of methods for Python type "SessionPool"
43 //-----------------------------------------------------------------------------
44 static PyMethodDef cxoSessionPoolMethods[] = {
17 // cxoSessionPool_new()
18 // Create a new session pool object.
19 //-----------------------------------------------------------------------------
20 static PyObject *cxoSessionPool_new(PyTypeObject *type, PyObject *args,
21 PyObject *keywordArgs)
22 {
23 return type->tp_alloc(type, 0);
24 }
25
26
27 //-----------------------------------------------------------------------------
28 // cxoSessionPool_init()
29 // Initialize the session pool object.
30 //-----------------------------------------------------------------------------
31 static int cxoSessionPool_init(cxoSessionPool *pool, PyObject *args,
32 PyObject *keywordArgs)
33 {
34 uint32_t minSessions, maxSessions, sessionIncrement, maxSessionsPerShard;
35 cxoBuffer userNameBuffer, passwordBuffer, dsnBuffer, editionBuffer;
36 PyObject *threadedObj, *eventsObj, *homogeneousObj, *passwordObj;
37 PyObject *usernameObj, *dsnObj, *sessionCallbackObj;
38 PyObject *externalAuthObj, *editionObj;
39 dpiCommonCreateParams dpiCommonParams;
40 dpiPoolCreateParams dpiCreateParams;
41 cxoBuffer sessionCallbackBuffer;
42 PyTypeObject *connectionType;
43 const char *encoding;
44 int status, temp;
45
46 // define keyword arguments
47 static char *keywordList[] = { "user", "password", "dsn", "min", "max",
48 "increment", "connectiontype", "threaded", "getmode", "events",
49 "homogeneous", "externalauth", "encoding", "nencoding", "edition",
50 "timeout", "waitTimeout", "maxLifetimeSession", "sessionCallback",
51 "maxSessionsPerShard", NULL };
52
53 // parse arguments and keywords
54 usernameObj = passwordObj = dsnObj = editionObj = Py_None;
55 externalAuthObj = sessionCallbackObj = NULL;
56 threadedObj = eventsObj = homogeneousObj = passwordObj = NULL;
57 connectionType = &cxoPyTypeConnection;
58 minSessions = 1;
59 maxSessions = 2;
60 sessionIncrement = 1;
61 maxSessionsPerShard = 0;
62 if (cxoUtils_initializeDPI(NULL) < 0)
63 return -1;
64 if (dpiContext_initCommonCreateParams(cxoDpiContext, &dpiCommonParams) < 0)
65 return cxoError_raiseAndReturnInt();
66 if (dpiContext_initPoolCreateParams(cxoDpiContext, &dpiCreateParams) < 0)
67 return cxoError_raiseAndReturnInt();
68 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs,
69 "|OOOiiiOObOOOssOiiiOi", keywordList, &usernameObj, &passwordObj,
70 &dsnObj, &minSessions, &maxSessions, &sessionIncrement,
71 &connectionType, &threadedObj, &dpiCreateParams.getMode,
72 &eventsObj, &homogeneousObj, &externalAuthObj,
73 &dpiCommonParams.encoding, &dpiCommonParams.nencoding, &editionObj,
74 &dpiCreateParams.timeout, &dpiCreateParams.waitTimeout,
75 &dpiCreateParams.maxLifetimeSession, &sessionCallbackObj,
76 &maxSessionsPerShard))
77 return -1;
78 if (!PyType_Check(connectionType)) {
79 cxoError_raiseFromString(cxoProgrammingErrorException,
80 "connectiontype must be a type");
81 return -1;
82 }
83 if (!PyType_IsSubtype(connectionType, &cxoPyTypeConnection)) {
84 cxoError_raiseFromString(cxoProgrammingErrorException,
85 "connectiontype must be a subclass of Connection");
86 return -1;
87 }
88 if (cxoUtils_getBooleanValue(threadedObj, 0, &temp) < 0)
89 return -1;
90 if (temp)
91 dpiCommonParams.createMode |= DPI_MODE_CREATE_THREADED;
92 if (cxoUtils_getBooleanValue(eventsObj, 0, &temp) < 0)
93 return -1;
94 if (temp)
95 dpiCommonParams.createMode |= DPI_MODE_CREATE_EVENTS;
96 if (cxoUtils_getBooleanValue(externalAuthObj, 0,
97 &dpiCreateParams.externalAuth) < 0)
98 return -1;
99 if (cxoUtils_getBooleanValue(homogeneousObj, 1,
100 &dpiCreateParams.homogeneous) < 0)
101 return -1;
102
103 // initialize the object's members
104 Py_INCREF(connectionType);
105 pool->connectionType = connectionType;
106 Py_INCREF(dsnObj);
107 pool->dsn = dsnObj;
108 Py_INCREF(usernameObj);
109 pool->username = usernameObj;
110 pool->minSessions = minSessions;
111 pool->maxSessions = maxSessions;
112 pool->sessionIncrement = sessionIncrement;
113 pool->homogeneous = dpiCreateParams.homogeneous;
114 pool->externalAuth = dpiCreateParams.externalAuth;
115 Py_XINCREF(sessionCallbackObj);
116 pool->sessionCallback = sessionCallbackObj;
117
118 // populate parameters
119 encoding = cxoUtils_getAdjustedEncoding(dpiCommonParams.encoding);
120 cxoBuffer_init(&userNameBuffer);
121 cxoBuffer_init(&passwordBuffer);
122 cxoBuffer_init(&dsnBuffer);
123 cxoBuffer_init(&editionBuffer);
124 cxoBuffer_init(&sessionCallbackBuffer);
125 if (sessionCallbackObj && !PyCallable_Check(sessionCallbackObj) &&
126 cxoBuffer_fromObject(&sessionCallbackBuffer, sessionCallbackObj,
127 encoding) < 0)
128 return -1;
129 if (cxoBuffer_fromObject(&userNameBuffer, usernameObj, encoding) < 0 ||
130 cxoBuffer_fromObject(&passwordBuffer, passwordObj, encoding) < 0 ||
131 cxoBuffer_fromObject(&dsnBuffer, dsnObj, encoding) < 0 ||
132 cxoBuffer_fromObject(&editionBuffer, editionObj, encoding) < 0) {
133 cxoBuffer_clear(&userNameBuffer);
134 cxoBuffer_clear(&passwordBuffer);
135 cxoBuffer_clear(&dsnBuffer);
136 cxoBuffer_clear(&sessionCallbackBuffer);
137 return -1;
138 }
139 dpiCreateParams.minSessions = minSessions;
140 dpiCreateParams.maxSessions = maxSessions;
141 dpiCreateParams.sessionIncrement = sessionIncrement;
142 dpiCreateParams.plsqlFixupCallback = sessionCallbackBuffer.ptr;
143 dpiCreateParams.plsqlFixupCallbackLength = sessionCallbackBuffer.size;
144 dpiCreateParams.maxSessionsPerShard = maxSessionsPerShard;
145 dpiCommonParams.edition = editionBuffer.ptr;
146 dpiCommonParams.editionLength = editionBuffer.size;
147
148 // create pool
149 Py_BEGIN_ALLOW_THREADS
150 status = dpiPool_create(cxoDpiContext, userNameBuffer.ptr,
151 userNameBuffer.size, passwordBuffer.ptr, passwordBuffer.size,
152 dsnBuffer.ptr, dsnBuffer.size, &dpiCommonParams, &dpiCreateParams,
153 &pool->handle);
154 Py_END_ALLOW_THREADS
155 cxoBuffer_clear(&userNameBuffer);
156 cxoBuffer_clear(&passwordBuffer);
157 cxoBuffer_clear(&dsnBuffer);
158 cxoBuffer_clear(&editionBuffer);
159 if (status < 0)
160 return cxoError_raiseAndReturnInt();
161
162 // get encodings and name
163 if (dpiPool_getEncodingInfo(pool->handle, &pool->encodingInfo) < 0)
164 return cxoError_raiseAndReturnInt();
165 pool->encodingInfo.encoding =
166 cxoUtils_getAdjustedEncoding(pool->encodingInfo.encoding);
167 pool->encodingInfo.nencoding =
168 cxoUtils_getAdjustedEncoding(pool->encodingInfo.nencoding);
169 pool->name = PyUnicode_Decode(dpiCreateParams.outPoolName,
170 dpiCreateParams.outPoolNameLength, pool->encodingInfo.encoding,
171 NULL);
172 if (!pool->name)
173 return -1;
174
175 return 0;
176 }
177
178
179 //-----------------------------------------------------------------------------
180 // cxoSessionPool_free()
181 // Deallocate the session pool.
182 //-----------------------------------------------------------------------------
183 static void cxoSessionPool_free(cxoSessionPool *pool)
184 {
185 if (pool->handle) {
186 dpiPool_release(pool->handle);
187 pool->handle = NULL;
188 }
189 Py_CLEAR(pool->username);
190 Py_CLEAR(pool->dsn);
191 Py_CLEAR(pool->name);
192 Py_CLEAR(pool->sessionCallback);
193 Py_TYPE(pool)->tp_free((PyObject*) pool);
194 }
195
196
197 //-----------------------------------------------------------------------------
198 // cxoSessionPool_acquire()
199 // Create a new connection within the session pool.
200 //-----------------------------------------------------------------------------
201 static PyObject *cxoSessionPool_acquire(cxoSessionPool *pool, PyObject *args,
202 PyObject *keywordArgs)
203 {
204 static char *keywordList[] = { "user", "password", "cclass", "purity",
205 "tag", "matchanytag", "shardingkey", "supershardingkey", NULL };
206 PyObject *createKeywordArgs, *result, *cclassObj, *purityObj, *tagObj;
207 PyObject *shardingKeyObj, *superShardingKeyObj;
208 Py_ssize_t usernameLength, passwordLength;
209 char *username, *password;
210 PyObject *matchAnyTagObj;
211
212 // parse arguments
213 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|s#s#OOOOOO",
214 keywordList, &username, &usernameLength, &password,
215 &passwordLength, &cclassObj, &purityObj, &tagObj, &matchAnyTagObj,
216 &shardingKeyObj, &superShardingKeyObj))
217 return NULL;
218
219 // create arguments
220 if (keywordArgs)
221 createKeywordArgs = PyDict_Copy(keywordArgs);
222 else createKeywordArgs = PyDict_New();
223 if (!createKeywordArgs)
224 return NULL;
225 if (PyDict_SetItemString(createKeywordArgs, "pool",
226 (PyObject*) pool) < 0) {
227 Py_DECREF(createKeywordArgs);
228 return NULL;
229 }
230
231 // create the connection object
232 result = PyObject_Call( (PyObject*) pool->connectionType, args,
233 createKeywordArgs);
234 Py_DECREF(createKeywordArgs);
235
236 return result;
237 }
238
239
240 //-----------------------------------------------------------------------------
241 // cxoSessionPool_close()
242 // Close the session pool and make it unusable.
243 //-----------------------------------------------------------------------------
244 static PyObject *cxoSessionPool_close(cxoSessionPool *pool, PyObject *args,
245 PyObject *keywordArgs)
246 {
247 static char *keywordList[] = { "force", NULL };
248 PyObject *forceObj;
249 uint32_t closeMode;
250 int temp, status;
251
252 // parse arguments
253 forceObj = NULL;
254 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|O", keywordList,
255 &forceObj))
256 return NULL;
257 if (cxoUtils_getBooleanValue(forceObj, 0, &temp) < 0)
258 return NULL;
259 closeMode = (temp) ? DPI_MODE_POOL_CLOSE_FORCE :
260 DPI_MODE_POOL_CLOSE_DEFAULT;
261
262 // close pool
263 Py_BEGIN_ALLOW_THREADS
264 status = dpiPool_close(pool->handle, closeMode);
265 Py_END_ALLOW_THREADS
266 if (status < 0)
267 return cxoError_raiseAndReturnNull();
268
269 Py_RETURN_NONE;
270 }
271
272
273 //-----------------------------------------------------------------------------
274 // cxoSessionPool_drop()
275 // Release a connection back to the session pool, dropping it so that a new
276 // connection will be created if needed.
277 //-----------------------------------------------------------------------------
278 static PyObject *cxoSessionPool_drop(cxoSessionPool *pool, PyObject *args)
279 {
280 cxoConnection *connection;
281 int status;
282
283 // connection is expected
284 if (!PyArg_ParseTuple(args, "O!", &cxoPyTypeConnection, &connection))
285 return NULL;
286
287 // release the connection
288 Py_BEGIN_ALLOW_THREADS
289 status = dpiConn_close(connection->handle, DPI_MODE_CONN_CLOSE_DROP, NULL,
290 0);
291 Py_END_ALLOW_THREADS
292 if (status < 0)
293 return cxoError_raiseAndReturnNull();
294
295 // mark connection as closed
296 Py_CLEAR(connection->sessionPool);
297 dpiConn_release(connection->handle);
298 connection->handle = NULL;
299 Py_RETURN_NONE;
300 }
301
302
303 //-----------------------------------------------------------------------------
304 // cxoSessionPool_release()
305 // Release a connection back to the session pool.
306 //-----------------------------------------------------------------------------
307 static PyObject *cxoSessionPool_release(cxoSessionPool *pool, PyObject *args,
308 PyObject *keywordArgs)
309 {
310 static char *keywordList[] = { "connection", "tag", NULL };
311 cxoConnection *conn;
312 cxoBuffer tagBuffer;
313 PyObject *tagObj;
314 uint32_t mode;
315 int status;
316
317 // parse arguments
318 tagObj = NULL;
319 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!|O",
320 keywordList, &cxoPyTypeConnection, &conn, &tagObj))
321 return NULL;
322 if (!tagObj)
323 tagObj = conn->tag;
324 if (cxoBuffer_fromObject(&tagBuffer, tagObj,
325 pool->encodingInfo.encoding) < 0)
326 return NULL;
327 mode = DPI_MODE_CONN_CLOSE_DEFAULT;
328 if (tagObj && tagObj != Py_None)
329 mode |= DPI_MODE_CONN_CLOSE_RETAG;
330 Py_BEGIN_ALLOW_THREADS
331 status = dpiConn_close(conn->handle, mode, (char*) tagBuffer.ptr,
332 tagBuffer.size);
333 Py_END_ALLOW_THREADS
334 cxoBuffer_clear(&tagBuffer);
335 if (status < 0)
336 return cxoError_raiseAndReturnNull();
337
338 // mark connection as closed
339 Py_CLEAR(conn->sessionPool);
340 dpiConn_release(conn->handle);
341 conn->handle = NULL;
342 Py_RETURN_NONE;
343 }
344
345
346 //-----------------------------------------------------------------------------
347 // cxoSessionPool_getAttribute()
348 // Return the value for the attribute.
349 //-----------------------------------------------------------------------------
350 static PyObject *cxoSessionPool_getAttribute(cxoSessionPool *pool,
351 int (*func)(dpiPool *pool, uint32_t *value))
352 {
353 uint32_t value;
354
355 if ((*func)(pool->handle, &value) < 0)
356 return cxoError_raiseAndReturnNull();
357 return PyLong_FromUnsignedLong(value);
358 }
359
360
361 //-----------------------------------------------------------------------------
362 // cxoSessionPool_setAttribute()
363 // Set the value of the OCI attribute.
364 //-----------------------------------------------------------------------------
365 static int cxoSessionPool_setAttribute(cxoSessionPool *pool, PyObject *value,
366 int (*func)(dpiPool *pool, uint32_t value))
367 {
368 uint32_t cValue;
369
370 if (!PyLong_Check(value)) {
371 PyErr_SetString(PyExc_TypeError, "value must be an integer");
372 return -1;
373 }
374 cValue = PyLong_AsUnsignedLong(value);
375 if (PyErr_Occurred())
376 return -1;
377 if ((*func)(pool->handle, cValue) < 0)
378 return cxoError_raiseAndReturnInt();
379
380 return 0;
381 }
382
383
384 //-----------------------------------------------------------------------------
385 // cxoSessionPool_getBusyCount()
386 // Return the number of busy connections in the session pool.
387 //-----------------------------------------------------------------------------
388 static PyObject *cxoSessionPool_getBusyCount(cxoSessionPool *pool,
389 void *unused)
390 {
391 return cxoSessionPool_getAttribute(pool, dpiPool_getBusyCount);
392 }
393
394
395 //-----------------------------------------------------------------------------
396 // cxoSessionPool_getGetMode()
397 // Return the "get" mode for connections in the session pool.
398 //-----------------------------------------------------------------------------
399 static PyObject *cxoSessionPool_getGetMode(cxoSessionPool *pool, void *unused)
400 {
401 dpiPoolGetMode value;
402
403 if (dpiPool_getGetMode(pool->handle, &value) < 0)
404 return cxoError_raiseAndReturnNull();
405 return PyLong_FromLong(value);
406 }
407
408
409 //-----------------------------------------------------------------------------
410 // cxoSessionPool_getMaxLifetimeSession()
411 // Return the maximum lifetime session of connections in the session pool.
412 //-----------------------------------------------------------------------------
413 static PyObject *cxoSessionPool_getMaxLifetimeSession(cxoSessionPool *pool,
414 void *unused)
415 {
416 return cxoSessionPool_getAttribute(pool, dpiPool_getMaxLifetimeSession);
417 }
418
419
420 //-----------------------------------------------------------------------------
421 // cxoSessionPool_getOpenCount()
422 // Return the number of open connections in the session pool.
423 //-----------------------------------------------------------------------------
424 static PyObject *cxoSessionPool_getOpenCount(cxoSessionPool *pool, void *unused)
425 {
426 return cxoSessionPool_getAttribute(pool, dpiPool_getOpenCount);
427 }
428
429
430 //-----------------------------------------------------------------------------
431 // cxoSessionPool_getStmtCacheSize()
432 // Return the size of the statement cache to use in connections that are
433 // acquired from the pool.
434 //-----------------------------------------------------------------------------
435 static PyObject *cxoSessionPool_getStmtCacheSize(cxoSessionPool *pool,
436 void *unused)
437 {
438 return cxoSessionPool_getAttribute(pool, dpiPool_getStmtCacheSize);
439 }
440
441
442 //-----------------------------------------------------------------------------
443 // cxoSessionPool_getTimeout()
444 // Return the timeout for connections in the session pool.
445 //-----------------------------------------------------------------------------
446 static PyObject *cxoSessionPool_getTimeout(cxoSessionPool *pool, void *unused)
447 {
448 return cxoSessionPool_getAttribute(pool, dpiPool_getTimeout);
449 }
450
451
452 //-----------------------------------------------------------------------------
453 // cxoSessionPool_getWaitTimeout()
454 // Return the wait timeout for connections in the session pool.
455 //-----------------------------------------------------------------------------
456 static PyObject *cxoSessionPool_getWaitTimeout(cxoSessionPool *pool,
457 void *unused)
458 {
459 return cxoSessionPool_getAttribute(pool, dpiPool_getWaitTimeout);
460 }
461
462
463 //-----------------------------------------------------------------------------
464 // cxoSessionPool_setGetMode()
465 // Set the "get" mode for connections in the session pool.
466 //-----------------------------------------------------------------------------
467 static int cxoSessionPool_setGetMode(cxoSessionPool *pool, PyObject *value,
468 void *unused)
469 {
470 dpiPoolGetMode cValue;
471
472 cValue = PyLong_AsLong(value);
473 if (PyErr_Occurred())
474 return -1;
475 if (dpiPool_setGetMode(pool->handle, cValue) < 0)
476 return cxoError_raiseAndReturnInt();
477
478 return 0;
479 }
480
481
482 //-----------------------------------------------------------------------------
483 // cxoSessionPool_setMaxLifetimeSession()
484 // Set the maximum lifetime for connections in the session pool.
485 //-----------------------------------------------------------------------------
486 static int cxoSessionPool_setMaxLifetimeSession(cxoSessionPool *pool,
487 PyObject *value, void *unused)
488 {
489 return cxoSessionPool_setAttribute(pool, value,
490 dpiPool_setMaxLifetimeSession);
491 }
492
493
494 //-----------------------------------------------------------------------------
495 // cxoSessionPool_setStmtCacheSize()
496 // Set the default size of the statement cache used for connections that are
497 // acquired from the pool.
498 //-----------------------------------------------------------------------------
499 static int cxoSessionPool_setStmtCacheSize(cxoSessionPool *pool,
500 PyObject *value, void *unused)
501 {
502 return cxoSessionPool_setAttribute(pool, value, dpiPool_setStmtCacheSize);
503 }
504
505
506 //-----------------------------------------------------------------------------
507 // cxoSessionPool_setTimeout()
508 // Set the timeout for connections in the session pool.
509 //-----------------------------------------------------------------------------
510 static int cxoSessionPool_setTimeout(cxoSessionPool *pool, PyObject *value,
511 void *unused)
512 {
513 return cxoSessionPool_setAttribute(pool, value, dpiPool_setTimeout);
514 }
515
516
517 //-----------------------------------------------------------------------------
518 // cxoSessionPool_setWaitTimeout()
519 // Set the wait timeout for connections in the session pool.
520 //-----------------------------------------------------------------------------
521 static int cxoSessionPool_setWaitTimeout(cxoSessionPool *pool, PyObject *value,
522 void *unused)
523 {
524 return cxoSessionPool_setAttribute(pool, value, dpiPool_setWaitTimeout);
525 }
526
527
528 //-----------------------------------------------------------------------------
529 // declaration of methods for Python type
530 //-----------------------------------------------------------------------------
531 static PyMethodDef cxoMethods[] = {
45532 { "acquire", (PyCFunction) cxoSessionPool_acquire,
46533 METH_VARARGS | METH_KEYWORDS },
47534 { "close", (PyCFunction) cxoSessionPool_close,
54541
55542
56543 //-----------------------------------------------------------------------------
57 // declaration of members for Python type "SessionPool"
58 //-----------------------------------------------------------------------------
59 static PyMemberDef cxoSessionPoolMembers[] = {
544 // declaration of members for Python type
545 //-----------------------------------------------------------------------------
546 static PyMemberDef cxoMembers[] = {
60547 { "username", T_OBJECT, offsetof(cxoSessionPool, username), READONLY },
61548 { "dsn", T_OBJECT, offsetof(cxoSessionPool, dsn), READONLY },
62549 { "tnsentry", T_OBJECT, offsetof(cxoSessionPool, dsn), READONLY },
71558
72559
73560 //-----------------------------------------------------------------------------
74 // declaration of calculated members for Python type "SessionPool"
75 //-----------------------------------------------------------------------------
76 static PyGetSetDef cxoSessionPoolCalcMembers[] = {
561 // declaration of calculated members for Python type
562 //-----------------------------------------------------------------------------
563 static PyGetSetDef cxoCalcMembers[] = {
77564 { "opened", (getter) cxoSessionPool_getOpenCount, 0, 0, 0 },
78565 { "busy", (getter) cxoSessionPool_getBusyCount, 0, 0, 0 },
79566 { "timeout", (getter) cxoSessionPool_getTimeout,
91578
92579
93580 //-----------------------------------------------------------------------------
94 // declaration of Python type "SessionPool"
581 // declaration of Python type
95582 //-----------------------------------------------------------------------------
96583 PyTypeObject cxoPyTypeSessionPool = {
97584 PyVarObject_HEAD_INIT(NULL, 0)
98 "cx_Oracle.SessionPool", // tp_name
99 sizeof(cxoSessionPool), // tp_basicsize
100 0, // tp_itemsize
101 (destructor) cxoSessionPool_free, // tp_dealloc
102 0, // tp_print
103 0, // tp_getattr
104 0, // tp_setattr
105 0, // tp_compare
106 0, // tp_repr
107 0, // tp_as_number
108 0, // tp_as_sequence
109 0, // tp_as_mapping
110 0, // tp_hash
111 0, // tp_call
112 0, // tp_str
113 0, // tp_getattro
114 0, // tp_setattro
115 0, // tp_as_buffer
116 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
117 // tp_flags
118 0, // tp_doc
119 0, // tp_traverse
120 0, // tp_clear
121 0, // tp_richcompare
122 0, // tp_weaklistoffset
123 0, // tp_iter
124 0, // tp_iternext
125 cxoSessionPoolMethods, // tp_methods
126 cxoSessionPoolMembers, // tp_members
127 cxoSessionPoolCalcMembers, // tp_getset
128 0, // tp_base
129 0, // tp_dict
130 0, // tp_descr_get
131 0, // tp_descr_set
132 0, // tp_dictoffset
133 (initproc) cxoSessionPool_init, // tp_init
134 0, // tp_alloc
135 (newfunc) cxoSessionPool_new, // tp_new
136 0, // tp_free
137 0, // tp_is_gc
138 0 // tp_bases
585 .tp_name = "cx_Oracle.SessionPool",
586 .tp_basicsize = sizeof(cxoSessionPool),
587 .tp_dealloc = (destructor) cxoSessionPool_free,
588 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
589 .tp_methods = cxoMethods,
590 .tp_members = cxoMembers,
591 .tp_getset = cxoCalcMembers,
592 .tp_init = (initproc) cxoSessionPool_init,
593 .tp_new = (newfunc) cxoSessionPool_new
139594 };
140
141
142 //-----------------------------------------------------------------------------
143 // cxoSessionPool_new()
144 // Create a new session pool object.
145 //-----------------------------------------------------------------------------
146 static PyObject *cxoSessionPool_new(PyTypeObject *type, PyObject *args,
147 PyObject *keywordArgs)
148 {
149 return type->tp_alloc(type, 0);
150 }
151
152
153 //-----------------------------------------------------------------------------
154 // cxoSessionPool_init()
155 // Initialize the session pool object.
156 //-----------------------------------------------------------------------------
157 static int cxoSessionPool_init(cxoSessionPool *pool, PyObject *args,
158 PyObject *keywordArgs)
159 {
160 cxoBuffer userNameBuffer, passwordBuffer, dsnBuffer, editionBuffer;
161 PyObject *threadedObj, *eventsObj, *homogeneousObj, *passwordObj;
162 PyObject *usernameObj, *dsnObj, *sessionCallbackObj;
163 uint32_t minSessions, maxSessions, sessionIncrement;
164 PyObject *externalAuthObj, *editionObj;
165 dpiCommonCreateParams dpiCommonParams;
166 dpiPoolCreateParams dpiCreateParams;
167 cxoBuffer sessionCallbackBuffer;
168 PyTypeObject *connectionType;
169 const char *encoding;
170 int status, temp;
171
172 // define keyword arguments
173 static char *keywordList[] = { "user", "password", "dsn", "min", "max",
174 "increment", "connectiontype", "threaded", "getmode", "events",
175 "homogeneous", "externalauth", "encoding", "nencoding", "edition",
176 "timeout", "waitTimeout", "maxLifetimeSession", "sessionCallback",
177 NULL };
178
179 // parse arguments and keywords
180 usernameObj = passwordObj = dsnObj = editionObj = Py_None;
181 externalAuthObj = sessionCallbackObj = NULL;
182 threadedObj = eventsObj = homogeneousObj = passwordObj = NULL;
183 connectionType = &cxoPyTypeConnection;
184 minSessions = 1;
185 maxSessions = 2;
186 sessionIncrement = 1;
187 if (cxoUtils_initializeDPI() < 0)
188 return -1;
189 if (dpiContext_initCommonCreateParams(cxoDpiContext, &dpiCommonParams) < 0)
190 return cxoError_raiseAndReturnInt();
191 dpiCommonParams.driverName = CXO_DRIVER_NAME;
192 dpiCommonParams.driverNameLength =
193 (uint32_t) strlen(dpiCommonParams.driverName);
194 if (dpiContext_initPoolCreateParams(cxoDpiContext, &dpiCreateParams) < 0)
195 return cxoError_raiseAndReturnInt();
196 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|OOOiiiOObOOOssOiiiO",
197 keywordList, &usernameObj, &passwordObj, &dsnObj, &minSessions,
198 &maxSessions, &sessionIncrement, &connectionType, &threadedObj,
199 &dpiCreateParams.getMode, &eventsObj, &homogeneousObj,
200 &externalAuthObj, &dpiCommonParams.encoding,
201 &dpiCommonParams.nencoding, &editionObj, &dpiCreateParams.timeout,
202 &dpiCreateParams.waitTimeout, &dpiCreateParams.maxLifetimeSession,
203 &sessionCallbackObj))
204 return -1;
205 if (!PyType_Check(connectionType)) {
206 cxoError_raiseFromString(cxoProgrammingErrorException,
207 "connectiontype must be a type");
208 return -1;
209 }
210 if (!PyType_IsSubtype(connectionType, &cxoPyTypeConnection)) {
211 cxoError_raiseFromString(cxoProgrammingErrorException,
212 "connectiontype must be a subclass of Connection");
213 return -1;
214 }
215 if (cxoUtils_getBooleanValue(threadedObj, 0, &temp) < 0)
216 return -1;
217 if (temp)
218 dpiCommonParams.createMode |= DPI_MODE_CREATE_THREADED;
219 if (cxoUtils_getBooleanValue(eventsObj, 0, &temp) < 0)
220 return -1;
221 if (temp)
222 dpiCommonParams.createMode |= DPI_MODE_CREATE_EVENTS;
223 if (cxoUtils_getBooleanValue(externalAuthObj, 0,
224 &dpiCreateParams.externalAuth) < 0)
225 return -1;
226 if (cxoUtils_getBooleanValue(homogeneousObj, 1,
227 &dpiCreateParams.homogeneous) < 0)
228 return -1;
229
230 // initialize the object's members
231 Py_INCREF(connectionType);
232 pool->connectionType = connectionType;
233 Py_INCREF(dsnObj);
234 pool->dsn = dsnObj;
235 Py_INCREF(usernameObj);
236 pool->username = usernameObj;
237 pool->minSessions = minSessions;
238 pool->maxSessions = maxSessions;
239 pool->sessionIncrement = sessionIncrement;
240 pool->homogeneous = dpiCreateParams.homogeneous;
241 pool->externalAuth = dpiCreateParams.externalAuth;
242 Py_XINCREF(sessionCallbackObj);
243 pool->sessionCallback = sessionCallbackObj;
244
245 // populate parameters
246 encoding = cxoUtils_getAdjustedEncoding(dpiCommonParams.encoding);
247 cxoBuffer_init(&userNameBuffer);
248 cxoBuffer_init(&passwordBuffer);
249 cxoBuffer_init(&dsnBuffer);
250 cxoBuffer_init(&editionBuffer);
251 cxoBuffer_init(&sessionCallbackBuffer);
252 if (sessionCallbackObj && !PyCallable_Check(sessionCallbackObj) &&
253 cxoBuffer_fromObject(&sessionCallbackBuffer, sessionCallbackObj,
254 encoding) < 0)
255 return -1;
256 if (cxoBuffer_fromObject(&userNameBuffer, usernameObj, encoding) < 0 ||
257 cxoBuffer_fromObject(&passwordBuffer, passwordObj, encoding) < 0 ||
258 cxoBuffer_fromObject(&dsnBuffer, dsnObj, encoding) < 0 ||
259 cxoBuffer_fromObject(&editionBuffer, editionObj, encoding) < 0) {
260 cxoBuffer_clear(&userNameBuffer);
261 cxoBuffer_clear(&passwordBuffer);
262 cxoBuffer_clear(&dsnBuffer);
263 cxoBuffer_clear(&sessionCallbackBuffer);
264 return -1;
265 }
266 dpiCreateParams.minSessions = minSessions;
267 dpiCreateParams.maxSessions = maxSessions;
268 dpiCreateParams.sessionIncrement = sessionIncrement;
269 dpiCreateParams.plsqlFixupCallback = sessionCallbackBuffer.ptr;
270 dpiCreateParams.plsqlFixupCallbackLength = sessionCallbackBuffer.size;
271 dpiCommonParams.edition = editionBuffer.ptr;
272 dpiCommonParams.editionLength = editionBuffer.size;
273
274 // create pool
275 Py_BEGIN_ALLOW_THREADS
276 status = dpiPool_create(cxoDpiContext, userNameBuffer.ptr,
277 userNameBuffer.size, passwordBuffer.ptr, passwordBuffer.size,
278 dsnBuffer.ptr, dsnBuffer.size, &dpiCommonParams, &dpiCreateParams,
279 &pool->handle);
280 Py_END_ALLOW_THREADS
281 cxoBuffer_clear(&userNameBuffer);
282 cxoBuffer_clear(&passwordBuffer);
283 cxoBuffer_clear(&dsnBuffer);
284 cxoBuffer_clear(&editionBuffer);
285 if (status < 0)
286 return cxoError_raiseAndReturnInt();
287
288 // get encodings and name
289 if (dpiPool_getEncodingInfo(pool->handle, &pool->encodingInfo) < 0)
290 return cxoError_raiseAndReturnInt();
291 pool->encodingInfo.encoding =
292 cxoUtils_getAdjustedEncoding(pool->encodingInfo.encoding);
293 pool->encodingInfo.nencoding =
294 cxoUtils_getAdjustedEncoding(pool->encodingInfo.nencoding);
295 pool->name = cxoPyString_fromEncodedString(dpiCreateParams.outPoolName,
296 dpiCreateParams.outPoolNameLength, pool->encodingInfo.encoding,
297 NULL);
298 if (!pool->name)
299 return -1;
300
301 return 0;
302 }
303
304
305 //-----------------------------------------------------------------------------
306 // cxoSessionPool_free()
307 // Deallocate the session pool.
308 //-----------------------------------------------------------------------------
309 static void cxoSessionPool_free(cxoSessionPool *pool)
310 {
311 if (pool->handle) {
312 dpiPool_release(pool->handle);
313 pool->handle = NULL;
314 }
315 Py_CLEAR(pool->username);
316 Py_CLEAR(pool->dsn);
317 Py_CLEAR(pool->name);
318 Py_CLEAR(pool->sessionCallback);
319 Py_TYPE(pool)->tp_free((PyObject*) pool);
320 }
321
322
323 //-----------------------------------------------------------------------------
324 // cxoSessionPool_acquire()
325 // Create a new connection within the session pool.
326 //-----------------------------------------------------------------------------
327 static PyObject *cxoSessionPool_acquire(cxoSessionPool *pool, PyObject *args,
328 PyObject *keywordArgs)
329 {
330 static char *keywordList[] = { "user", "password", "cclass", "purity",
331 "tag", "matchanytag", "shardingkey", "supershardingkey", NULL };
332 PyObject *createKeywordArgs, *result, *cclassObj, *purityObj, *tagObj;
333 PyObject *shardingKeyObj, *superShardingKeyObj;
334 unsigned usernameLength, passwordLength;
335 char *username, *password;
336 PyObject *matchAnyTagObj;
337
338 // parse arguments
339 username = NULL;
340 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|s#s#OOOOOO",
341 keywordList, &username, &usernameLength, &password,
342 &passwordLength, &cclassObj, &purityObj, &tagObj, &matchAnyTagObj,
343 &shardingKeyObj, &superShardingKeyObj))
344 return NULL;
345 if (pool->homogeneous && username)
346 return cxoError_raiseFromString(cxoProgrammingErrorException,
347 "pool is homogeneous. Proxy authentication is not possible.");
348
349 // create arguments
350 if (keywordArgs)
351 createKeywordArgs = PyDict_Copy(keywordArgs);
352 else createKeywordArgs = PyDict_New();
353 if (!createKeywordArgs)
354 return NULL;
355 if (PyDict_SetItemString(createKeywordArgs, "pool",
356 (PyObject*) pool) < 0) {
357 Py_DECREF(createKeywordArgs);
358 return NULL;
359 }
360
361 // create the connection object
362 result = PyObject_Call( (PyObject*) pool->connectionType, args,
363 createKeywordArgs);
364 Py_DECREF(createKeywordArgs);
365
366 return result;
367 }
368
369
370 //-----------------------------------------------------------------------------
371 // cxoSessionPool_close()
372 // Close the session pool and make it unusable.
373 //-----------------------------------------------------------------------------
374 static PyObject *cxoSessionPool_close(cxoSessionPool *pool, PyObject *args,
375 PyObject *keywordArgs)
376 {
377 static char *keywordList[] = { "force", NULL };
378 PyObject *forceObj;
379 uint32_t closeMode;
380 int temp, status;
381
382 // parse arguments
383 forceObj = NULL;
384 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|O", keywordList,
385 &forceObj))
386 return NULL;
387 if (cxoUtils_getBooleanValue(forceObj, 0, &temp) < 0)
388 return NULL;
389 closeMode = (temp) ? DPI_MODE_POOL_CLOSE_FORCE :
390 DPI_MODE_POOL_CLOSE_DEFAULT;
391
392 // close pool
393 Py_BEGIN_ALLOW_THREADS
394 status = dpiPool_close(pool->handle, closeMode);
395 Py_END_ALLOW_THREADS
396 if (status < 0)
397 return cxoError_raiseAndReturnNull();
398
399 Py_RETURN_NONE;
400 }
401
402
403 //-----------------------------------------------------------------------------
404 // cxoSessionPool_drop()
405 // Release a connection back to the session pool, dropping it so that a new
406 // connection will be created if needed.
407 //-----------------------------------------------------------------------------
408 static PyObject *cxoSessionPool_drop(cxoSessionPool *pool, PyObject *args)
409 {
410 cxoConnection *connection;
411 int status;
412
413 // connection is expected
414 if (!PyArg_ParseTuple(args, "O!", &cxoPyTypeConnection, &connection))
415 return NULL;
416
417 // release the connection
418 Py_BEGIN_ALLOW_THREADS
419 status = dpiConn_close(connection->handle, DPI_MODE_CONN_CLOSE_DROP, NULL,
420 0);
421 Py_END_ALLOW_THREADS
422 if (status < 0)
423 return cxoError_raiseAndReturnNull();
424
425 // mark connection as closed
426 Py_CLEAR(connection->sessionPool);
427 dpiConn_release(connection->handle);
428 connection->handle = NULL;
429 Py_RETURN_NONE;
430 }
431
432
433 //-----------------------------------------------------------------------------
434 // cxoSessionPool_release()
435 // Release a connection back to the session pool.
436 //-----------------------------------------------------------------------------
437 static PyObject *cxoSessionPool_release(cxoSessionPool *pool, PyObject *args,
438 PyObject *keywordArgs)
439 {
440 static char *keywordList[] = { "connection", "tag", NULL };
441 cxoConnection *conn;
442 cxoBuffer tagBuffer;
443 PyObject *tagObj;
444 uint32_t mode;
445 int status;
446
447 // parse arguments
448 tagObj = NULL;
449 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!|O",
450 keywordList, &cxoPyTypeConnection, &conn, &tagObj))
451 return NULL;
452 if (!tagObj)
453 tagObj = conn->tag;
454 if (cxoBuffer_fromObject(&tagBuffer, tagObj,
455 pool->encodingInfo.encoding) < 0)
456 return NULL;
457 mode = DPI_MODE_CONN_CLOSE_DEFAULT;
458 if (tagObj && tagObj != Py_None)
459 mode |= DPI_MODE_CONN_CLOSE_RETAG;
460 Py_BEGIN_ALLOW_THREADS
461 status = dpiConn_close(conn->handle, mode, (char*) tagBuffer.ptr,
462 tagBuffer.size);
463 Py_END_ALLOW_THREADS
464 cxoBuffer_clear(&tagBuffer);
465 if (status < 0)
466 return cxoError_raiseAndReturnNull();
467
468 // mark connection as closed
469 Py_CLEAR(conn->sessionPool);
470 dpiConn_release(conn->handle);
471 conn->handle = NULL;
472 Py_RETURN_NONE;
473 }
474
475
476 //-----------------------------------------------------------------------------
477 // cxoSessionPool_getAttribute()
478 // Return the value for the attribute.
479 //-----------------------------------------------------------------------------
480 static PyObject *cxoSessionPool_getAttribute(cxoSessionPool *pool,
481 int (*func)(dpiPool *pool, uint32_t *value))
482 {
483 uint32_t value;
484
485 if ((*func)(pool->handle, &value) < 0)
486 return cxoError_raiseAndReturnNull();
487 #if PY_MAJOR_VERSION >= 3
488 return PyLong_FromUnsignedLong(value);
489 #else
490 return PyInt_FromLong(value);
491 #endif
492 }
493
494
495 //-----------------------------------------------------------------------------
496 // cxoSessionPool_setAttribute()
497 // Set the value of the OCI attribute.
498 //-----------------------------------------------------------------------------
499 static int cxoSessionPool_setAttribute(cxoSessionPool *pool, PyObject *value,
500 int (*func)(dpiPool *pool, uint32_t value))
501 {
502 uint32_t cValue;
503
504 if (!PyInt_Check(value)) {
505 PyErr_SetString(PyExc_TypeError, "value must be an integer");
506 return -1;
507 }
508 #if PY_MAJOR_VERSION >= 3
509 cValue = PyLong_AsUnsignedLong(value);
510 #else
511 cValue = PyInt_AsLong(value);
512 #endif
513 if (PyErr_Occurred())
514 return -1;
515 if ((*func)(pool->handle, cValue) < 0)
516 return cxoError_raiseAndReturnInt();
517
518 return 0;
519 }
520
521
522 //-----------------------------------------------------------------------------
523 // cxoSessionPool_getBusyCount()
524 // Return the number of busy connections in the session pool.
525 //-----------------------------------------------------------------------------
526 static PyObject *cxoSessionPool_getBusyCount(cxoSessionPool *pool,
527 void *unused)
528 {
529 return cxoSessionPool_getAttribute(pool, dpiPool_getBusyCount);
530 }
531
532
533 //-----------------------------------------------------------------------------
534 // cxoSessionPool_getGetMode()
535 // Return the "get" mode for connections in the session pool.
536 //-----------------------------------------------------------------------------
537 static PyObject *cxoSessionPool_getGetMode(cxoSessionPool *pool, void *unused)
538 {
539 dpiPoolGetMode value;
540
541 if (dpiPool_getGetMode(pool->handle, &value) < 0)
542 return cxoError_raiseAndReturnNull();
543 return PyInt_FromLong(value);
544 }
545
546
547 //-----------------------------------------------------------------------------
548 // cxoSessionPool_getMaxLifetimeSession()
549 // Return the maximum lifetime session of connections in the session pool.
550 //-----------------------------------------------------------------------------
551 static PyObject *cxoSessionPool_getMaxLifetimeSession(cxoSessionPool *pool,
552 void *unused)
553 {
554 return cxoSessionPool_getAttribute(pool, dpiPool_getMaxLifetimeSession);
555 }
556
557
558 //-----------------------------------------------------------------------------
559 // cxoSessionPool_getOpenCount()
560 // Return the number of open connections in the session pool.
561 //-----------------------------------------------------------------------------
562 static PyObject *cxoSessionPool_getOpenCount(cxoSessionPool *pool, void *unused)
563 {
564 return cxoSessionPool_getAttribute(pool, dpiPool_getOpenCount);
565 }
566
567
568 //-----------------------------------------------------------------------------
569 // cxoSessionPool_getStmtCacheSize()
570 // Return the size of the statement cache to use in connections that are
571 // acquired from the pool.
572 //-----------------------------------------------------------------------------
573 static PyObject *cxoSessionPool_getStmtCacheSize(cxoSessionPool *pool,
574 void *unused)
575 {
576 return cxoSessionPool_getAttribute(pool, dpiPool_getStmtCacheSize);
577 }
578
579
580 //-----------------------------------------------------------------------------
581 // cxoSessionPool_getTimeout()
582 // Return the timeout for connections in the session pool.
583 //-----------------------------------------------------------------------------
584 static PyObject *cxoSessionPool_getTimeout(cxoSessionPool *pool, void *unused)
585 {
586 return cxoSessionPool_getAttribute(pool, dpiPool_getTimeout);
587 }
588
589
590 //-----------------------------------------------------------------------------
591 // cxoSessionPool_getWaitTimeout()
592 // Return the wait timeout for connections in the session pool.
593 //-----------------------------------------------------------------------------
594 static PyObject *cxoSessionPool_getWaitTimeout(cxoSessionPool *pool,
595 void *unused)
596 {
597 return cxoSessionPool_getAttribute(pool, dpiPool_getWaitTimeout);
598 }
599
600
601 //-----------------------------------------------------------------------------
602 // cxoSessionPool_setGetMode()
603 // Set the "get" mode for connections in the session pool.
604 //-----------------------------------------------------------------------------
605 static int cxoSessionPool_setGetMode(cxoSessionPool *pool, PyObject *value,
606 void *unused)
607 {
608 dpiPoolGetMode cValue;
609
610 cValue = PyInt_AsLong(value);
611 if (PyErr_Occurred())
612 return -1;
613 if (dpiPool_setGetMode(pool->handle, cValue) < 0)
614 return cxoError_raiseAndReturnInt();
615
616 return 0;
617 }
618
619
620 //-----------------------------------------------------------------------------
621 // cxoSessionPool_setMaxLifetimeSession()
622 // Set the maximum lifetime for connections in the session pool.
623 //-----------------------------------------------------------------------------
624 static int cxoSessionPool_setMaxLifetimeSession(cxoSessionPool *pool,
625 PyObject *value, void *unused)
626 {
627 return cxoSessionPool_setAttribute(pool, value,
628 dpiPool_setMaxLifetimeSession);
629 }
630
631
632 //-----------------------------------------------------------------------------
633 // cxoSessionPool_setStmtCacheSize()
634 // Set the default size of the statement cache used for connections that are
635 // acquired from the pool.
636 //-----------------------------------------------------------------------------
637 static int cxoSessionPool_setStmtCacheSize(cxoSessionPool *pool,
638 PyObject *value, void *unused)
639 {
640 return cxoSessionPool_setAttribute(pool, value, dpiPool_setStmtCacheSize);
641 }
642
643
644 //-----------------------------------------------------------------------------
645 // cxoSessionPool_setTimeout()
646 // Set the timeout for connections in the session pool.
647 //-----------------------------------------------------------------------------
648 static int cxoSessionPool_setTimeout(cxoSessionPool *pool, PyObject *value,
649 void *unused)
650 {
651 return cxoSessionPool_setAttribute(pool, value, dpiPool_setTimeout);
652 }
653
654
655 //-----------------------------------------------------------------------------
656 // cxoSessionPool_setWaitTimeout()
657 // Set the wait timeout for connections in the session pool.
658 //-----------------------------------------------------------------------------
659 static int cxoSessionPool_setWaitTimeout(cxoSessionPool *pool, PyObject *value,
660 void *unused)
661 {
662 return cxoSessionPool_setAttribute(pool, value, dpiPool_setWaitTimeout);
663 }
664
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //-----------------------------------------------------------------------------
33
44 //-----------------------------------------------------------------------------
99 #include "cxoModule.h"
1010
1111 //-----------------------------------------------------------------------------
12 // declaration of functions
13 //-----------------------------------------------------------------------------
14 static void cxoSodaCollection_free(cxoSodaCollection*);
15 static PyObject *cxoSodaCollection_repr(cxoSodaCollection*);
16 static PyObject *cxoSodaCollection_createIndex(cxoSodaCollection*, PyObject*);
17 static PyObject *cxoSodaCollection_drop(cxoSodaCollection*, PyObject*);
18 static PyObject *cxoSodaCollection_dropIndex(cxoSodaCollection*, PyObject*,
19 PyObject*);
20 static PyObject *cxoSodaCollection_find(cxoSodaCollection*, PyObject*);
21 static PyObject *cxoSodaCollection_getDataGuide(cxoSodaCollection*, PyObject*);
22 static PyObject *cxoSodaCollection_insertOne(cxoSodaCollection*, PyObject*);
23 static PyObject *cxoSodaCollection_insertOneAndGet(cxoSodaCollection*,
24 PyObject*);
25 static PyObject *cxoSodaCollection_getMetadata(cxoSodaCollection*, PyObject*);
12 // cxoSodaCollection_initialize()
13 // Initialize a new collection with its attributes.
14 //-----------------------------------------------------------------------------
15 static int cxoSodaCollection_initialize(cxoSodaCollection *coll,
16 cxoSodaDatabase *db, const char *encoding, dpiSodaColl *handle)
17 {
18 uint32_t nameLength;
19 const char *name;
20
21 // get name from ODPI-C
22 if (dpiSodaColl_getName(handle, &name, &nameLength) < 0)
23 return cxoError_raiseAndReturnInt();
24 coll->name = PyUnicode_Decode(name, nameLength, encoding, NULL);
25 if (!coll->name)
26 return -1;
27
28 // set base attributes (handle should not be added until there is no
29 // possibility of further failure)
30 coll->handle = handle;
31 Py_INCREF(db);
32 coll->db = db;
33
34 return 0;
35 }
36
37
38 //-----------------------------------------------------------------------------
39 // cxoSodaCollection_new()
40 // Create a new SODA collection object.
41 //-----------------------------------------------------------------------------
42 cxoSodaCollection *cxoSodaCollection_new(cxoSodaDatabase *db,
43 dpiSodaColl *handle)
44 {
45 cxoSodaCollection *coll;
46
47 coll = (cxoSodaCollection*)
48 cxoPyTypeSodaCollection.tp_alloc(&cxoPyTypeSodaCollection, 0);
49 if (!coll)
50 return NULL;
51 if (cxoSodaCollection_initialize(coll, db,
52 db->connection->encodingInfo.encoding, handle) < 0) {
53 Py_DECREF(coll);
54 return NULL;
55 }
56
57 return coll;
58 }
59
60
61 //-----------------------------------------------------------------------------
62 // cxoSodaCollection_free()
63 // Free the memory associated with a SODA collection.
64 //-----------------------------------------------------------------------------
65 static void cxoSodaCollection_free(cxoSodaCollection *coll)
66 {
67 if (coll->handle) {
68 dpiSodaColl_release(coll->handle);
69 coll->handle = NULL;
70 }
71 Py_CLEAR(coll->db);
72 Py_CLEAR(coll->name);
73 Py_TYPE(coll)->tp_free((PyObject*) coll);
74 }
75
76
77 //-----------------------------------------------------------------------------
78 // cxoSodaCollection_repr()
79 // Return a string representation of a SODA collection.
80 //-----------------------------------------------------------------------------
81 static PyObject *cxoSodaCollection_repr(cxoSodaCollection *coll)
82 {
83 PyObject *module, *name, *result;
84
85 if (cxoUtils_getModuleAndName(Py_TYPE(coll), &module, &name) < 0)
86 return NULL;
87 result = cxoUtils_formatString("<%s.%s %s>",
88 PyTuple_Pack(3, module, name, coll->name));
89 Py_DECREF(module);
90 Py_DECREF(name);
91 return result;
92 }
93
94
95 //-----------------------------------------------------------------------------
96 // cxoSodaCollection_createIndex()
97 // Create an index on a SODA collection.
98 //-----------------------------------------------------------------------------
99 static PyObject *cxoSodaCollection_createIndex(cxoSodaCollection *coll,
100 PyObject *specObj)
101 {
102 cxoBuffer specBuffer;
103 uint32_t flags;
104 int status;
105
106 if (cxoUtils_processJsonArg(specObj, &specBuffer) < 0)
107 return NULL;
108 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
109 return NULL;
110 Py_BEGIN_ALLOW_THREADS
111 status = dpiSodaColl_createIndex(coll->handle, specBuffer.ptr,
112 specBuffer.size, flags);
113 Py_END_ALLOW_THREADS
114 cxoBuffer_clear(&specBuffer);
115 if (status < 0)
116 return cxoError_raiseAndReturnNull();
117 Py_RETURN_NONE;
118 }
119
120
121 //-----------------------------------------------------------------------------
122 // cxoSodaCollection_drop()
123 // Create a SODA collection and return it.
124 //-----------------------------------------------------------------------------
125 static PyObject *cxoSodaCollection_drop(cxoSodaCollection *coll,
126 PyObject *args)
127 {
128 uint32_t flags;
129 int isDropped;
130
131 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
132 return NULL;
133 if (dpiSodaColl_drop(coll->handle, flags, &isDropped) < 0)
134 return cxoError_raiseAndReturnNull();
135 if (isDropped)
136 Py_RETURN_TRUE;
137 Py_RETURN_FALSE;
138 }
139
140
141 //-----------------------------------------------------------------------------
142 // cxoSodaCollection_dropIndex()
143 // Drop an index on a SODA collection.
144 //-----------------------------------------------------------------------------
145 static PyObject *cxoSodaCollection_dropIndex(cxoSodaCollection *coll,
146 PyObject *args, PyObject *keywordArgs)
147 {
148 static char *keywordList[] = { "name", "force", NULL };
149 int status, isDropped, force;
150 PyObject *nameObj, *forceObj;
151 cxoBuffer nameBuffer;
152 uint32_t flags;
153
154 // parse arguments
155 forceObj = NULL;
156 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList,
157 &nameObj, &forceObj))
158 return NULL;
159 if (cxoUtils_getBooleanValue(forceObj, 0, &force) < 0)
160 return NULL;
161
162 // drop index
163 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
164 return NULL;
165 if (force)
166 flags |= DPI_SODA_FLAGS_INDEX_DROP_FORCE;
167 if (cxoBuffer_fromObject(&nameBuffer, nameObj,
168 coll->db->connection->encodingInfo.encoding) < 0)
169 return NULL;
170 Py_BEGIN_ALLOW_THREADS
171 status = dpiSodaColl_dropIndex(coll->handle, nameBuffer.ptr,
172 nameBuffer.size, flags, &isDropped);
173 Py_END_ALLOW_THREADS
174 cxoBuffer_clear(&nameBuffer);
175 if (status < 0)
176 return cxoError_raiseAndReturnNull();
177 if (isDropped)
178 Py_RETURN_TRUE;
179 Py_RETURN_FALSE;
180 }
181
182
183 //-----------------------------------------------------------------------------
184 // cxoSodaCollection_find()
185 // Creates an operation options object which can be used to perform a number
186 // of operations on the collection using the criteria set on the object.
187 //-----------------------------------------------------------------------------
188 static PyObject *cxoSodaCollection_find(cxoSodaCollection *coll,
189 PyObject *args)
190 {
191 return (PyObject*) cxoSodaOperation_new(coll);
192 }
193
194
195 //-----------------------------------------------------------------------------
196 // cxoSodaCollection_getDataGuide()
197 // Return the data guide associated with the collection.
198 //-----------------------------------------------------------------------------
199 static PyObject *cxoSodaCollection_getDataGuide(cxoSodaCollection *coll,
200 PyObject *args)
201 {
202 dpiSodaDoc *handle;
203 cxoSodaDoc *doc;
204 uint32_t flags;
205 int status;
206
207 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
208 return NULL;
209 Py_BEGIN_ALLOW_THREADS
210 status = dpiSodaColl_getDataGuide(coll->handle, flags, &handle);
211 Py_END_ALLOW_THREADS
212 if (status < 0)
213 return cxoError_raiseAndReturnNull();
214 if (handle) {
215 doc = cxoSodaDoc_new(coll->db, handle);
216 if (!doc)
217 return NULL;
218 return (PyObject*) doc;
219 }
220 Py_RETURN_NONE;
221 }
222
223
224 //-----------------------------------------------------------------------------
225 // cxoSodaCollection_insertManyHelper()
226 // Helper method to perform bulk insert of SODA documents into a collection.
227 //-----------------------------------------------------------------------------
228 static PyObject *cxoSodaCollection_insertManyHelper(cxoSodaCollection *coll,
229 PyObject *docs, Py_ssize_t numDocs, dpiSodaDoc **handles,
230 dpiSodaDoc **returnHandles)
231 {
232 PyObject *element, *returnDocs;
233 Py_ssize_t i, j;
234 cxoSodaDoc *doc;
235 uint32_t flags;
236 int status;
237
238 // determine flags to use
239 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
240 return NULL;
241
242 // populate array of document handles
243 for (i = 0; i < numDocs; i++) {
244 element = PyList_GET_ITEM(docs, i);
245 if (cxoUtils_processSodaDocArg(coll->db, element, &handles[i]) < 0) {
246 for (j = 0; j < i; j++)
247 dpiSodaDoc_release(handles[j]);
248 return NULL;
249 }
250 }
251
252 // perform bulk insert
253 Py_BEGIN_ALLOW_THREADS
254 status = dpiSodaColl_insertMany(coll->handle, (uint32_t) numDocs, handles,
255 flags, returnHandles);
256 Py_END_ALLOW_THREADS
257 if (status < 0)
258 cxoError_raiseAndReturnNull();
259 for (i = 0; i < numDocs; i++)
260 dpiSodaDoc_release(handles[i]);
261 if (status < 0)
262 return NULL;
263
264 // if no documents are to be returned, None is returned
265 if (!returnHandles)
266 Py_RETURN_NONE;
267
268 // otherwise, return list of documents
269 returnDocs = PyList_New(numDocs);
270 if (!returnDocs) {
271 for (i = 0; i < numDocs; i++)
272 dpiSodaDoc_release(returnHandles[i]);
273 return NULL;
274 }
275 for (i = 0; i < numDocs; i++) {
276 doc = cxoSodaDoc_new(coll->db, returnHandles[i]);
277 if (!doc) {
278 for (j = i; j < numDocs; j++)
279 dpiSodaDoc_release(returnHandles[j]);
280 Py_DECREF(returnDocs);
281 return NULL;
282 }
283 PyList_SET_ITEM(returnDocs, i, (PyObject*) doc);
284 }
285 return returnDocs;
286 }
287
288
289 //-----------------------------------------------------------------------------
290 // cxoSodaCollection_insertMany()
291 // Inserts multilple document into the collection at one time.
292 //-----------------------------------------------------------------------------
293 static PyObject *cxoSodaCollection_insertMany(cxoSodaCollection *coll,
294 PyObject *arg)
295 {
296 dpiSodaDoc **handles;
297 Py_ssize_t numDocs;
298 PyObject *result;
299
300 if (!PyList_Check(arg)) {
301 PyErr_SetString(PyExc_TypeError, "expecting list");
302 return NULL;
303 }
304 numDocs = PyList_GET_SIZE(arg);
305 handles = PyMem_Malloc(numDocs * sizeof(dpiSodaDoc*));
306 if (!handles) {
307 PyErr_NoMemory();
308 return NULL;
309 }
310 result = cxoSodaCollection_insertManyHelper(coll, arg, numDocs, handles,
311 NULL);
312 PyMem_Free(handles);
313 return result;
314 }
315
316
317 //-----------------------------------------------------------------------------
318 // cxoSodaCollection_insertManyAndGet()
319 // Inserts multiple documents into the collection at one time and return a
320 // list of documents containing all but the content itself.
321 //-----------------------------------------------------------------------------
322 static PyObject *cxoSodaCollection_insertManyAndGet(cxoSodaCollection *coll,
323 PyObject *arg)
324 {
325 dpiSodaDoc **handles, **returnHandles;
326 Py_ssize_t numDocs;
327 PyObject *result;
328
329 if (!PyList_Check(arg)) {
330 PyErr_SetString(PyExc_TypeError, "expecting list");
331 return NULL;
332 }
333 numDocs = PyList_GET_SIZE(arg);
334 handles = PyMem_Malloc(numDocs * sizeof(dpiSodaDoc*));
335 if (!handles) {
336 PyErr_NoMemory();
337 return NULL;
338 }
339 returnHandles = PyMem_Malloc(numDocs * sizeof(dpiSodaDoc*));
340 if (!returnHandles) {
341 PyErr_NoMemory();
342 PyMem_Free(handles);
343 return NULL;
344 }
345 result = cxoSodaCollection_insertManyHelper(coll, arg, numDocs, handles,
346 returnHandles);
347 PyMem_Free(handles);
348 PyMem_Free(returnHandles);
349 return result;
350 }
351
352
353 //-----------------------------------------------------------------------------
354 // cxoSodaCollection_insertOne()
355 // Insert a single document into the collection.
356 //-----------------------------------------------------------------------------
357 static PyObject *cxoSodaCollection_insertOne(cxoSodaCollection *coll,
358 PyObject *arg)
359 {
360 dpiSodaDoc *handle;
361 uint32_t flags;
362 int status;
363
364 if (cxoUtils_processSodaDocArg(coll->db, arg, &handle) < 0)
365 return NULL;
366 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
367 return NULL;
368 Py_BEGIN_ALLOW_THREADS
369 status = dpiSodaColl_insertOne(coll->handle, handle, flags, NULL);
370 Py_END_ALLOW_THREADS
371 if (status < 0)
372 cxoError_raiseAndReturnNull();
373 dpiSodaDoc_release(handle);
374 if (status < 0)
375 return NULL;
376 Py_RETURN_NONE;
377 }
378
379
380 //-----------------------------------------------------------------------------
381 // cxoSodaCollection_insertOneAndGet()
382 // Insert a single document into the collection and return a document
383 // containing all but the content itself.
384 //-----------------------------------------------------------------------------
385 static PyObject *cxoSodaCollection_insertOneAndGet(cxoSodaCollection *coll,
386 PyObject *arg)
387 {
388 dpiSodaDoc *handle, *returnedHandle;
389 uint32_t flags;
390 int status;
391
392 if (cxoUtils_processSodaDocArg(coll->db, arg, &handle) < 0)
393 return NULL;
394 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
395 return NULL;
396 Py_BEGIN_ALLOW_THREADS
397 status = dpiSodaColl_insertOne(coll->handle, handle, flags,
398 &returnedHandle);
399 Py_END_ALLOW_THREADS
400 if (status < 0)
401 cxoError_raiseAndReturnNull();
402 dpiSodaDoc_release(handle);
403 if (status < 0)
404 return NULL;
405 return (PyObject*) cxoSodaDoc_new(coll->db, returnedHandle);
406 }
407
408
409 //-----------------------------------------------------------------------------
410 // cxoSodaCollection_getMetadata()
411 // Retrieve the metadata for the collection.
412 //-----------------------------------------------------------------------------
413 static PyObject *cxoSodaCollection_getMetadata(cxoSodaCollection *coll,
414 PyObject *unused)
415 {
416 PyObject *str, *result;
417 uint32_t valueLength;
418 const char *value;
419
420 if (dpiSodaColl_getMetadata(coll->handle, &value, &valueLength) < 0)
421 return cxoError_raiseAndReturnNull();
422 str = PyUnicode_Decode(value, valueLength,
423 coll->db->connection->encodingInfo.encoding, NULL);
424 if (!str)
425 return NULL;
426 result = PyObject_CallFunctionObjArgs(cxoJsonLoadFunction, str, NULL);
427 Py_DECREF(str);
428 return result;
429 }
430
431
432 //-----------------------------------------------------------------------------
433 // cxoSodaCollection_save()
434 // Insert a single document into the collection.
435 //-----------------------------------------------------------------------------
436 static PyObject *cxoSodaCollection_save(cxoSodaCollection *coll,
437 PyObject *arg)
438 {
439 dpiSodaDoc *handle;
440 uint32_t flags;
441 int status;
442
443 if (cxoUtils_processSodaDocArg(coll->db, arg, &handle) < 0)
444 return NULL;
445 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
446 return NULL;
447 Py_BEGIN_ALLOW_THREADS
448 status = dpiSodaColl_save(coll->handle, handle, flags, NULL);
449 Py_END_ALLOW_THREADS
450 if (status < 0)
451 cxoError_raiseAndReturnNull();
452 dpiSodaDoc_release(handle);
453 if (status < 0)
454 return NULL;
455 Py_RETURN_NONE;
456 }
457
458
459 //-----------------------------------------------------------------------------
460 // cxoSodaCollection_saveAndGet()
461 // Insert a single document into the collection and return a document
462 // containing all but the content itself.
463 //-----------------------------------------------------------------------------
464 static PyObject *cxoSodaCollection_saveAndGet(cxoSodaCollection *coll,
465 PyObject *arg)
466 {
467 dpiSodaDoc *handle, *returnedHandle;
468 uint32_t flags;
469 int status;
470
471 if (cxoUtils_processSodaDocArg(coll->db, arg, &handle) < 0)
472 return NULL;
473 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
474 return NULL;
475 Py_BEGIN_ALLOW_THREADS
476 status = dpiSodaColl_save(coll->handle, handle, flags, &returnedHandle);
477 Py_END_ALLOW_THREADS
478 if (status < 0)
479 cxoError_raiseAndReturnNull();
480 dpiSodaDoc_release(handle);
481 if (status < 0)
482 return NULL;
483 return (PyObject*) cxoSodaDoc_new(coll->db, returnedHandle);
484 }
485
486
487 //-----------------------------------------------------------------------------
488 // cxoSodaCollection_truncate()
489 // Remove all of the documents from the SODA collection.
490 //-----------------------------------------------------------------------------
491 static PyObject *cxoSodaCollection_truncate(cxoSodaCollection *coll,
492 PyObject *arg)
493 {
494 int status;
495
496 Py_BEGIN_ALLOW_THREADS
497 status = dpiSodaColl_truncate(coll->handle);
498 Py_END_ALLOW_THREADS
499 if (status < 0)
500 return cxoError_raiseAndReturnNull();
501 Py_RETURN_NONE;
502 }
26503
27504
28505 //-----------------------------------------------------------------------------
39516 { "insertOne", (PyCFunction) cxoSodaCollection_insertOne, METH_O },
40517 { "insertOneAndGet", (PyCFunction) cxoSodaCollection_insertOneAndGet,
41518 METH_O },
519 { "insertMany", (PyCFunction) cxoSodaCollection_insertMany, METH_O },
520 { "insertManyAndGet", (PyCFunction) cxoSodaCollection_insertManyAndGet,
521 METH_O },
522 { "save", (PyCFunction) cxoSodaCollection_save, METH_O },
523 { "saveAndGet", (PyCFunction) cxoSodaCollection_saveAndGet, METH_O },
524 { "truncate", (PyCFunction) cxoSodaCollection_truncate, METH_NOARGS },
42525 { NULL }
43526 };
44527
66549 //-----------------------------------------------------------------------------
67550 PyTypeObject cxoPyTypeSodaCollection = {
68551 PyVarObject_HEAD_INIT(NULL, 0)
69 "cx_Oracle.SodaCollection", // tp_name
70 sizeof(cxoSodaCollection), // tp_basicsize
71 0, // tp_itemsize
72 (destructor) cxoSodaCollection_free,// tp_dealloc
73 0, // tp_print
74 0, // tp_getattr
75 0, // tp_setattr
76 0, // tp_compare
77 (reprfunc) cxoSodaCollection_repr, // tp_repr
78 0, // tp_as_number
79 0, // tp_as_sequence
80 0, // tp_as_mapping
81 0, // tp_hash
82 0, // tp_call
83 0, // tp_str
84 0, // tp_getattro
85 0, // tp_setattro
86 0, // tp_as_buffer
87 Py_TPFLAGS_DEFAULT, // tp_flags
88 0, // tp_doc
89 0, // tp_traverse
90 0, // tp_clear
91 0, // tp_richcompare
92 0, // tp_weaklistoffset
93 0, // tp_iter
94 0, // tp_iternext
95 cxoMethods, // tp_methods
96 cxoMembers, // tp_members
97 cxoCalcMembers, // tp_getset
98 0, // tp_base
99 0, // tp_dict
100 0, // tp_descr_get
101 0, // tp_descr_set
102 0, // tp_dictoffset
103 0, // tp_init
104 0, // tp_alloc
105 0, // tp_new
106 0, // tp_free
107 0, // tp_is_gc
108 0 // tp_bases
552 .tp_name = "cx_Oracle.SodaCollection",
553 .tp_basicsize = sizeof(cxoSodaCollection),
554 .tp_dealloc = (destructor) cxoSodaCollection_free,
555 .tp_repr = (reprfunc) cxoSodaCollection_repr,
556 .tp_flags = Py_TPFLAGS_DEFAULT,
557 .tp_methods = cxoMethods,
558 .tp_members = cxoMembers,
559 .tp_getset = cxoCalcMembers
109560 };
110
111 //-----------------------------------------------------------------------------
112 // cxoSodaCollection_initialize()
113 // Initialize a new collection with its attributes.
114 //-----------------------------------------------------------------------------
115 static int cxoSodaCollection_initialize(cxoSodaCollection *coll,
116 cxoSodaDatabase *db, const char *encoding, dpiSodaColl *handle)
117 {
118 uint32_t nameLength;
119 const char *name;
120
121 // get name from ODPI-C
122 if (dpiSodaColl_getName(handle, &name, &nameLength) < 0)
123 return cxoError_raiseAndReturnInt();
124 coll->name = cxoPyString_fromEncodedString(name, nameLength, encoding,
125 NULL);
126 if (!coll->name)
127 return -1;
128
129 // set base attributes (handle should not be added until there is no
130 // possibility of further failure)
131 coll->handle = handle;
132 Py_INCREF(db);
133 coll->db = db;
134
135 return 0;
136 }
137
138
139 //-----------------------------------------------------------------------------
140 // cxoSodaCollection_new()
141 // Create a new SODA collection object.
142 //-----------------------------------------------------------------------------
143 cxoSodaCollection *cxoSodaCollection_new(cxoSodaDatabase *db,
144 dpiSodaColl *handle)
145 {
146 cxoSodaCollection *coll;
147
148 coll = (cxoSodaCollection*)
149 cxoPyTypeSodaCollection.tp_alloc(&cxoPyTypeSodaCollection, 0);
150 if (!coll)
151 return NULL;
152 if (cxoSodaCollection_initialize(coll, db,
153 db->connection->encodingInfo.encoding, handle) < 0) {
154 Py_DECREF(coll);
155 return NULL;
156 }
157
158 return coll;
159 }
160
161
162 //-----------------------------------------------------------------------------
163 // cxoSodaCollection_free()
164 // Free the memory associated with a SODA collection.
165 //-----------------------------------------------------------------------------
166 static void cxoSodaCollection_free(cxoSodaCollection *coll)
167 {
168 if (coll->handle) {
169 dpiSodaColl_release(coll->handle);
170 coll->handle = NULL;
171 }
172 Py_CLEAR(coll->db);
173 Py_CLEAR(coll->name);
174 Py_TYPE(coll)->tp_free((PyObject*) coll);
175 }
176
177
178 //-----------------------------------------------------------------------------
179 // cxoSodaCollection_repr()
180 // Return a string representation of a SODA collection.
181 //-----------------------------------------------------------------------------
182 static PyObject *cxoSodaCollection_repr(cxoSodaCollection *coll)
183 {
184 PyObject *module, *name, *result;
185
186 if (cxoUtils_getModuleAndName(Py_TYPE(coll), &module, &name) < 0)
187 return NULL;
188 result = cxoUtils_formatString("<%s.%s %s>",
189 PyTuple_Pack(3, module, name, coll->name));
190 Py_DECREF(module);
191 Py_DECREF(name);
192 return result;
193 }
194
195
196 //-----------------------------------------------------------------------------
197 // cxoSodaCollection_createIndex()
198 // Create an index on a SODA collection.
199 //-----------------------------------------------------------------------------
200 static PyObject *cxoSodaCollection_createIndex(cxoSodaCollection *coll,
201 PyObject *specObj)
202 {
203 cxoBuffer specBuffer;
204 uint32_t flags;
205 int status;
206
207 if (cxoUtils_processJsonArg(specObj, &specBuffer) < 0)
208 return NULL;
209 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
210 return NULL;
211 Py_BEGIN_ALLOW_THREADS
212 status = dpiSodaColl_createIndex(coll->handle, specBuffer.ptr,
213 specBuffer.size, flags);
214 Py_END_ALLOW_THREADS
215 cxoBuffer_clear(&specBuffer);
216 if (status < 0)
217 return cxoError_raiseAndReturnNull();
218 Py_RETURN_NONE;
219 }
220
221
222 //-----------------------------------------------------------------------------
223 // cxoSodaCollection_drop()
224 // Create a SODA collection and return it.
225 //-----------------------------------------------------------------------------
226 static PyObject *cxoSodaCollection_drop(cxoSodaCollection *coll,
227 PyObject *args)
228 {
229 uint32_t flags;
230 int isDropped;
231
232 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
233 return NULL;
234 if (dpiSodaColl_drop(coll->handle, flags, &isDropped) < 0)
235 return cxoError_raiseAndReturnNull();
236 if (isDropped)
237 Py_RETURN_TRUE;
238 Py_RETURN_FALSE;
239 }
240
241
242 //-----------------------------------------------------------------------------
243 // cxoSodaCollection_dropIndex()
244 // Drop an index on a SODA collection.
245 //-----------------------------------------------------------------------------
246 static PyObject *cxoSodaCollection_dropIndex(cxoSodaCollection *coll,
247 PyObject *args, PyObject *keywordArgs)
248 {
249 static char *keywordList[] = { "name", "force", NULL };
250 int status, isDropped, force;
251 PyObject *nameObj, *forceObj;
252 cxoBuffer nameBuffer;
253 uint32_t flags;
254
255 // parse arguments
256 forceObj = NULL;
257 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList,
258 &nameObj, &forceObj))
259 return NULL;
260 if (cxoUtils_getBooleanValue(forceObj, 0, &force) < 0)
261 return NULL;
262
263 // drop index
264 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
265 return NULL;
266 if (force)
267 flags |= DPI_SODA_FLAGS_INDEX_DROP_FORCE;
268 if (cxoBuffer_fromObject(&nameBuffer, nameObj,
269 coll->db->connection->encodingInfo.encoding) < 0)
270 return NULL;
271 Py_BEGIN_ALLOW_THREADS
272 status = dpiSodaColl_dropIndex(coll->handle, nameBuffer.ptr,
273 nameBuffer.size, flags, &isDropped);
274 Py_END_ALLOW_THREADS
275 cxoBuffer_clear(&nameBuffer);
276 if (status < 0)
277 return cxoError_raiseAndReturnNull();
278 if (isDropped)
279 Py_RETURN_TRUE;
280 Py_RETURN_FALSE;
281 }
282
283
284 //-----------------------------------------------------------------------------
285 // cxoSodaCollection_find()
286 // Creates an operation options object which can be used to perform a number
287 // of operations on the collection using the criteria set on the object.
288 //-----------------------------------------------------------------------------
289 static PyObject *cxoSodaCollection_find(cxoSodaCollection *coll,
290 PyObject *args)
291 {
292 return (PyObject*) cxoSodaOperation_new(coll);
293 }
294
295
296 //-----------------------------------------------------------------------------
297 // cxoSodaCollection_getDataGuide()
298 // Return the data guide associated with the collection.
299 //-----------------------------------------------------------------------------
300 static PyObject *cxoSodaCollection_getDataGuide(cxoSodaCollection *coll,
301 PyObject *args)
302 {
303 dpiSodaDoc *handle;
304 cxoSodaDoc *doc;
305 uint32_t flags;
306 int status;
307
308 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
309 return NULL;
310 Py_BEGIN_ALLOW_THREADS
311 status = dpiSodaColl_getDataGuide(coll->handle, flags, &handle);
312 Py_END_ALLOW_THREADS
313 if (status < 0)
314 return cxoError_raiseAndReturnNull();
315 if (handle) {
316 doc = cxoSodaDoc_new(coll->db, handle);
317 if (!doc)
318 return NULL;
319 return (PyObject*) doc;
320 }
321 Py_RETURN_NONE;
322 }
323
324
325 //-----------------------------------------------------------------------------
326 // cxoSodaCollection_insertOne()
327 // Insert a single document into the collection.
328 //-----------------------------------------------------------------------------
329 static PyObject *cxoSodaCollection_insertOne(cxoSodaCollection *coll,
330 PyObject *arg)
331 {
332 cxoSodaDoc *doc;
333 uint32_t flags;
334 int status;
335
336 if (cxoUtils_processSodaDocArg(coll->db, arg, &doc) < 0)
337 return NULL;
338 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
339 return NULL;
340 Py_BEGIN_ALLOW_THREADS
341 status = dpiSodaColl_insertOne(coll->handle, doc->handle, flags, NULL);
342 Py_END_ALLOW_THREADS
343 if (status < 0) {
344 cxoError_raiseAndReturnNull();
345 Py_DECREF(doc);
346 return NULL;
347 }
348 Py_DECREF(doc);
349 Py_RETURN_NONE;
350 }
351
352
353 //-----------------------------------------------------------------------------
354 // cxoSodaCollection_insertOneAndGet()
355 // Insert a single document into the collection and return a document
356 // containing all but the content itself.
357 //-----------------------------------------------------------------------------
358 static PyObject *cxoSodaCollection_insertOneAndGet(cxoSodaCollection *coll,
359 PyObject *arg)
360 {
361 dpiSodaDoc *returnedDoc;
362 cxoSodaDoc *doc;
363 uint32_t flags;
364 int status;
365
366 if (cxoUtils_processSodaDocArg(coll->db, arg, &doc) < 0)
367 return NULL;
368 if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
369 return NULL;
370 Py_BEGIN_ALLOW_THREADS
371 status = dpiSodaColl_insertOne(coll->handle, doc->handle, flags,
372 &returnedDoc);
373 Py_END_ALLOW_THREADS
374 if (status < 0) {
375 cxoError_raiseAndReturnNull();
376 Py_DECREF(doc);
377 return NULL;
378 }
379 Py_DECREF(doc);
380 return (PyObject*) cxoSodaDoc_new(coll->db, returnedDoc);
381 }
382
383
384 //-----------------------------------------------------------------------------
385 // cxoSodaCollection_getMetadata()
386 // Retrieve the metadata for the collection.
387 //-----------------------------------------------------------------------------
388 static PyObject *cxoSodaCollection_getMetadata(cxoSodaCollection *coll,
389 PyObject *unused)
390 {
391 PyObject *str, *result;
392 uint32_t valueLength;
393 const char *value;
394
395 if (dpiSodaColl_getMetadata(coll->handle, &value, &valueLength) < 0)
396 return cxoError_raiseAndReturnNull();
397 str = PyUnicode_Decode(value, valueLength,
398 coll->db->connection->encodingInfo.encoding, NULL);
399 if (!str)
400 return NULL;
401 result = PyObject_CallFunctionObjArgs(cxoJsonLoadFunction, str, NULL);
402 Py_DECREF(str);
403 return result;
404 }
405
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //-----------------------------------------------------------------------------
33
44 //-----------------------------------------------------------------------------
77 //-----------------------------------------------------------------------------
88
99 #include "cxoModule.h"
10
11 //-----------------------------------------------------------------------------
12 // Declaration of functions
13 //-----------------------------------------------------------------------------
14 static void cxoSodaDatabase_free(cxoSodaDatabase*);
15 static PyObject *cxoSodaDatabase_repr(cxoSodaDatabase*);
16 static PyObject *cxoSodaDatabase_createCollection(cxoSodaDatabase*,
17 PyObject*, PyObject*);
18 static PyObject *cxoSodaDatabase_createDocument(cxoSodaDatabase*,
19 PyObject*, PyObject*);
20 static PyObject *cxoSodaDatabase_getCollectionNames(cxoSodaDatabase*,
21 PyObject*, PyObject*);
22 static PyObject *cxoSodaDatabase_openCollection(cxoSodaDatabase*, PyObject*);
23
24
25 //-----------------------------------------------------------------------------
26 // declaration of methods for Python type "SodaDatabase"
27 //-----------------------------------------------------------------------------
28 static PyMethodDef cxoMethods[] = {
29 { "createCollection", (PyCFunction) cxoSodaDatabase_createCollection,
30 METH_VARARGS | METH_KEYWORDS },
31 { "createDocument", (PyCFunction) cxoSodaDatabase_createDocument,
32 METH_VARARGS | METH_KEYWORDS },
33 { "getCollectionNames", (PyCFunction) cxoSodaDatabase_getCollectionNames,
34 METH_VARARGS | METH_KEYWORDS },
35 { "openCollection", (PyCFunction) cxoSodaDatabase_openCollection, METH_O },
36 { NULL }
37 };
38
39
40 //-----------------------------------------------------------------------------
41 // Python type declarations
42 //-----------------------------------------------------------------------------
43 PyTypeObject cxoPyTypeSodaDatabase = {
44 PyVarObject_HEAD_INIT(NULL, 0)
45 "cx_Oracle.SodaDatabase", // tp_name
46 sizeof(cxoSodaDatabase), // tp_basicsize
47 0, // tp_itemsize
48 (destructor) cxoSodaDatabase_free, // tp_dealloc
49 0, // tp_print
50 0, // tp_getattr
51 0, // tp_setattr
52 0, // tp_compare
53 (reprfunc) cxoSodaDatabase_repr, // tp_repr
54 0, // tp_as_number
55 0, // tp_as_sequence
56 0, // tp_as_mapping
57 0, // tp_hash
58 0, // tp_call
59 0, // tp_str
60 0, // tp_getattro
61 0, // tp_setattro
62 0, // tp_as_buffer
63 Py_TPFLAGS_DEFAULT, // tp_flags
64 0, // tp_doc
65 0, // tp_traverse
66 0, // tp_clear
67 0, // tp_richcompare
68 0, // tp_weaklistoffset
69 0, // tp_iter
70 0, // tp_iternext
71 cxoMethods, // tp_methods
72 0, // tp_members
73 0, // tp_getset
74 0, // tp_base
75 0, // tp_dict
76 0, // tp_descr_get
77 0, // tp_descr_set
78 0, // tp_dictoffset
79 0, // tp_init
80 0, // tp_alloc
81 0, // tp_new
82 0, // tp_free
83 0, // tp_is_gc
84 0 // tp_bases
85 };
86
8710
8811 //-----------------------------------------------------------------------------
8912 // cxoSodaDatabase_new()
324247 if (!result)
325248 return NULL;
326249 for (i = 0; i < collNames.numNames; i++) {
327 temp = cxoPyString_fromEncodedString(collNames.names[i],
328 collNames.nameLengths[i], encoding, NULL);
250 temp = PyUnicode_Decode(collNames.names[i], collNames.nameLengths[i],
251 encoding, NULL);
329252 if (!temp) {
330253 Py_DECREF(result);
331254 return NULL;
378301 return (PyObject*) coll;
379302 }
380303
304
305 //-----------------------------------------------------------------------------
306 // declaration of methods for Python type
307 //-----------------------------------------------------------------------------
308 static PyMethodDef cxoMethods[] = {
309 { "createCollection", (PyCFunction) cxoSodaDatabase_createCollection,
310 METH_VARARGS | METH_KEYWORDS },
311 { "createDocument", (PyCFunction) cxoSodaDatabase_createDocument,
312 METH_VARARGS | METH_KEYWORDS },
313 { "getCollectionNames", (PyCFunction) cxoSodaDatabase_getCollectionNames,
314 METH_VARARGS | METH_KEYWORDS },
315 { "openCollection", (PyCFunction) cxoSodaDatabase_openCollection, METH_O },
316 { NULL }
317 };
318
319
320 //-----------------------------------------------------------------------------
321 // declaration of Python type
322 //-----------------------------------------------------------------------------
323 PyTypeObject cxoPyTypeSodaDatabase = {
324 PyVarObject_HEAD_INIT(NULL, 0)
325 .tp_name = "cx_Oracle.SodaDatabase",
326 .tp_basicsize = sizeof(cxoSodaDatabase),
327 .tp_dealloc = (destructor) cxoSodaDatabase_free,
328 .tp_repr = (reprfunc) cxoSodaDatabase_repr,
329 .tp_flags = Py_TPFLAGS_DEFAULT,
330 .tp_methods = cxoMethods
331 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //-----------------------------------------------------------------------------
33
44 //-----------------------------------------------------------------------------
88
99 #include "cxoModule.h"
1010
11 //-----------------------------------------------------------------------------
12 // Declaration of functions
13 //-----------------------------------------------------------------------------
14 static void cxoSodaDoc_free(cxoSodaDoc*);
15 static PyObject *cxoSodaDoc_repr(cxoSodaDoc*);
16 static PyObject *cxoSodaDoc_getCreatedOn(cxoSodaDoc*, void*);
17 static PyObject *cxoSodaDoc_getKey(cxoSodaDoc*, void*);
18 static PyObject *cxoSodaDoc_getLastModified(cxoSodaDoc*, void*);
19 static PyObject *cxoSodaDoc_getMediaType(cxoSodaDoc*, void*);
20 static PyObject *cxoSodaDoc_getVersion(cxoSodaDoc*, void*);
21 static PyObject *cxoSodaDoc_getContent(cxoSodaDoc*, PyObject*);
22 static PyObject *cxoSodaDoc_getContentAsBytes(cxoSodaDoc*, PyObject*);
23 static PyObject *cxoSodaDoc_getContentAsString(cxoSodaDoc*, PyObject*);
11 // forward declarations
12 static PyObject *cxoSodaDoc_getContentAsString(cxoSodaDoc *doc,
13 PyObject *args);
14
15
16 //-----------------------------------------------------------------------------
17 // cxoSodaDoc_new()
18 // Create a new SODA document.
19 //-----------------------------------------------------------------------------
20 cxoSodaDoc *cxoSodaDoc_new(cxoSodaDatabase *db, dpiSodaDoc *handle)
21 {
22 cxoSodaDoc *doc;
23
24 doc = (cxoSodaDoc*) cxoPyTypeSodaDoc.tp_alloc(&cxoPyTypeSodaDoc, 0);
25 if (!doc) {
26 dpiSodaDoc_release(handle);
27 return NULL;
28 }
29 Py_INCREF(db);
30 doc->db = db;
31 doc->handle = handle;
32 return doc;
33 }
34
35
36 //-----------------------------------------------------------------------------
37 // cxoSodaDoc_free()
38 // Free the memory associated with a SODA document.
39 //-----------------------------------------------------------------------------
40 static void cxoSodaDoc_free(cxoSodaDoc *doc)
41 {
42 if (doc->handle) {
43 dpiSodaDoc_release(doc->handle);
44 doc->handle = NULL;
45 }
46 Py_CLEAR(doc->db);
47 Py_TYPE(doc)->tp_free((PyObject*) doc);
48 }
49
50
51 //-----------------------------------------------------------------------------
52 // cxoSodaDoc_repr()
53 // Return a string representation of a SODA document.
54 //-----------------------------------------------------------------------------
55 static PyObject *cxoSodaDoc_repr(cxoSodaDoc *doc)
56 {
57 PyObject *module, *name, *result, *keyObj;
58 uint32_t keyLength;
59 const char *key;
60
61 if (dpiSodaDoc_getKey(doc->handle, &key, &keyLength) < 0)
62 return cxoError_raiseAndReturnNull();
63 keyObj = PyUnicode_Decode(key, keyLength,
64 doc->db->connection->encodingInfo.encoding, NULL);
65 if (!keyObj)
66 return NULL;
67 if (cxoUtils_getModuleAndName(Py_TYPE(doc), &module, &name) < 0) {
68 Py_DECREF(keyObj);
69 return NULL;
70 }
71 result = cxoUtils_formatString("<%s.%s with key %s>",
72 PyTuple_Pack(3, module, name, keyObj));
73 Py_DECREF(module);
74 Py_DECREF(name);
75 return result;
76 }
77
78
79 //-----------------------------------------------------------------------------
80 // cxoSodaDoc_getCreatedOn()
81 // Retrieve the time the SODA document was created, as a string in ISO 8601
82 // format.
83 //-----------------------------------------------------------------------------
84 static PyObject *cxoSodaDoc_getCreatedOn(cxoSodaDoc *doc, void *unused)
85 {
86 uint32_t valueLength;
87 const char *value;
88
89 if (dpiSodaDoc_getCreatedOn(doc->handle, &value, &valueLength) < 0)
90 return cxoError_raiseAndReturnNull();
91 if (valueLength > 0)
92 return PyUnicode_Decode(value, valueLength,
93 doc->db->connection->encodingInfo.encoding, NULL);
94 Py_RETURN_NONE;
95 }
96
97
98 //-----------------------------------------------------------------------------
99 // cxoSodaDoc_getKey()
100 // Retrieve the key for the SODA document.
101 //-----------------------------------------------------------------------------
102 static PyObject *cxoSodaDoc_getKey(cxoSodaDoc *doc, void *unused)
103 {
104 uint32_t valueLength;
105 const char *value;
106
107 if (dpiSodaDoc_getKey(doc->handle, &value, &valueLength) < 0)
108 return cxoError_raiseAndReturnNull();
109 if (valueLength > 0)
110 return PyUnicode_Decode(value, valueLength,
111 doc->db->connection->encodingInfo.encoding, NULL);
112 Py_RETURN_NONE;
113 }
114
115
116 //-----------------------------------------------------------------------------
117 // cxoSodaDoc_getLastModified()
118 // Retrieve the time the SODA document was last modified, as a string in ISO
119 // 8601 format.
120 //-----------------------------------------------------------------------------
121 static PyObject *cxoSodaDoc_getLastModified(cxoSodaDoc *doc, void *unused)
122 {
123 uint32_t valueLength;
124 const char *value;
125
126 if (dpiSodaDoc_getLastModified(doc->handle, &value, &valueLength) < 0)
127 return cxoError_raiseAndReturnNull();
128 if (valueLength > 0)
129 return PyUnicode_Decode(value, valueLength,
130 doc->db->connection->encodingInfo.encoding, NULL);
131 Py_RETURN_NONE;
132 }
133
134
135 //-----------------------------------------------------------------------------
136 // cxoSodaDoc_getMediaType()
137 // Retrieve the media type of the SODA document.
138 //-----------------------------------------------------------------------------
139 static PyObject *cxoSodaDoc_getMediaType(cxoSodaDoc *doc, void *unused)
140 {
141 uint32_t valueLength;
142 const char *value;
143
144 if (dpiSodaDoc_getMediaType(doc->handle, &value, &valueLength) < 0)
145 return cxoError_raiseAndReturnNull();
146 if (valueLength > 0)
147 return PyUnicode_Decode(value, valueLength,
148 doc->db->connection->encodingInfo.encoding, NULL);
149 Py_RETURN_NONE;
150 }
151
152
153 //-----------------------------------------------------------------------------
154 // cxoSodaDoc_getVersion()
155 // Retrieve the version for the SODA document.
156 //-----------------------------------------------------------------------------
157 static PyObject *cxoSodaDoc_getVersion(cxoSodaDoc *doc, void *unused)
158 {
159 uint32_t valueLength;
160 const char *value;
161
162 if (dpiSodaDoc_getVersion(doc->handle, &value, &valueLength) < 0)
163 return cxoError_raiseAndReturnNull();
164 if (valueLength > 0)
165 return PyUnicode_Decode(value, valueLength,
166 doc->db->connection->encodingInfo.encoding, NULL);
167 Py_RETURN_NONE;
168 }
169
170
171 //-----------------------------------------------------------------------------
172 // cxoSodaDoc_getContent()
173 // Get the content from the document and return a Python object.
174 //-----------------------------------------------------------------------------
175 static PyObject *cxoSodaDoc_getContent(cxoSodaDoc *doc, PyObject *args)
176 {
177 PyObject *str, *result;
178
179 str = cxoSodaDoc_getContentAsString(doc, args);
180 if (!str)
181 return NULL;
182 if (str == Py_None)
183 return str;
184 result = PyObject_CallFunctionObjArgs(cxoJsonLoadFunction, str, NULL);
185 Py_DECREF(str);
186 return result;
187 }
188
189
190 //-----------------------------------------------------------------------------
191 // cxoSodaDoc_getContentAsBytes()
192 // Get the content from the document and return a bytes object.
193 //-----------------------------------------------------------------------------
194 static PyObject *cxoSodaDoc_getContentAsBytes(cxoSodaDoc *doc, PyObject *args)
195 {
196 const char *content, *encoding;
197 uint32_t contentLength;
198
199 if (dpiSodaDoc_getContent(doc->handle, &content, &contentLength,
200 &encoding) < 0)
201 return cxoError_raiseAndReturnNull();
202 if (contentLength > 0)
203 return PyBytes_FromStringAndSize(content, contentLength);
204 Py_RETURN_NONE;
205 }
206
207
208 //-----------------------------------------------------------------------------
209 // cxoSodaDoc_getContentAsString()
210 // Get the content from the document and return a string.
211 //-----------------------------------------------------------------------------
212 static PyObject *cxoSodaDoc_getContentAsString(cxoSodaDoc *doc, PyObject *args)
213 {
214 const char *content, *encoding;
215 uint32_t contentLength;
216
217 if (dpiSodaDoc_getContent(doc->handle, &content, &contentLength,
218 &encoding) < 0)
219 return cxoError_raiseAndReturnNull();
220 if (contentLength > 0)
221 return PyUnicode_Decode(content, contentLength, encoding, NULL);
222 Py_RETURN_NONE;
223 }
24224
25225
26226 //-----------------------------------------------------------------------------
50250
51251
52252 //-----------------------------------------------------------------------------
53 // Python type declarations
253 // declaration of Python type
54254 //-----------------------------------------------------------------------------
55255 PyTypeObject cxoPyTypeSodaDoc = {
56256 PyVarObject_HEAD_INIT(NULL, 0)
57 "cx_Oracle.SodaDoc", // tp_name
58 sizeof(cxoSodaDoc), // tp_basicsize
59 0, // tp_itemsize
60 (destructor) cxoSodaDoc_free, // tp_dealloc
61 0, // tp_print
62 0, // tp_getattr
63 0, // tp_setattr
64 0, // tp_compare
65 (reprfunc) cxoSodaDoc_repr, // tp_repr
66 0, // tp_as_number
67 0, // tp_as_sequence
68 0, // tp_as_mapping
69 0, // tp_hash
70 0, // tp_call
71 0, // tp_str
72 0, // tp_getattro
73 0, // tp_setattro
74 0, // tp_as_buffer
75 Py_TPFLAGS_DEFAULT, // tp_flags
76 0, // tp_doc
77 0, // tp_traverse
78 0, // tp_clear
79 0, // tp_richcompare
80 0, // tp_weaklistoffset
81 0, // tp_iter
82 0, // tp_iternext
83 cxoMethods, // tp_methods
84 0, // tp_members
85 cxoCalcMembers, // tp_getset
86 0, // tp_base
87 0, // tp_dict
88 0, // tp_descr_get
89 0, // tp_descr_set
90 0, // tp_dictoffset
91 0, // tp_init
92 0, // tp_alloc
93 0, // tp_new
94 0, // tp_free
95 0, // tp_is_gc
96 0 // tp_bases
257 .tp_name = "cx_Oracle.SodaDoc",
258 .tp_basicsize = sizeof(cxoSodaDoc),
259 .tp_dealloc = (destructor) cxoSodaDoc_free,
260 .tp_repr = (reprfunc) cxoSodaDoc_repr,
261 .tp_flags = Py_TPFLAGS_DEFAULT,
262 .tp_methods = cxoMethods,
263 .tp_getset = cxoCalcMembers
97264 };
98
99
100 //-----------------------------------------------------------------------------
101 // cxoSodaDoc_new()
102 // Create a new SODA document.
103 //-----------------------------------------------------------------------------
104 cxoSodaDoc *cxoSodaDoc_new(cxoSodaDatabase *db, dpiSodaDoc *handle)
105 {
106 cxoSodaDoc *doc;
107
108 doc = (cxoSodaDoc*) cxoPyTypeSodaDoc.tp_alloc(&cxoPyTypeSodaDoc, 0);
109 if (!doc) {
110 dpiSodaDoc_release(handle);
111 return NULL;
112 }
113 Py_INCREF(db);
114 doc->db = db;
115 doc->handle = handle;
116 return doc;
117 }
118
119
120 //-----------------------------------------------------------------------------
121 // cxoSodaDoc_free()
122 // Free the memory associated with a SODA document.
123 //-----------------------------------------------------------------------------
124 static void cxoSodaDoc_free(cxoSodaDoc *doc)
125 {
126 if (doc->handle) {
127 dpiSodaDoc_release(doc->handle);
128 doc->handle = NULL;
129 }
130 Py_CLEAR(doc->db);
131 Py_TYPE(doc)->tp_free((PyObject*) doc);
132 }
133
134
135 //-----------------------------------------------------------------------------
136 // cxoSodaDoc_repr()
137 // Return a string representation of a SODA document.
138 //-----------------------------------------------------------------------------
139 static PyObject *cxoSodaDoc_repr(cxoSodaDoc *doc)
140 {
141 PyObject *module, *name, *result, *keyObj;
142 uint32_t keyLength;
143 const char *key;
144
145 if (dpiSodaDoc_getKey(doc->handle, &key, &keyLength) < 0)
146 return cxoError_raiseAndReturnNull();
147 keyObj = cxoPyString_fromEncodedString(key, keyLength,
148 doc->db->connection->encodingInfo.encoding, NULL);
149 if (!keyObj)
150 return NULL;
151 if (cxoUtils_getModuleAndName(Py_TYPE(doc), &module, &name) < 0) {
152 Py_DECREF(keyObj);
153 return NULL;
154 }
155 result = cxoUtils_formatString("<%s.%s with key %s>",
156 PyTuple_Pack(3, module, name, keyObj));
157 Py_DECREF(module);
158 Py_DECREF(name);
159 return result;
160 }
161
162
163 //-----------------------------------------------------------------------------
164 // cxoSodaDoc_getCreatedOn()
165 // Retrieve the time the SODA document was created, as a string in ISO 8601
166 // format.
167 //-----------------------------------------------------------------------------
168 static PyObject *cxoSodaDoc_getCreatedOn(cxoSodaDoc *doc, void *unused)
169 {
170 uint32_t valueLength;
171 const char *value;
172
173 if (dpiSodaDoc_getCreatedOn(doc->handle, &value, &valueLength) < 0)
174 return cxoError_raiseAndReturnNull();
175 if (valueLength > 0)
176 return cxoPyString_fromEncodedString(value, valueLength,
177 doc->db->connection->encodingInfo.encoding, NULL);
178 Py_RETURN_NONE;
179 }
180
181
182 //-----------------------------------------------------------------------------
183 // cxoSodaDoc_getKey()
184 // Retrieve the key for the SODA document.
185 //-----------------------------------------------------------------------------
186 static PyObject *cxoSodaDoc_getKey(cxoSodaDoc *doc, void *unused)
187 {
188 uint32_t valueLength;
189 const char *value;
190
191 if (dpiSodaDoc_getKey(doc->handle, &value, &valueLength) < 0)
192 return cxoError_raiseAndReturnNull();
193 if (valueLength > 0)
194 return cxoPyString_fromEncodedString(value, valueLength,
195 doc->db->connection->encodingInfo.encoding, NULL);
196 Py_RETURN_NONE;
197 }
198
199
200 //-----------------------------------------------------------------------------
201 // cxoSodaDoc_getLastModified()
202 // Retrieve the time the SODA document was last modified, as a string in ISO
203 // 8601 format.
204 //-----------------------------------------------------------------------------
205 static PyObject *cxoSodaDoc_getLastModified(cxoSodaDoc *doc, void *unused)
206 {
207 uint32_t valueLength;
208 const char *value;
209
210 if (dpiSodaDoc_getLastModified(doc->handle, &value, &valueLength) < 0)
211 return cxoError_raiseAndReturnNull();
212 if (valueLength > 0)
213 return cxoPyString_fromEncodedString(value, valueLength,
214 doc->db->connection->encodingInfo.encoding, NULL);
215 Py_RETURN_NONE;
216 }
217
218
219 //-----------------------------------------------------------------------------
220 // cxoSodaDoc_getMediaType()
221 // Retrieve the media type of the SODA document.
222 //-----------------------------------------------------------------------------
223 static PyObject *cxoSodaDoc_getMediaType(cxoSodaDoc *doc, void *unused)
224 {
225 uint32_t valueLength;
226 const char *value;
227
228 if (dpiSodaDoc_getMediaType(doc->handle, &value, &valueLength) < 0)
229 return cxoError_raiseAndReturnNull();
230 if (valueLength > 0)
231 return cxoPyString_fromEncodedString(value, valueLength,
232 doc->db->connection->encodingInfo.encoding, NULL);
233 Py_RETURN_NONE;
234 }
235
236
237 //-----------------------------------------------------------------------------
238 // cxoSodaDoc_getVersion()
239 // Retrieve the version for the SODA document.
240 //-----------------------------------------------------------------------------
241 static PyObject *cxoSodaDoc_getVersion(cxoSodaDoc *doc, void *unused)
242 {
243 uint32_t valueLength;
244 const char *value;
245
246 if (dpiSodaDoc_getVersion(doc->handle, &value, &valueLength) < 0)
247 return cxoError_raiseAndReturnNull();
248 if (valueLength > 0)
249 return cxoPyString_fromEncodedString(value, valueLength,
250 doc->db->connection->encodingInfo.encoding, NULL);
251 Py_RETURN_NONE;
252 }
253
254
255 //-----------------------------------------------------------------------------
256 // cxoSodaDoc_getContent()
257 // Get the content from the document and return a Python object.
258 //-----------------------------------------------------------------------------
259 static PyObject *cxoSodaDoc_getContent(cxoSodaDoc *doc, PyObject *args)
260 {
261 PyObject *str, *result;
262
263 str = cxoSodaDoc_getContentAsString(doc, args);
264 if (!str)
265 return NULL;
266 if (str == Py_None)
267 return str;
268 result = PyObject_CallFunctionObjArgs(cxoJsonLoadFunction, str, NULL);
269 Py_DECREF(str);
270 return result;
271 }
272
273
274 //-----------------------------------------------------------------------------
275 // cxoSodaDoc_getContentAsBytes()
276 // Get the content from the document and return a bytes object.
277 //-----------------------------------------------------------------------------
278 static PyObject *cxoSodaDoc_getContentAsBytes(cxoSodaDoc *doc, PyObject *args)
279 {
280 const char *content, *encoding;
281 uint32_t contentLength;
282
283 if (dpiSodaDoc_getContent(doc->handle, &content, &contentLength,
284 &encoding) < 0)
285 return cxoError_raiseAndReturnNull();
286 if (contentLength > 0)
287 return PyBytes_FromStringAndSize(content, contentLength);
288 Py_RETURN_NONE;
289 }
290
291
292 //-----------------------------------------------------------------------------
293 // cxoSodaDoc_getContentAsString()
294 // Get the content from the document and return a string.
295 //-----------------------------------------------------------------------------
296 static PyObject *cxoSodaDoc_getContentAsString(cxoSodaDoc *doc, PyObject *args)
297 {
298 const char *content, *encoding;
299 uint32_t contentLength;
300
301 if (dpiSodaDoc_getContent(doc->handle, &content, &contentLength,
302 &encoding) < 0)
303 return cxoError_raiseAndReturnNull();
304 if (contentLength > 0)
305 return PyUnicode_Decode(content, contentLength, encoding, NULL);
306 Py_RETURN_NONE;
307 }
308
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //-----------------------------------------------------------------------------
33
44 //-----------------------------------------------------------------------------
99 //-----------------------------------------------------------------------------
1010
1111 #include "cxoModule.h"
12
13 //-----------------------------------------------------------------------------
14 // Declaration of functions
15 //-----------------------------------------------------------------------------
16 static void cxoSodaDocCursor_free(cxoSodaDocCursor*);
17 static PyObject *cxoSodaDocCursor_repr(cxoSodaDocCursor*);
18 static PyObject *cxoSodaDocCursor_getIter(cxoSodaDocCursor*);
19 static PyObject *cxoSodaDocCursor_getNext(cxoSodaDocCursor*);
20 static PyObject *cxoSodaDocCursor_close(cxoSodaDocCursor*, PyObject*);
21
22
23 //-----------------------------------------------------------------------------
24 // declaration of methods
25 //-----------------------------------------------------------------------------
26 static PyMethodDef cxoMethods[] = {
27 { "close", (PyCFunction) cxoSodaDocCursor_close, METH_NOARGS },
28 { NULL }
29 };
30
31
32 //-----------------------------------------------------------------------------
33 // Python type declarations
34 //-----------------------------------------------------------------------------
35 PyTypeObject cxoPyTypeSodaDocCursor = {
36 PyVarObject_HEAD_INIT(NULL, 0)
37 "cx_Oracle.SodaDocCursor", // tp_name
38 sizeof(cxoSodaDocCursor), // tp_basicsize
39 0, // tp_itemsize
40 (destructor) cxoSodaDocCursor_free, // tp_dealloc
41 0, // tp_print
42 0, // tp_getattr
43 0, // tp_setattr
44 0, // tp_compare
45 (reprfunc) cxoSodaDocCursor_repr, // tp_repr
46 0, // tp_as_number
47 0, // tp_as_sequence
48 0, // tp_as_mapping
49 0, // tp_hash
50 0, // tp_call
51 0, // tp_str
52 0, // tp_getattro
53 0, // tp_setattro
54 0, // tp_as_buffer
55 Py_TPFLAGS_DEFAULT, // tp_flags
56 0, // tp_doc
57 0, // tp_traverse
58 0, // tp_clear
59 0, // tp_richcompare
60 0, // tp_weaklistoffset
61 (getiterfunc) cxoSodaDocCursor_getIter, // tp_iter
62 (iternextfunc) cxoSodaDocCursor_getNext, // tp_iternext
63 cxoMethods, // tp_methods
64 0, // tp_members
65 0, // tp_getset
66 0, // tp_base
67 0, // tp_dict
68 0, // tp_descr_get
69 0, // tp_descr_set
70 0, // tp_dictoffset
71 0, // tp_init
72 0, // tp_alloc
73 0, // tp_new
74 0, // tp_free
75 0, // tp_is_gc
76 0 // tp_bases
77 };
78
7912
8013 //-----------------------------------------------------------------------------
8114 // cxoSodaDocCursor_new()
181114 return (PyObject*) doc;
182115 }
183116
117
118 //-----------------------------------------------------------------------------
119 // declaration of methods
120 //-----------------------------------------------------------------------------
121 static PyMethodDef cxoMethods[] = {
122 { "close", (PyCFunction) cxoSodaDocCursor_close, METH_NOARGS },
123 { NULL }
124 };
125
126
127 //-----------------------------------------------------------------------------
128 // Python type declarations
129 //-----------------------------------------------------------------------------
130 PyTypeObject cxoPyTypeSodaDocCursor = {
131 PyVarObject_HEAD_INIT(NULL, 0)
132 .tp_name = "cx_Oracle.SodaDocCursor",
133 .tp_basicsize = sizeof(cxoSodaDocCursor),
134 .tp_dealloc = (destructor) cxoSodaDocCursor_free,
135 .tp_repr = (reprfunc) cxoSodaDocCursor_repr,
136 .tp_flags = Py_TPFLAGS_DEFAULT,
137 .tp_iter = (getiterfunc) cxoSodaDocCursor_getIter,
138 .tp_iternext = (iternextfunc) cxoSodaDocCursor_getNext,
139 .tp_methods = cxoMethods
140 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //-----------------------------------------------------------------------------
33
44 //-----------------------------------------------------------------------------
88 //-----------------------------------------------------------------------------
99
1010 #include "cxoModule.h"
11
12 //-----------------------------------------------------------------------------
13 // Declaration of functions
14 //-----------------------------------------------------------------------------
15 static void cxoSodaOperation_free(cxoSodaOperation*);
16 static PyObject *cxoSodaOperation_repr(cxoSodaOperation*);
17 static PyObject *cxoSodaOperation_filter(cxoSodaOperation*, PyObject*);
18 static PyObject *cxoSodaOperation_key(cxoSodaOperation*, PyObject*);
19 static PyObject *cxoSodaOperation_keys(cxoSodaOperation*, PyObject*);
20 static PyObject *cxoSodaOperation_limit(cxoSodaOperation*, PyObject*);
21 static PyObject *cxoSodaOperation_skip(cxoSodaOperation*, PyObject*);
22 static PyObject *cxoSodaOperation_version(cxoSodaOperation*, PyObject*);
23 static PyObject *cxoSodaOperation_count(cxoSodaOperation*, PyObject*);
24 static PyObject *cxoSodaOperation_getCursor(cxoSodaOperation*, PyObject*);
25 static PyObject *cxoSodaOperation_getDocuments(cxoSodaOperation*, PyObject*);
26 static PyObject *cxoSodaOperation_getOne(cxoSodaOperation*, PyObject*);
27 static PyObject *cxoSodaOperation_remove(cxoSodaOperation*, PyObject*);
28 static PyObject *cxoSodaOperation_replaceOne(cxoSodaOperation*, PyObject*);
29 static PyObject *cxoSodaOperation_replaceOneAndGet(cxoSodaOperation*,
30 PyObject*);
31
32
33 //-----------------------------------------------------------------------------
34 // declaration of methods for Python type "SodaOperation"
35 //-----------------------------------------------------------------------------
36 static PyMethodDef cxoMethods[] = {
37 { "filter", (PyCFunction) cxoSodaOperation_filter, METH_O },
38 { "key", (PyCFunction) cxoSodaOperation_key, METH_O },
39 { "keys", (PyCFunction) cxoSodaOperation_keys, METH_O },
40 { "limit", (PyCFunction) cxoSodaOperation_limit, METH_O },
41 { "skip", (PyCFunction) cxoSodaOperation_skip, METH_O },
42 { "version", (PyCFunction) cxoSodaOperation_version, METH_O },
43 { "count", (PyCFunction) cxoSodaOperation_count, METH_NOARGS },
44 { "getCursor", (PyCFunction) cxoSodaOperation_getCursor, METH_NOARGS },
45 { "getDocuments", (PyCFunction) cxoSodaOperation_getDocuments,
46 METH_NOARGS },
47 { "getOne", (PyCFunction) cxoSodaOperation_getOne, METH_NOARGS },
48 { "remove", (PyCFunction) cxoSodaOperation_remove, METH_NOARGS },
49 { "replaceOne", (PyCFunction) cxoSodaOperation_replaceOne, METH_O },
50 { "replaceOneAndGet", (PyCFunction) cxoSodaOperation_replaceOneAndGet,
51 METH_O },
52 { NULL }
53 };
54
55
56 //-----------------------------------------------------------------------------
57 // Python type declarations
58 //-----------------------------------------------------------------------------
59 PyTypeObject cxoPyTypeSodaOperation = {
60 PyVarObject_HEAD_INIT(NULL, 0)
61 "cx_Oracle.SodaOperation", // tp_name
62 sizeof(cxoSodaOperation), // tp_basicsize
63 0, // tp_itemsize
64 (destructor) cxoSodaOperation_free, // tp_dealloc
65 0, // tp_print
66 0, // tp_getattr
67 0, // tp_setattr
68 0, // tp_compare
69 (reprfunc) cxoSodaOperation_repr, // tp_repr
70 0, // tp_as_number
71 0, // tp_as_sequence
72 0, // tp_as_mapping
73 0, // tp_hash
74 0, // tp_call
75 0, // tp_str
76 0, // tp_getattro
77 0, // tp_setattro
78 0, // tp_as_buffer
79 Py_TPFLAGS_DEFAULT, // tp_flags
80 0, // tp_doc
81 0, // tp_traverse
82 0, // tp_clear
83 0, // tp_richcompare
84 0, // tp_weaklistoffset
85 0, // tp_iter
86 0, // tp_iternext
87 cxoMethods, // tp_methods
88 0, // tp_members
89 0, // tp_getset
90 0, // tp_base
91 0, // tp_dict
92 0, // tp_descr_get
93 0, // tp_descr_set
94 0, // tp_dictoffset
95 0, // tp_init
96 0, // tp_alloc
97 0, // tp_new
98 0, // tp_free
99 0, // tp_is_gc
100 0 // tp_bases
101 };
102
10311
10412 //-----------------------------------------------------------------------------
10513 // cxoSodaOperation_clearKeys()
506414 PyObject *arg)
507415 {
508416 int status, replaced;
509 cxoSodaDoc *doc;
510 uint32_t flags;
511
512 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
513 return NULL;
514 if (cxoUtils_processSodaDocArg(op->coll->db, arg, &doc) < 0)
417 dpiSodaDoc *handle;
418 uint32_t flags;
419
420 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
421 return NULL;
422 if (cxoUtils_processSodaDocArg(op->coll->db, arg, &handle) < 0)
515423 return NULL;
516424 Py_BEGIN_ALLOW_THREADS
517425 status = dpiSodaColl_replaceOne(op->coll->handle, &op->options,
518 doc->handle, flags, &replaced, NULL);
519 Py_END_ALLOW_THREADS
520 if (status < 0) {
426 handle, flags, &replaced, NULL);
427 Py_END_ALLOW_THREADS
428 if (status < 0)
521429 cxoError_raiseAndReturnNull();
522 Py_DECREF(doc);
523 return NULL;
524 }
525 Py_DECREF(doc);
430 dpiSodaDoc_release(handle);
431 if (status < 0)
432 return NULL;
526433 if (replaced)
527434 Py_RETURN_TRUE;
528435 Py_RETURN_FALSE;
537444 static PyObject *cxoSodaOperation_replaceOneAndGet(cxoSodaOperation *op,
538445 PyObject *arg)
539446 {
540 dpiSodaDoc *replacedDoc;
541 cxoSodaDoc *doc;
447 dpiSodaDoc *handle, *replacedHandle;
542448 uint32_t flags;
543449 int status;
544450
545451 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
546452 return NULL;
547 if (cxoUtils_processSodaDocArg(op->coll->db, arg, &doc) < 0)
548 return NULL;
549 Py_BEGIN_ALLOW_THREADS
550 status = dpiSodaColl_replaceOne(op->coll->handle, &op->options,
551 doc->handle, flags, NULL, &replacedDoc);
552 Py_END_ALLOW_THREADS
553 if (status < 0) {
453 if (cxoUtils_processSodaDocArg(op->coll->db, arg, &handle) < 0)
454 return NULL;
455 Py_BEGIN_ALLOW_THREADS
456 status = dpiSodaColl_replaceOne(op->coll->handle, &op->options, handle,
457 flags, NULL, &replacedHandle);
458 Py_END_ALLOW_THREADS
459 if (status < 0)
554460 cxoError_raiseAndReturnNull();
555 Py_DECREF(doc);
556 return NULL;
557 }
558 Py_DECREF(doc);
559 if (replacedDoc)
560 return (PyObject*) cxoSodaDoc_new(op->coll->db, replacedDoc);
461 dpiSodaDoc_release(handle);
462 if (status < 0)
463 return NULL;
464 if (replacedHandle)
465 return (PyObject*) cxoSodaDoc_new(op->coll->db, replacedHandle);
561466 Py_RETURN_NONE;
562467 }
563468
469
470 //-----------------------------------------------------------------------------
471 // cxoSodaOperation_fetchArraySize()
472 // Set the fetch array size to be used for the operation.
473 //-----------------------------------------------------------------------------
474 static PyObject *cxoSodaOperation_fetchArraySize(cxoSodaOperation *op,
475 PyObject *fetchArraySizeObj)
476 {
477 op->options.fetchArraySize = PyLong_AsUnsignedLong(fetchArraySizeObj);
478 if (PyErr_Occurred())
479 return NULL;
480 Py_INCREF(op);
481 return (PyObject*) op;
482 }
483
484
485 //-----------------------------------------------------------------------------
486 // declaration of methods for Python type
487 //-----------------------------------------------------------------------------
488 static PyMethodDef cxoMethods[] = {
489 { "filter", (PyCFunction) cxoSodaOperation_filter, METH_O },
490 { "key", (PyCFunction) cxoSodaOperation_key, METH_O },
491 { "keys", (PyCFunction) cxoSodaOperation_keys, METH_O },
492 { "limit", (PyCFunction) cxoSodaOperation_limit, METH_O },
493 { "skip", (PyCFunction) cxoSodaOperation_skip, METH_O },
494 { "version", (PyCFunction) cxoSodaOperation_version, METH_O },
495 { "count", (PyCFunction) cxoSodaOperation_count, METH_NOARGS },
496 { "getCursor", (PyCFunction) cxoSodaOperation_getCursor, METH_NOARGS },
497 { "getDocuments", (PyCFunction) cxoSodaOperation_getDocuments,
498 METH_NOARGS },
499 { "getOne", (PyCFunction) cxoSodaOperation_getOne, METH_NOARGS },
500 { "remove", (PyCFunction) cxoSodaOperation_remove, METH_NOARGS },
501 { "replaceOne", (PyCFunction) cxoSodaOperation_replaceOne, METH_O },
502 { "replaceOneAndGet", (PyCFunction) cxoSodaOperation_replaceOneAndGet,
503 METH_O },
504 { "fetchArraySize", (PyCFunction) cxoSodaOperation_fetchArraySize,
505 METH_O },
506 { NULL }
507 };
508
509
510 //-----------------------------------------------------------------------------
511 // declaration of Python type
512 //-----------------------------------------------------------------------------
513 PyTypeObject cxoPyTypeSodaOperation = {
514 PyVarObject_HEAD_INIT(NULL, 0)
515 .tp_name = "cx_Oracle.SodaOperation",
516 .tp_basicsize = sizeof(cxoSodaOperation),
517 .tp_dealloc = (destructor) cxoSodaOperation_free,
518 .tp_repr = (reprfunc) cxoSodaOperation_repr,
519 .tp_flags = Py_TPFLAGS_DEFAULT,
520 .tp_methods = cxoMethods
521 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // Declaration of subscription functions
18 //-----------------------------------------------------------------------------
19 static void cxoSubscr_free(cxoSubscr*);
20 static PyObject *cxoSubscr_repr(cxoSubscr*);
21 static PyObject *cxoSubscr_registerQuery(cxoSubscr*, PyObject*);
22 static void cxoMessage_free(cxoMessage*);
23 static void cxoMessageTable_free(cxoMessageTable*);
24 static void cxoMessageRow_free(cxoMessageRow*);
25 static void cxoMessageQuery_free(cxoMessageQuery*);
26
27 //-----------------------------------------------------------------------------
28 // declaration of members for Python types
29 //-----------------------------------------------------------------------------
30 static PyMemberDef cxoSubscrTypeMembers[] = {
31 { "callback", T_OBJECT, offsetof(cxoSubscr, callback), READONLY },
32 { "connection", T_OBJECT, offsetof(cxoSubscr, connection),
33 READONLY },
34 { "namespace", T_UINT, offsetof(cxoSubscr, namespace), READONLY },
35 { "name", T_OBJECT, offsetof(cxoSubscr, name), READONLY },
36 { "protocol", T_UINT, offsetof(cxoSubscr, protocol), READONLY },
37 { "ipAddress", T_OBJECT, offsetof(cxoSubscr, ipAddress), READONLY },
38 { "port", T_UINT, offsetof(cxoSubscr, port), READONLY },
39 { "timeout", T_UINT, offsetof(cxoSubscr, timeout), READONLY },
40 { "operations", T_UINT, offsetof(cxoSubscr, operations), READONLY },
41 { "qos", T_UINT, offsetof(cxoSubscr, qos), READONLY },
42 { "id", T_ULONG, offsetof(cxoSubscr, id), READONLY },
43 { NULL }
44 };
45
46 static PyMemberDef cxoMessageTypeMembers[] = {
47 { "subscription", T_OBJECT, offsetof(cxoMessage, subscription),
48 READONLY },
49 { "type", T_INT, offsetof(cxoMessage, type), READONLY },
50 { "dbname", T_OBJECT, offsetof(cxoMessage, dbname), READONLY },
51 { "txid", T_OBJECT, offsetof(cxoMessage, txId), READONLY },
52 { "tables", T_OBJECT, offsetof(cxoMessage, tables), READONLY },
53 { "queries", T_OBJECT, offsetof(cxoMessage, queries), READONLY },
54 { "queueName", T_OBJECT, offsetof(cxoMessage, queueName), READONLY },
55 { "consumerName", T_OBJECT, offsetof(cxoMessage, consumerName), READONLY },
56 { "registered", T_BOOL, offsetof(cxoMessage, registered), READONLY },
57 { NULL }
58 };
59
60 static PyMemberDef cxoMessageTableTypeMembers[] = {
61 { "name", T_OBJECT, offsetof(cxoMessageTable, name), READONLY },
62 { "rows", T_OBJECT, offsetof(cxoMessageTable, rows), READONLY },
63 { "operation", T_INT, offsetof(cxoMessageTable, operation), READONLY },
64 { NULL }
65 };
66
67 static PyMemberDef cxoMessageRowTypeMembers[] = {
68 { "rowid", T_OBJECT, offsetof(cxoMessageRow, rowid), READONLY },
69 { "operation", T_INT, offsetof(cxoMessageRow, operation), READONLY },
70 { NULL }
71 };
72
73 static PyMemberDef cxoMessageQueryTypeMembers[] = {
74 { "id", T_INT, offsetof(cxoMessageQuery, id), READONLY },
75 { "operation", T_INT, offsetof(cxoMessageQuery, operation), READONLY },
76 { "tables", T_OBJECT, offsetof(cxoMessageQuery, tables), READONLY },
77 { NULL }
78 };
79
80
81 //-----------------------------------------------------------------------------
82 // declaration of methods for Python types
83 //-----------------------------------------------------------------------------
84 static PyMethodDef cxoSubscrTypeMethods[] = {
85 { "registerquery", (PyCFunction) cxoSubscr_registerQuery,
86 METH_VARARGS },
87 { NULL, NULL }
88 };
89
90
91 //-----------------------------------------------------------------------------
92 // Python type declarations
93 //-----------------------------------------------------------------------------
94 PyTypeObject cxoPyTypeSubscr = {
95 PyVarObject_HEAD_INIT(NULL, 0)
96 "cx_Oracle.Subscription", // tp_name
97 sizeof(cxoSubscr), // tp_basicsize
98 0, // tp_itemsize
99 (destructor) cxoSubscr_free, // tp_dealloc
100 0, // tp_print
101 0, // tp_getattr
102 0, // tp_setattr
103 0, // tp_compare
104 (reprfunc) cxoSubscr_repr, // tp_repr
105 0, // tp_as_number
106 0, // tp_as_sequence
107 0, // tp_as_mapping
108 0, // tp_hash
109 0, // tp_call
110 0, // tp_str
111 0, // tp_getattro
112 0, // tp_setattro
113 0, // tp_as_buffer
114 Py_TPFLAGS_DEFAULT, // tp_flags
115 0, // tp_doc
116 0, // tp_traverse
117 0, // tp_clear
118 0, // tp_richcompare
119 0, // tp_weaklistoffset
120 0, // tp_iter
121 0, // tp_iternext
122 cxoSubscrTypeMethods, // tp_methods
123 cxoSubscrTypeMembers, // tp_members
124 0, // tp_getset
125 0, // tp_base
126 0, // tp_dict
127 0, // tp_descr_get
128 0, // tp_descr_set
129 0, // tp_dictoffset
130 0, // tp_init
131 0, // tp_alloc
132 0, // tp_new
133 0, // tp_free
134 0, // tp_is_gc
135 0 // tp_bases
136 };
137
138 PyTypeObject cxoPyTypeMessage = {
139 PyVarObject_HEAD_INIT(NULL, 0)
140 "cx_Oracle.Message", // tp_name
141 sizeof(cxoMessage), // tp_basicsize
142 0, // tp_itemsize
143 (destructor) cxoMessage_free, // tp_dealloc
144 0, // tp_print
145 0, // tp_getattr
146 0, // tp_setattr
147 0, // tp_compare
148 0, // tp_repr
149 0, // tp_as_number
150 0, // tp_as_sequence
151 0, // tp_as_mapping
152 0, // tp_hash
153 0, // tp_call
154 0, // tp_str
155 0, // tp_getattro
156 0, // tp_setattro
157 0, // tp_as_buffer
158 Py_TPFLAGS_DEFAULT, // tp_flags
159 0, // tp_doc
160 0, // tp_traverse
161 0, // tp_clear
162 0, // tp_richcompare
163 0, // tp_weaklistoffset
164 0, // tp_iter
165 0, // tp_iternext
166 0, // tp_methods
167 cxoMessageTypeMembers, // tp_members
168 0, // tp_getset
169 0, // tp_base
170 0, // tp_dict
171 0, // tp_descr_get
172 0, // tp_descr_set
173 0, // tp_dictoffset
174 0, // tp_init
175 0, // tp_alloc
176 0, // tp_new
177 0, // tp_free
178 0, // tp_is_gc
179 0 // tp_bases
180 };
181
182 PyTypeObject cxoPyTypeMessageTable = {
183 PyVarObject_HEAD_INIT(NULL, 0)
184 "cx_Oracle.MessageTable", // tp_name
185 sizeof(cxoMessageTable), // tp_basicsize
186 0, // tp_itemsize
187 (destructor) cxoMessageTable_free, // tp_dealloc
188 0, // tp_print
189 0, // tp_getattr
190 0, // tp_setattr
191 0, // tp_compare
192 0, // tp_repr
193 0, // tp_as_number
194 0, // tp_as_sequence
195 0, // tp_as_mapping
196 0, // tp_hash
197 0, // tp_call
198 0, // tp_str
199 0, // tp_getattro
200 0, // tp_setattro
201 0, // tp_as_buffer
202 Py_TPFLAGS_DEFAULT, // tp_flags
203 0, // tp_doc
204 0, // tp_traverse
205 0, // tp_clear
206 0, // tp_richcompare
207 0, // tp_weaklistoffset
208 0, // tp_iter
209 0, // tp_iternext
210 0, // tp_methods
211 cxoMessageTableTypeMembers, // tp_members
212 0, // tp_getset
213 0, // tp_base
214 0, // tp_dict
215 0, // tp_descr_get
216 0, // tp_descr_set
217 0, // tp_dictoffset
218 0, // tp_init
219 0, // tp_alloc
220 0, // tp_new
221 0, // tp_free
222 0, // tp_is_gc
223 0 // tp_bases
224 };
225
226
227 PyTypeObject cxoPyTypeMessageRow = {
228 PyVarObject_HEAD_INIT(NULL, 0)
229 "cx_Oracle.MessageRow", // tp_name
230 sizeof(cxoMessageRow), // tp_basicsize
231 0, // tp_itemsize
232 (destructor) cxoMessageRow_free, // tp_dealloc
233 0, // tp_print
234 0, // tp_getattr
235 0, // tp_setattr
236 0, // tp_compare
237 0, // tp_repr
238 0, // tp_as_number
239 0, // tp_as_sequence
240 0, // tp_as_mapping
241 0, // tp_hash
242 0, // tp_call
243 0, // tp_str
244 0, // tp_getattro
245 0, // tp_setattro
246 0, // tp_as_buffer
247 Py_TPFLAGS_DEFAULT, // tp_flags
248 0, // tp_doc
249 0, // tp_traverse
250 0, // tp_clear
251 0, // tp_richcompare
252 0, // tp_weaklistoffset
253 0, // tp_iter
254 0, // tp_iternext
255 0, // tp_methods
256 cxoMessageRowTypeMembers, // tp_members
257 0, // tp_getset
258 0, // tp_base
259 0, // tp_dict
260 0, // tp_descr_get
261 0, // tp_descr_set
262 0, // tp_dictoffset
263 0, // tp_init
264 0, // tp_alloc
265 0, // tp_new
266 0, // tp_free
267 0, // tp_is_gc
268 0 // tp_bases
269 };
270
271
272 PyTypeObject cxoPyTypeMessageQuery = {
273 PyVarObject_HEAD_INIT(NULL, 0)
274 "cx_Oracle.MessageQuery", // tp_name
275 sizeof(cxoMessageQuery), // tp_basicsize
276 0, // tp_itemsize
277 (destructor) cxoMessageQuery_free, // tp_dealloc
278 0, // tp_print
279 0, // tp_getattr
280 0, // tp_setattr
281 0, // tp_compare
282 0, // tp_repr
283 0, // tp_as_number
284 0, // tp_as_sequence
285 0, // tp_as_mapping
286 0, // tp_hash
287 0, // tp_call
288 0, // tp_str
289 0, // tp_getattro
290 0, // tp_setattro
291 0, // tp_as_buffer
292 Py_TPFLAGS_DEFAULT, // tp_flags
293 0, // tp_doc
294 0, // tp_traverse
295 0, // tp_clear
296 0, // tp_richcompare
297 0, // tp_weaklistoffset
298 0, // tp_iter
299 0, // tp_iternext
300 0, // tp_methods
301 cxoMessageQueryTypeMembers, // tp_members
302 0, // tp_getset
303 0, // tp_base
304 0, // tp_dict
305 0, // tp_descr_get
306 0, // tp_descr_set
307 0, // tp_dictoffset
308 0, // tp_init
309 0, // tp_alloc
310 0, // tp_new
311 0, // tp_free
312 0, // tp_is_gc
313 0 // tp_bases
314 };
315
316
317 //-----------------------------------------------------------------------------
31817 // cxoMessageRow_initialize()
31918 // Initialize a new message row with the information from the descriptor.
32019 //-----------------------------------------------------------------------------
32221 const char *encoding, dpiSubscrMessageRow *row)
32322 {
32423 rowObj->operation = row->operation;
325 rowObj->rowid = cxoPyString_fromEncodedString(row->rowid, row->rowidLength,
326 encoding, NULL);
24 rowObj->rowid = PyUnicode_Decode(row->rowid, row->rowidLength, encoding,
25 NULL);
32726 if (!rowObj->rowid)
32827 return -1;
32928
34241 uint32_t i;
34342
34443 tableObj->operation = table->operation;
345 tableObj->name = cxoPyString_fromEncodedString(table->name,
346 table->nameLength, encoding, NULL);
44 tableObj->name = PyUnicode_Decode(table->name, table->nameLength, encoding,
45 NULL);
34746 tableObj->rows = PyList_New(table->numRows);
34847 if (!tableObj->rows)
34948 return -1;
407106 encoding = subscription->connection->encodingInfo.encoding;
408107 messageObj->type = message->eventType;
409108 messageObj->registered = message->registered;
410 messageObj->dbname = cxoPyString_fromEncodedString(message->dbName,
109 messageObj->dbname = PyUnicode_Decode(message->dbName,
411110 message->dbNameLength, encoding, NULL);
412111 if (!messageObj->dbname)
413112 return -1;
418117 return -1;
419118 }
420119 if (message->queueName) {
421 messageObj->queueName = cxoPyString_fromEncodedString(
422 message->queueName, message->queueNameLength, encoding, NULL);
120 messageObj->queueName = PyUnicode_Decode(message->queueName,
121 message->queueNameLength, encoding, NULL);
423122 if (!messageObj->queueName)
424123 return -1;
425124 }
426125 if (message->consumerName) {
427 messageObj->consumerName = cxoPyString_fromEncodedString(
428 message->consumerName, message->consumerNameLength, encoding,
429 NULL);
126 messageObj->consumerName = PyUnicode_Decode(message->consumerName,
127 message->consumerNameLength, encoding, NULL);
430128 if (!messageObj->consumerName)
431129 return -1;
432130 }
541239 }
542240 Py_CLEAR(subscr->connection);
543241 Py_CLEAR(subscr->callback);
242 Py_CLEAR(subscr->name);
243 Py_CLEAR(subscr->ipAddress);
544244 Py_TYPE(subscr)->tp_free((PyObject*) subscr);
545245 }
546246
646346 return NULL;
647347 }
648348 Py_DECREF(cursor);
649 return PyInt_FromLong((long) queryId);
349 return PyLong_FromLong((long) queryId);
650350 }
651351
652352 Py_DECREF(cursor);
662362 {
663363 Py_CLEAR(message->subscription);
664364 Py_CLEAR(message->dbname);
365 Py_CLEAR(message->txId);
665366 Py_CLEAR(message->tables);
666367 Py_CLEAR(message->queries);
667368 Py_CLEAR(message->queueName);
671372
672373
673374 //-----------------------------------------------------------------------------
375 // cxoMessageQuery_free()
376 // Free the memory associated with a query in a message.
377 //-----------------------------------------------------------------------------
378 static void cxoMessageQuery_free(cxoMessageQuery *query)
379 {
380 Py_CLEAR(query->tables);
381 Py_TYPE(query)->tp_free((PyObject*) query);
382 }
383
384
385 //-----------------------------------------------------------------------------
386 // cxoMessageRow_free()
387 // Free the memory associated with a row in a message.
388 //-----------------------------------------------------------------------------
389 static void cxoMessageRow_free(cxoMessageRow *row)
390 {
391 Py_CLEAR(row->rowid);
392 Py_TYPE(row)->tp_free((PyObject*) row);
393 }
394
395
396 //-----------------------------------------------------------------------------
674397 // cxoMessageTable_free()
675398 // Free the memory associated with a table in a message.
676399 //-----------------------------------------------------------------------------
683406
684407
685408 //-----------------------------------------------------------------------------
686 // cxoMessageRow_free()
687 // Free the memory associated with a row in a message.
688 //-----------------------------------------------------------------------------
689 static void cxoMessageRow_free(cxoMessageRow *row)
690 {
691 Py_CLEAR(row->rowid);
692 Py_TYPE(row)->tp_free((PyObject*) row);
693 }
694
695
696 //-----------------------------------------------------------------------------
697 // cxoMessageQuery_free()
698 // Free the memory associated with a query in a message.
699 //-----------------------------------------------------------------------------
700 static void cxoMessageQuery_free(cxoMessageQuery *query)
701 {
702 Py_CLEAR(query->tables);
703 Py_TYPE(query)->tp_free((PyObject*) query);
704 }
705
409 // declaration of members for Python types
410 //-----------------------------------------------------------------------------
411 static PyMemberDef cxoSubscrTypeMembers[] = {
412 { "callback", T_OBJECT, offsetof(cxoSubscr, callback), READONLY },
413 { "connection", T_OBJECT, offsetof(cxoSubscr, connection),
414 READONLY },
415 { "namespace", T_UINT, offsetof(cxoSubscr, namespace), READONLY },
416 { "name", T_OBJECT, offsetof(cxoSubscr, name), READONLY },
417 { "protocol", T_UINT, offsetof(cxoSubscr, protocol), READONLY },
418 { "ipAddress", T_OBJECT, offsetof(cxoSubscr, ipAddress), READONLY },
419 { "port", T_UINT, offsetof(cxoSubscr, port), READONLY },
420 { "timeout", T_UINT, offsetof(cxoSubscr, timeout), READONLY },
421 { "operations", T_UINT, offsetof(cxoSubscr, operations), READONLY },
422 { "qos", T_UINT, offsetof(cxoSubscr, qos), READONLY },
423 { "id", T_ULONG, offsetof(cxoSubscr, id), READONLY },
424 { NULL }
425 };
426
427 static PyMemberDef cxoMessageTypeMembers[] = {
428 { "subscription", T_OBJECT, offsetof(cxoMessage, subscription),
429 READONLY },
430 { "type", T_INT, offsetof(cxoMessage, type), READONLY },
431 { "dbname", T_OBJECT, offsetof(cxoMessage, dbname), READONLY },
432 { "txid", T_OBJECT, offsetof(cxoMessage, txId), READONLY },
433 { "tables", T_OBJECT, offsetof(cxoMessage, tables), READONLY },
434 { "queries", T_OBJECT, offsetof(cxoMessage, queries), READONLY },
435 { "queueName", T_OBJECT, offsetof(cxoMessage, queueName), READONLY },
436 { "consumerName", T_OBJECT, offsetof(cxoMessage, consumerName), READONLY },
437 { "registered", T_BOOL, offsetof(cxoMessage, registered), READONLY },
438 { NULL }
439 };
440
441 static PyMemberDef cxoMessageTableTypeMembers[] = {
442 { "name", T_OBJECT, offsetof(cxoMessageTable, name), READONLY },
443 { "rows", T_OBJECT, offsetof(cxoMessageTable, rows), READONLY },
444 { "operation", T_INT, offsetof(cxoMessageTable, operation), READONLY },
445 { NULL }
446 };
447
448 static PyMemberDef cxoMessageRowTypeMembers[] = {
449 { "rowid", T_OBJECT, offsetof(cxoMessageRow, rowid), READONLY },
450 { "operation", T_INT, offsetof(cxoMessageRow, operation), READONLY },
451 { NULL }
452 };
453
454 static PyMemberDef cxoMessageQueryTypeMembers[] = {
455 { "id", T_INT, offsetof(cxoMessageQuery, id), READONLY },
456 { "operation", T_INT, offsetof(cxoMessageQuery, operation), READONLY },
457 { "tables", T_OBJECT, offsetof(cxoMessageQuery, tables), READONLY },
458 { NULL }
459 };
460
461
462 //-----------------------------------------------------------------------------
463 // declaration of methods for Python types
464 //-----------------------------------------------------------------------------
465 static PyMethodDef cxoSubscrTypeMethods[] = {
466 { "registerquery", (PyCFunction) cxoSubscr_registerQuery,
467 METH_VARARGS },
468 { NULL, NULL }
469 };
470
471
472 //-----------------------------------------------------------------------------
473 // Python type declarations
474 //-----------------------------------------------------------------------------
475 PyTypeObject cxoPyTypeSubscr = {
476 PyVarObject_HEAD_INIT(NULL, 0)
477 .tp_name = "cx_Oracle.Subscription",
478 .tp_basicsize = sizeof(cxoSubscr),
479 .tp_dealloc = (destructor) cxoSubscr_free,
480 .tp_repr = (reprfunc) cxoSubscr_repr,
481 .tp_flags = Py_TPFLAGS_DEFAULT,
482 .tp_methods = cxoSubscrTypeMethods,
483 .tp_members = cxoSubscrTypeMembers
484 };
485
486 PyTypeObject cxoPyTypeMessage = {
487 PyVarObject_HEAD_INIT(NULL, 0)
488 .tp_name = "cx_Oracle.Message",
489 .tp_basicsize = sizeof(cxoMessage),
490 .tp_dealloc = (destructor) cxoMessage_free,
491 .tp_flags = Py_TPFLAGS_DEFAULT,
492 .tp_members = cxoMessageTypeMembers
493 };
494
495 PyTypeObject cxoPyTypeMessageTable = {
496 PyVarObject_HEAD_INIT(NULL, 0)
497 .tp_name = "cx_Oracle.MessageTable",
498 .tp_basicsize = sizeof(cxoMessageTable),
499 .tp_dealloc = (destructor) cxoMessageTable_free,
500 .tp_flags = Py_TPFLAGS_DEFAULT,
501 .tp_members = cxoMessageTableTypeMembers
502 };
503
504
505 PyTypeObject cxoPyTypeMessageRow = {
506 PyVarObject_HEAD_INIT(NULL, 0)
507 .tp_name = "cx_Oracle.MessageRow",
508 .tp_basicsize = sizeof(cxoMessageRow),
509 .tp_dealloc = (destructor) cxoMessageRow_free,
510 .tp_flags = Py_TPFLAGS_DEFAULT,
511 .tp_members = cxoMessageRowTypeMembers
512 };
513
514
515 PyTypeObject cxoPyTypeMessageQuery = {
516 PyVarObject_HEAD_INIT(NULL, 0)
517 .tp_name = "cx_Oracle.MessageQuery",
518 .tp_basicsize = sizeof(cxoMessageQuery),
519 .tp_dealloc = (destructor) cxoMessageQuery_free,
520 .tp_flags = Py_TPFLAGS_DEFAULT,
521 .tp_members = cxoMessageQueryTypeMembers
522 };
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22 //-----------------------------------------------------------------------------
33
44 //-----------------------------------------------------------------------------
2222 #ifndef PyDateTime_DELTA_GET_MICROSECONDS
2323 #define PyDateTime_DELTA_GET_MICROSECONDS(x) ((x)->microseconds)
2424 #endif
25
26 // forward declarations
27 static Py_ssize_t cxoTransform_calculateSize(PyObject *value,
28 cxoTransformNum transformNum);
29 static cxoTransformNum cxoTransform_getNumFromPythonType(PyTypeObject *type);
30
31
32 //-----------------------------------------------------------------------------
33 // Forward declarations
34 //-----------------------------------------------------------------------------
35 static PyObject *cxoTransform_toPythonFromJson(cxoConnection *connection,
36 dpiJsonNode *node, const char *encodingErrors);
37
2538
2639 //-----------------------------------------------------------------------------
2740 // Types
174187 CXO_TRANSFORM_TIMESTAMP_LTZ,
175188 DPI_ORACLE_TYPE_TIMESTAMP_LTZ,
176189 DPI_NATIVE_TYPE_TIMESTAMP
190 },
191 {
192 CXO_TRANSFORM_TIMESTAMP_TZ,
193 DPI_ORACLE_TYPE_TIMESTAMP_TZ,
194 DPI_NATIVE_TYPE_TIMESTAMP
195 },
196 {
197 CXO_TRANSFORM_JSON,
198 DPI_ORACLE_TYPE_JSON,
199 DPI_NATIVE_TYPE_JSON
177200 }
178201 };
202
203
204 //-----------------------------------------------------------------------------
205 // cxoTransform_calculateSize()
206 // Calculate the size to use with the specified transform and Python value.
207 // This function is only called by cxoTransform_getNumFromValue() and no
208 // attempt is made to verify the value further.
209 //-----------------------------------------------------------------------------
210 static Py_ssize_t cxoTransform_calculateSize(PyObject *value,
211 cxoTransformNum transformNum)
212 {
213 switch (transformNum) {
214 case CXO_TRANSFORM_NONE:
215 return 1;
216 case CXO_TRANSFORM_BINARY:
217 return PyBytes_GET_SIZE(value);
218 case CXO_TRANSFORM_NSTRING:
219 case CXO_TRANSFORM_STRING:
220 return PyUnicode_GET_LENGTH(value);
221 default:
222 break;
223 }
224 return 0;
225 }
179226
180227
181228 //-----------------------------------------------------------------------------
192239 // cxoTransform_fromPython()
193240 // Transforms a Python object into its corresponding database value.
194241 //-----------------------------------------------------------------------------
195 int cxoTransform_fromPython(cxoTransformNum transformNum, PyObject *pyValue,
242 int cxoTransform_fromPython(cxoTransformNum transformNum,
243 dpiNativeTypeNum *nativeTypeNum, PyObject *pyValue,
196244 dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding,
197245 const char *nencoding, cxoVar *var, uint32_t arrayPos)
198246 {
247 cxoJsonBuffer jsonBuffer;
199248 dpiIntervalDS *interval;
200249 PyDateTime_Delta *delta;
201250 int32_t deltaSeconds;
206255
207256 switch (transformNum) {
208257 case CXO_TRANSFORM_BOOLEAN:
209 dbValue->asBoolean = (pyValue == Py_True);
258 dbValue->asBoolean = PyObject_IsTrue(pyValue);
259 if (PyErr_Occurred())
260 return -1;
210261 return 0;
211262 case CXO_TRANSFORM_BINARY:
212263 case CXO_TRANSFORM_FIXED_CHAR:
241292 encoding = nencoding;
242293 if (cxoBuffer_fromObject(buffer, pyValue, encoding) < 0)
243294 return -1;
244 Py_BEGIN_ALLOW_THREADS
245 status = dpiLob_setFromBytes(dbValue->asLOB, buffer->ptr,
246 buffer->size);
247 Py_END_ALLOW_THREADS
248 return status;
295 if (var) {
296 Py_BEGIN_ALLOW_THREADS
297 status = dpiLob_setFromBytes(dbValue->asLOB, buffer->ptr,
298 buffer->size);
299 Py_END_ALLOW_THREADS
300 if (status < 0)
301 return cxoError_raiseAndReturnInt();
302 } else {
303 *nativeTypeNum = DPI_NATIVE_TYPE_BYTES;
304 dbValue->asBytes.ptr = (char*) buffer->ptr;
305 dbValue->asBytes.length = buffer->size;
306 }
307 return 0;
249308 case CXO_TRANSFORM_NATIVE_INT:
250 #if PY_MAJOR_VERSION < 3
251 if (PyInt_Check(pyValue)) {
252 dbValue->asInt64 = PyInt_AS_LONG(pyValue);
253 return 0;
254 }
255 #endif
256309 if (PyBool_Check(pyValue)) {
257310 dbValue->asInt64 = (pyValue == Py_True);
258311 return 0;
264317 case CXO_TRANSFORM_INT:
265318 case CXO_TRANSFORM_DECIMAL:
266319 case CXO_TRANSFORM_FLOAT:
267 if (!PyFloat_Check(pyValue) &&
268 #if PY_MAJOR_VERSION < 3
269 !PyInt_Check(pyValue) &&
270 #endif
271 !PyLong_Check(pyValue) &&
272 !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal)) {
273 PyErr_SetString(PyExc_TypeError, "expecting number");
274 return -1;
275 }
276 textValue = PyObject_Str(pyValue);
277 if (!textValue)
278 return -1;
279 status = cxoBuffer_fromObject(buffer, textValue, encoding);
280 Py_DECREF(textValue);
281 if (status < 0)
282 return -1;
320 if (PyBool_Check(pyValue)) {
321 buffer->ptr = (pyValue == Py_True) ? "1" : "0";
322 buffer->size = 1;
323 buffer->numCharacters = 1;
324 } else {
325 if (!PyFloat_Check(pyValue) &&
326 !PyLong_Check(pyValue) &&
327 !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal)) {
328 PyErr_SetString(PyExc_TypeError, "expecting number");
329 return -1;
330 }
331 textValue = PyObject_Str(pyValue);
332 if (!textValue)
333 return -1;
334 status = cxoBuffer_fromObject(buffer, textValue, encoding);
335 Py_DECREF(textValue);
336 if (status < 0)
337 return -1;
338 }
283339 dbValue->asBytes.ptr = (char*) buffer->ptr;
284340 dbValue->asBytes.length = buffer->size;
285341 return 0;
286342 case CXO_TRANSFORM_NATIVE_DOUBLE:
287343 case CXO_TRANSFORM_NATIVE_FLOAT:
288344 if (!PyFloat_Check(pyValue) &&
289 #if PY_MAJOR_VERSION < 3
290 !PyInt_Check(pyValue) &&
291 #endif
292345 !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal) &&
293346 !PyLong_Check(pyValue)) {
294347 PyErr_SetString(PyExc_TypeError, "expecting float");
316369 case CXO_TRANSFORM_DATETIME:
317370 case CXO_TRANSFORM_TIMESTAMP:
318371 case CXO_TRANSFORM_TIMESTAMP_LTZ:
372 case CXO_TRANSFORM_TIMESTAMP_TZ:
319373 if (PyDateTime_Check(pyValue)) {
320374 memset(&dbValue->asTimestamp, 0, sizeof(dbValue->asTimestamp));
321375 dbValue->asTimestamp.year = PyDateTime_GET_YEAR(pyValue);
354408 interval->fseconds =
355409 PyDateTime_DELTA_GET_MICROSECONDS(delta) * 1000;
356410 return 0;
411 case CXO_TRANSFORM_JSON:
412 status = cxoJsonBuffer_fromObject(&jsonBuffer, pyValue);
413 if (status < 0) {
414 cxoJsonBuffer_free(&jsonBuffer);
415 return -1;
416 }
417 status = dpiJson_setValue(dbValue->asJson, &jsonBuffer.topNode);
418 cxoJsonBuffer_free(&jsonBuffer);
419 if (status < 0)
420 return cxoError_raiseAndReturnInt();
421 return 0;
357422 default:
358423 break;
359424 }
361426 cxoError_raiseFromString(cxoNotSupportedErrorException,
362427 "Python value cannot be converted to a database value");
363428 return -1;
429 }
430
431
432 //-----------------------------------------------------------------------------
433 // cxoTransform_getDefaultSize()
434 // Return the default size for the specified transform.
435 //-----------------------------------------------------------------------------
436 uint32_t cxoTransform_getDefaultSize(cxoTransformNum transformNum)
437 {
438 switch (transformNum) {
439 case CXO_TRANSFORM_NONE:
440 return 1;
441 case CXO_TRANSFORM_BINARY:
442 case CXO_TRANSFORM_NSTRING:
443 case CXO_TRANSFORM_STRING:
444 return 4000;
445 case CXO_TRANSFORM_DECIMAL:
446 case CXO_TRANSFORM_FLOAT:
447 case CXO_TRANSFORM_INT:
448 return 1000;
449 case CXO_TRANSFORM_FIXED_CHAR:
450 case CXO_TRANSFORM_FIXED_NCHAR:
451 return 2000;
452 case CXO_TRANSFORM_LONG_BINARY:
453 case CXO_TRANSFORM_LONG_STRING:
454 return 128 * 1024;
455 default:
456 break;
457 }
458
459 return 0;
364460 }
365461
366462
399495 case DPI_ORACLE_TYPE_TIMESTAMP:
400496 return CXO_TRANSFORM_TIMESTAMP;
401497 case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
498 return CXO_TRANSFORM_TIMESTAMP_TZ;
402499 case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
403500 return CXO_TRANSFORM_TIMESTAMP_LTZ;
404501 case DPI_ORACLE_TYPE_INTERVAL_DS:
421518 return CXO_TRANSFORM_LONG_BINARY;
422519 case DPI_ORACLE_TYPE_BOOLEAN:
423520 return CXO_TRANSFORM_BOOLEAN;
521 case DPI_ORACLE_TYPE_JSON:
522 return CXO_TRANSFORM_JSON;
424523 default:
425524 break;
426525 }
429528
430529
431530 //-----------------------------------------------------------------------------
432 // cxoTransform_getNumFromType()
531 // cxoTransform_getNumFromPythonType()
433532 // Get the appropriate transformation to use for the specified Python type.
434533 //-----------------------------------------------------------------------------
435 cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type)
436 {
437 if (type == &cxoPyTypeString)
534 static cxoTransformNum cxoTransform_getNumFromPythonType(PyTypeObject *type)
535 {
536 if (type == &PyUnicode_Type)
438537 return CXO_TRANSFORM_STRING;
439 if (type == &cxoPyTypeStringVar)
440 return CXO_TRANSFORM_STRING;
441 if (type == &cxoPyTypeFixedCharVar)
442 return CXO_TRANSFORM_FIXED_CHAR;
443 if (type == &cxoPyTypeNcharVar)
444 return CXO_TRANSFORM_NSTRING;
445 if (type == &cxoPyTypeFixedNcharVar)
446 return CXO_TRANSFORM_FIXED_NCHAR;
447 if (type == &cxoPyTypeRowidVar)
448 return CXO_TRANSFORM_ROWID;
449 #if PY_MAJOR_VERSION < 3
450 if (type == &PyUnicode_Type)
451 return CXO_TRANSFORM_NSTRING;
452 if (type == &PyInt_Type)
453 return CXO_TRANSFORM_INT;
454 #endif
455 if (type == &cxoPyTypeBinary)
456 return CXO_TRANSFORM_BINARY;
457 if (type == &cxoPyTypeBinaryVar)
538 if (type == &PyBytes_Type)
458539 return CXO_TRANSFORM_BINARY;
459540 if (type == &PyFloat_Type)
460541 return CXO_TRANSFORM_FLOAT;
462543 return CXO_TRANSFORM_INT;
463544 if (type == cxoPyTypeDecimal)
464545 return CXO_TRANSFORM_DECIMAL;
465 if (type == &cxoPyTypeNumberVar)
466 return CXO_TRANSFORM_FLOAT;
467 if (type == &cxoPyTypeNativeFloatVar)
468 return CXO_TRANSFORM_NATIVE_DOUBLE;
469 if (type == &cxoPyTypeNativeIntVar)
470 return CXO_TRANSFORM_NATIVE_INT;
471546 if (type == &PyBool_Type)
472 return CXO_TRANSFORM_BOOLEAN;
473 if (type == &cxoPyTypeBooleanVar)
474547 return CXO_TRANSFORM_BOOLEAN;
475548 if (type == PyDateTimeAPI->DateType)
476549 return CXO_TRANSFORM_DATE;
477550 if (type == PyDateTimeAPI->DateTimeType)
478551 return CXO_TRANSFORM_DATETIME;
479 if (type == &cxoPyTypeDateTimeVar)
480 return CXO_TRANSFORM_DATETIME;
481 if (type == &cxoPyTypeTimestampVar)
482 return CXO_TRANSFORM_TIMESTAMP;
483552 if (type == PyDateTimeAPI->DeltaType)
484553 return CXO_TRANSFORM_TIMEDELTA;
485 if (type == &cxoPyTypeIntervalVar)
486 return CXO_TRANSFORM_TIMEDELTA;
487 if (type == &cxoPyTypeObject)
488 return CXO_TRANSFORM_OBJECT;
489 if (type == &cxoPyTypeObjectVar)
490 return CXO_TRANSFORM_OBJECT;
491 if (type == &cxoPyTypeClobVar)
492 return CXO_TRANSFORM_CLOB;
493 if (type == &cxoPyTypeNclobVar)
494 return CXO_TRANSFORM_NCLOB;
495 if (type == &cxoPyTypeBlobVar)
496 return CXO_TRANSFORM_BLOB;
497 if (type == &cxoPyTypeBfileVar)
498 return CXO_TRANSFORM_BFILE;
499 if (type == &cxoPyTypeCursorVar)
500 return CXO_TRANSFORM_CURSOR;
501 if (type == &cxoPyTypeLongStringVar)
502 return CXO_TRANSFORM_LONG_STRING;
503 if (type == &cxoPyTypeLongBinaryVar)
504 return CXO_TRANSFORM_LONG_BINARY;
505554
506555 return CXO_TRANSFORM_UNSUPPORTED;
507556 }
508557
509558
510559 //-----------------------------------------------------------------------------
511 // cxoTransform_getNumFromValue()
512 // Get the appropriate transformation to use for the specified Python object.
513 //-----------------------------------------------------------------------------
514 cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql)
560 // cxoTransform_getNumFromPythonValue()
561 // Get the appropriate transformation to use for the specified Python value.
562 //-----------------------------------------------------------------------------
563 cxoTransformNum cxoTransform_getNumFromPythonValue(PyObject *value, int plsql)
515564 {
516565 cxoLob *lob;
517566
522571 return CXO_TRANSFORM_NATIVE_INT;
523572 return CXO_TRANSFORM_BOOLEAN;
524573 }
525 #if PY_MAJOR_VERSION >= 3
526574 if (PyUnicode_Check(value))
527575 return CXO_TRANSFORM_STRING;
528576 if (PyBytes_Check(value))
529577 return CXO_TRANSFORM_BINARY;
530 #else
531 if (PyUnicode_Check(value))
532 return CXO_TRANSFORM_NSTRING;
533 if (PyString_Check(value))
534 return CXO_TRANSFORM_STRING;
535 if (PyBuffer_Check(value))
536 return CXO_TRANSFORM_BINARY;
537 if (PyInt_Check(value))
538 return CXO_TRANSFORM_INT;
539 #endif
540578 if (PyLong_Check(value))
541579 return CXO_TRANSFORM_INT;
542580 if (PyFloat_Check(value))
555593 return CXO_TRANSFORM_OBJECT;
556594 if (PyObject_TypeCheck(value, &cxoPyTypeLob)) {
557595 lob = (cxoLob*) value;
558 if (lob->oracleTypeNum == DPI_ORACLE_TYPE_CLOB)
559 return CXO_TRANSFORM_CLOB;
560 if (lob->oracleTypeNum == DPI_ORACLE_TYPE_NCLOB)
561 return CXO_TRANSFORM_NCLOB;
562 if (lob->oracleTypeNum == DPI_ORACLE_TYPE_BLOB)
563 return CXO_TRANSFORM_BLOB;
564 if (lob->oracleTypeNum == DPI_ORACLE_TYPE_BFILE)
565 return CXO_TRANSFORM_BFILE;
596 return lob->dbType->defaultTransformNum;
566597 }
567598 return CXO_TRANSFORM_UNSUPPORTED;
599 }
600
601
602 //-----------------------------------------------------------------------------
603 // cxoTransform_getNumFromType()
604 // Get the appropriate transformation to use for the specified type. This
605 // can be either a database type constant defined at the module level or a
606 // Python type.
607 //-----------------------------------------------------------------------------
608 int cxoTransform_getNumFromType(PyObject *type, cxoTransformNum *transformNum,
609 cxoObjectType **objType)
610 {
611 PyTypeObject *pyType;
612 cxoApiType *apiType;
613 cxoDbType *dbType;
614 char message[250];
615 int status;
616
617 // check to see if a database type constant has been specified
618 status = PyObject_IsInstance(type, (PyObject*) &cxoPyTypeDbType);
619 if (status < 0)
620 return -1;
621 if (status == 1) {
622 dbType = (cxoDbType*) type;
623 *transformNum = dbType->defaultTransformNum;
624 *objType = NULL;
625 return 0;
626 }
627
628 // check to see if a DB API type constant has been specified
629 status = PyObject_IsInstance(type, (PyObject*) &cxoPyTypeApiType);
630 if (status < 0)
631 return -1;
632 if (status == 1) {
633 apiType = (cxoApiType*) type;
634 *transformNum = apiType->defaultTransformNum;
635 *objType = NULL;
636 return 0;
637 }
638
639 // check to see if an object type has been specified
640 if (Py_TYPE(type) == &cxoPyTypeObjectType) {
641 *transformNum = CXO_TRANSFORM_OBJECT;
642 *objType = (cxoObjectType*) type;
643 return 0;
644 }
645
646 // check to see if a Python type has been specified
647 if (Py_TYPE(type) != &PyType_Type) {
648 PyErr_SetString(PyExc_TypeError, "expecting type");
649 return -1;
650 }
651
652 // check to see if the Python type is a supported type
653 pyType = (PyTypeObject*) type;
654 *objType = NULL;
655 *transformNum = cxoTransform_getNumFromPythonType(pyType);
656 if (*transformNum != CXO_TRANSFORM_UNSUPPORTED)
657 return 0;
658
659 // no valid type specified
660 snprintf(message, sizeof(message), "Python type %s not supported.",
661 pyType->tp_name);
662 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
663 return -1;
664 }
665
666
667 //-----------------------------------------------------------------------------
668 // cxoTransform_getNumFromValue()
669 // Get the appropriate transformation to use for the specified value. If the
670 // value is an array, determine the transformation that can be used for all of
671 // the elements in that array.
672 //-----------------------------------------------------------------------------
673 int cxoTransform_getNumFromValue(PyObject *value, int *isArray,
674 Py_ssize_t *size, Py_ssize_t *numElements, int plsql,
675 cxoTransformNum *transformNum)
676 {
677 cxoTransformNum tempTransformNum;
678 PyObject *elementValue;
679 Py_ssize_t i, tempSize;
680 char message[250];
681
682 // initialization (except numElements which always has a valid value and is
683 // only overridden when a an array is encountered)
684 *size = 0;
685 *isArray = 0;
686
687 // handle arrays
688 if (PyList_Check(value)) {
689 *transformNum = CXO_TRANSFORM_NONE;
690 for (i = 0; i < PyList_GET_SIZE(value); i++) {
691 elementValue = PyList_GET_ITEM(value, i);
692 tempTransformNum = cxoTransform_getNumFromPythonValue(elementValue,
693 1);
694 if (tempTransformNum == CXO_TRANSFORM_UNSUPPORTED) {
695 snprintf(message, sizeof(message),
696 "element %u value of type %s is not supported",
697 (unsigned) i, Py_TYPE(value)->tp_name);
698 cxoError_raiseFromString(cxoNotSupportedErrorException,
699 message);
700 return -1;
701 } else if (*transformNum == CXO_TRANSFORM_NONE) {
702 *transformNum = tempTransformNum;
703 } else if (*transformNum != tempTransformNum) {
704 snprintf(message, sizeof(message),
705 "element %u value is not the same type as previous "
706 "elements", (unsigned) i);
707 cxoError_raiseFromString(cxoNotSupportedErrorException,
708 message);
709 return -1;
710 }
711 tempSize = cxoTransform_calculateSize(elementValue, *transformNum);
712 if (tempSize > *size)
713 *size = tempSize;
714 }
715 *isArray = 1;
716 *numElements = PyList_GET_SIZE(value);
717 return 0;
718 }
719
720 // handle scalar values
721 *transformNum = cxoTransform_getNumFromPythonValue(value, plsql);
722 if (*transformNum == CXO_TRANSFORM_UNSUPPORTED) {
723 snprintf(message, sizeof(message),
724 "Python value of type %s not supported.",
725 Py_TYPE(value)->tp_name);
726 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
727 return -1;
728 }
729 *size = cxoTransform_calculateSize(value, *transformNum);
730 return 0;
568731 }
569732
570733
631794 cxoConnection *connection, cxoObjectType *objType,
632795 dpiDataBuffer *dbValue, const char *encodingErrors)
633796 {
634 const cxoTransform *transform;
635797 PyObject *stringObj, *result;
636798 dpiIntervalDS *intervalDS;
637799 dpiTimestamp *timestamp;
800 dpiJsonNode *jsonNode;
638801 uint32_t rowidLength;
802 cxoDbType *dbType;
639803 const char *rowid;
640804 cxoCursor *cursor;
641805 dpiBytes *bytes;
642806 int32_t seconds;
643807
644 transform = &cxoAllTransforms[transformNum];
645808 switch (transformNum) {
646809 case CXO_TRANSFORM_BINARY:
647810 case CXO_TRANSFORM_LONG_BINARY:
651814 case CXO_TRANSFORM_BLOB:
652815 case CXO_TRANSFORM_CLOB:
653816 case CXO_TRANSFORM_NCLOB:
654 return cxoLob_new(connection, transform->oracleTypeNum,
655 dbValue->asLOB);
817 dbType = cxoDbType_fromTransformNum(transformNum);
818 return cxoLob_new(connection, dbType, dbValue->asLOB);
656819 case CXO_TRANSFORM_BOOLEAN:
657820 if (dbValue->asBoolean)
658821 Py_RETURN_TRUE;
673836 case CXO_TRANSFORM_DATETIME:
674837 case CXO_TRANSFORM_TIMESTAMP:
675838 case CXO_TRANSFORM_TIMESTAMP_LTZ:
839 case CXO_TRANSFORM_TIMESTAMP_TZ:
676840 timestamp = &dbValue->asTimestamp;
677 #if PY_MAJOR_VERSION < 3
678 if (timestamp->year < 1 || timestamp->year > 9999)
679 return PyErr_Format(PyExc_ValueError,
680 "year %d is out of range", timestamp->year);
681 #endif
682841 return PyDateTime_FromDateAndTime(timestamp->year,
683842 timestamp->month, timestamp->day, timestamp->hour,
684843 timestamp->minute, timestamp->second,
685844 timestamp->fsecond / 1000);
845 case CXO_TRANSFORM_FIXED_CHAR:
686846 case CXO_TRANSFORM_FIXED_NCHAR:
847 case CXO_TRANSFORM_LONG_STRING:
687848 case CXO_TRANSFORM_NSTRING:
849 case CXO_TRANSFORM_STRING:
688850 bytes = &dbValue->asBytes;
689851 return PyUnicode_Decode(bytes->ptr, bytes->length, bytes->encoding,
690852 encodingErrors);
693855 case CXO_TRANSFORM_NATIVE_FLOAT:
694856 return PyFloat_FromDouble(dbValue->asFloat);
695857 case CXO_TRANSFORM_NATIVE_INT:
696 #if PY_MAJOR_VERSION < 3
697 if (sizeof(long) == 8 || (dbValue->asInt64 <= INT_MAX &&
698 dbValue->asInt64 >= -INT_MAX))
699 return PyInt_FromLong((long) dbValue->asInt64);
700 #endif
701858 return PyLong_FromLongLong(dbValue->asInt64);
702859 case CXO_TRANSFORM_DECIMAL:
703860 case CXO_TRANSFORM_INT:
704861 case CXO_TRANSFORM_FLOAT:
705862 bytes = &dbValue->asBytes;
706 stringObj = cxoPyString_fromEncodedString(bytes->ptr,
707 bytes->length, bytes->encoding, encodingErrors);
863 stringObj = PyUnicode_Decode(bytes->ptr, bytes->length,
864 bytes->encoding, encodingErrors);
708865 if (!stringObj)
709866 return NULL;
710867 if (transformNum == CXO_TRANSFORM_INT &&
711868 memchr(bytes->ptr, '.', bytes->length) == NULL) {
712 #if PY_MAJOR_VERSION >= 3
713869 result = PyNumber_Long(stringObj);
714 #else
715 result = PyNumber_Int(stringObj);
716 #endif
717 Py_DECREF(stringObj);
718 return result;
719 } else if (transformNum != CXO_TRANSFORM_DECIMAL &&
720 bytes->length <= 15) {
870 } else if (transformNum == CXO_TRANSFORM_DECIMAL) {
871 result = PyObject_CallFunctionObjArgs(
872 (PyObject*) cxoPyTypeDecimal, stringObj, NULL);
873 } else {
721874 result = PyNumber_Float(stringObj);
722 Py_DECREF(stringObj);
723 return result;
724 }
725 result = PyObject_CallFunctionObjArgs(
726 (PyObject*) cxoPyTypeDecimal, stringObj, NULL);
875 }
727876 Py_DECREF(stringObj);
728877 return result;
729878 case CXO_TRANSFORM_OBJECT:
732881 if (dpiRowid_getStringValue(dbValue->asRowid, &rowid,
733882 &rowidLength) < 0)
734883 return cxoError_raiseAndReturnNull();
735 return cxoPyString_fromEncodedString(rowid, rowidLength,
884 return PyUnicode_Decode(rowid, rowidLength,
736885 connection->encodingInfo.encoding, NULL);
737 case CXO_TRANSFORM_FIXED_CHAR:
738 case CXO_TRANSFORM_STRING:
739 case CXO_TRANSFORM_LONG_STRING:
740 bytes = &dbValue->asBytes;
741 return cxoPyString_fromEncodedString(bytes->ptr, bytes->length,
742 bytes->encoding, encodingErrors);
886 case CXO_TRANSFORM_JSON:
887 if (dpiJson_getValue(dbValue->asJson,
888 DPI_JSON_OPT_NUMBER_AS_STRING, &jsonNode) < 0)
889 return cxoError_raiseAndReturnNull();
890 return cxoTransform_toPythonFromJson(connection, jsonNode,
891 encodingErrors);
743892 case CXO_TRANSFORM_TIMEDELTA:
744893 intervalDS = &dbValue->asIntervalDS;
745894 seconds = intervalDS->hours * 60 * 60 + intervalDS->minutes * 60 +
754903 "Database value cannot be converted to a Python value");
755904 }
756905
906
907 //-----------------------------------------------------------------------------
908 // cxoTransform_toPythonFromJson()
909 // Transforms a JSON node to its equivalent Python value.
910 //-----------------------------------------------------------------------------
911 static PyObject *cxoTransform_toPythonFromJson(cxoConnection *connection,
912 dpiJsonNode *node, const char *encodingErrors)
913 {
914 PyObject *result, *temp, *name;
915 cxoTransformNum transformNum;
916 dpiJsonArray *array;
917 dpiJsonObject *obj;
918 uint32_t i;
919
920 // null is a special case
921 if (node->nativeTypeNum == DPI_NATIVE_TYPE_NULL)
922 Py_RETURN_NONE;
923
924 switch (node->oracleTypeNum) {
925 case DPI_ORACLE_TYPE_NUMBER:
926 transformNum = (node->nativeTypeNum == DPI_NATIVE_TYPE_DOUBLE) ?
927 CXO_TRANSFORM_NATIVE_DOUBLE : CXO_TRANSFORM_DECIMAL;
928 break;
929 case DPI_ORACLE_TYPE_VARCHAR:
930 transformNum = CXO_TRANSFORM_STRING;
931 break;
932 case DPI_ORACLE_TYPE_RAW:
933 transformNum = CXO_TRANSFORM_BINARY;
934 break;
935 case DPI_ORACLE_TYPE_DATE:
936 case DPI_ORACLE_TYPE_TIMESTAMP:
937 transformNum = CXO_TRANSFORM_DATETIME;
938 break;
939 case DPI_ORACLE_TYPE_BOOLEAN:
940 transformNum = CXO_TRANSFORM_BOOLEAN;
941 break;
942 case DPI_ORACLE_TYPE_INTERVAL_DS:
943 transformNum = CXO_TRANSFORM_TIMEDELTA;
944 break;
945 case DPI_ORACLE_TYPE_JSON_OBJECT:
946 obj = &node->value->asJsonObject;
947 result = PyDict_New();
948 for (i = 0; i < obj->numFields; i++) {
949 name = PyUnicode_DecodeUTF8(obj->fieldNames[i],
950 obj->fieldNameLengths[i], NULL);
951 if (!name)
952 return NULL;
953 temp = cxoTransform_toPythonFromJson(connection,
954 &obj->fields[i], encodingErrors);
955 if (!temp)
956 return NULL;
957 if (PyDict_SetItem(result, name, temp) < 0) {
958 Py_DECREF(name);
959 Py_DECREF(temp);
960 return NULL;
961 }
962 Py_DECREF(name);
963 Py_DECREF(temp);
964 }
965 return result;
966 case DPI_ORACLE_TYPE_JSON_ARRAY:
967 array = &node->value->asJsonArray;
968 result = PyList_New(array->numElements);
969 for (i = 0; i < array->numElements; i++) {
970 temp = cxoTransform_toPythonFromJson(connection,
971 &array->elements[i], encodingErrors);
972 if (!temp) {
973 Py_DECREF(result);
974 return NULL;
975 }
976 PyList_SET_ITEM(result, i, temp);
977 }
978 return result;
979 default:
980 transformNum = CXO_TRANSFORM_UNSUPPORTED;
981 }
982
983 return cxoTransform_toPython(transformNum, connection, NULL,
984 node->value, encodingErrors);
985 }
77 //-----------------------------------------------------------------------------
88
99 #include "cxoModule.h"
10
11 //-----------------------------------------------------------------------------
12 // cxoUtils_convertOciAttrToPythonValue()
13 // Convert the OCI attribute value to an equivalent Python value using the
14 // specified type.
15 //-----------------------------------------------------------------------------
16 PyObject *cxoUtils_convertOciAttrToPythonValue(unsigned attrType,
17 dpiDataBuffer *value, uint32_t valueLength, const char *encoding)
18 {
19 switch (attrType) {
20 case CXO_OCI_ATTR_TYPE_STRING:
21 if (!value->asString) {
22 Py_RETURN_NONE;
23 }
24 return PyUnicode_Decode(value->asString, valueLength, encoding,
25 NULL);
26 case CXO_OCI_ATTR_TYPE_BOOLEAN:
27 if (value->asBoolean) {
28 Py_RETURN_TRUE;
29 }
30 Py_RETURN_FALSE;
31 case CXO_OCI_ATTR_TYPE_UINT8:
32 return PyLong_FromUnsignedLong(value->asUint8);
33 case CXO_OCI_ATTR_TYPE_UINT16:
34 return PyLong_FromUnsignedLong(value->asUint16);
35 case CXO_OCI_ATTR_TYPE_UINT32:
36 return PyLong_FromUnsignedLong(value->asUint32);
37 case CXO_OCI_ATTR_TYPE_UINT64:
38 return PyLong_FromUnsignedLongLong(value->asUint64);
39 }
40
41 return cxoError_raiseFromString(cxoProgrammingErrorException,
42 "invalid attribute type specified");
43 }
44
45
46 //-----------------------------------------------------------------------------
47 // cxoUtils_convertPythonValueToOciAttr()
48 // Convert the Python value to an equivalent OCI attribute value using the
49 // specified type.
50 //-----------------------------------------------------------------------------
51 int cxoUtils_convertPythonValueToOciAttr(PyObject *value, unsigned attrType,
52 cxoBuffer *buffer, dpiDataBuffer *ociBuffer, void **ociValue,
53 uint32_t *ociValueLength, const char *encoding)
54 {
55 unsigned long tempValue;
56
57 switch (attrType) {
58 case CXO_OCI_ATTR_TYPE_STRING:
59 if (cxoBuffer_fromObject(buffer, value, encoding) < 0)
60 return -1;
61 *ociValue = (void*) buffer->ptr;
62 *ociValueLength = (uint32_t) buffer->size;
63 break;
64 case CXO_OCI_ATTR_TYPE_BOOLEAN:
65 ociBuffer->asBoolean = PyObject_IsTrue(value);
66 if (PyErr_Occurred())
67 return -1;
68 *ociValue = &ociBuffer->asBoolean;
69 *ociValueLength = sizeof(ociBuffer->asBoolean);
70 break;
71 case CXO_OCI_ATTR_TYPE_UINT8:
72 tempValue = PyLong_AsUnsignedLong(value);
73 if (PyErr_Occurred())
74 return -1;
75 if (tempValue > UINT8_MAX) {
76 PyErr_SetString(PyExc_OverflowError,
77 "Python int too large to convert to uint8_t");
78 return -1;
79 }
80 ociBuffer->asUint8 = (uint8_t) tempValue;
81 *ociValue = &ociBuffer->asUint8;
82 *ociValueLength = sizeof(ociBuffer->asUint8);
83 break;
84 case CXO_OCI_ATTR_TYPE_UINT16:
85 tempValue = PyLong_AsUnsignedLong(value);
86 if (PyErr_Occurred())
87 return -1;
88 if (tempValue > UINT16_MAX) {
89 PyErr_SetString(PyExc_OverflowError,
90 "Python int too large to convert to uint16_t");
91 return -1;
92 }
93 ociBuffer->asUint16 = (uint16_t) tempValue;
94 *ociValue = &ociBuffer->asUint16;
95 *ociValueLength = sizeof(ociBuffer->asUint16);
96 break;
97 case CXO_OCI_ATTR_TYPE_UINT32:
98 tempValue = PyLong_AsUnsignedLong(value);
99 if (PyErr_Occurred())
100 return -1;
101 if (tempValue > UINT32_MAX) {
102 PyErr_SetString(PyExc_OverflowError,
103 "Python int too large to convert to uint32_t");
104 return -1;
105 }
106 ociBuffer->asUint32 = (uint32_t) tempValue;
107 *ociValue = &ociBuffer->asUint32;
108 *ociValueLength = sizeof(ociBuffer->asUint32);
109 break;
110 case CXO_OCI_ATTR_TYPE_UINT64:
111 ociBuffer->asUint64 = (uint64_t) PyLong_AsUnsignedLongLong(value);
112 if (PyErr_Occurred())
113 return -1;
114 *ociValue = &ociBuffer->asUint64;
115 *ociValueLength = sizeof(ociBuffer->asUint64);
116 break;
117 default:
118 cxoError_raiseFromString(cxoProgrammingErrorException,
119 "invalid attribute type specified");
120 return -1;
121 }
122
123 return 0;
124 }
125
10126
11127 //-----------------------------------------------------------------------------
12128 // cxoUtils_formatString()
24140 return NULL;
25141
26142 // convert string format to Python object
27 #if PY_MAJOR_VERSION >= 3
28143 formatObj = PyUnicode_DecodeASCII(format, strlen(format), NULL);
29 #else
30 formatObj = PyString_FromString(format);
31 #endif
32144 if (!formatObj) {
33145 Py_DECREF(args);
34146 return NULL;
35147 }
36148
37149 // create formatted result
38 #if PY_MAJOR_VERSION >= 3
39150 result = PyUnicode_Format(formatObj, args);
40 #else
41 result = PyString_Format(formatObj, args);
42 #endif
43151 Py_DECREF(args);
44152 Py_DECREF(formatObj);
45153 return result;
113221 // work as expected. It also has the additional benefit of reducing the number
114222 // of errors that can take place when the module is imported.
115223 //-----------------------------------------------------------------------------
116 int cxoUtils_initializeDPI(void)
117 {
224 int cxoUtils_initializeDPI(dpiContextCreateParams *params)
225 {
226 dpiContextCreateParams localParams;
118227 dpiErrorInfo errorInfo;
119228 dpiContext *context;
120229
121 if (!cxoDpiContext) {
122 if (dpiContext_create(DPI_MAJOR_VERSION, DPI_MINOR_VERSION,
123 &context, &errorInfo) < 0)
124 return cxoError_raiseFromInfo(&errorInfo);
125 if (dpiContext_getClientVersion(context, &cxoClientVersionInfo) < 0)
126 return cxoError_raiseAndReturnInt();
127 cxoDpiContext = context;
128 }
129
230 // if already initialized and parameters were passed, raise an exception;
231 // otherwise do nothing as this is implicitly called when creating a
232 // standalone connection or session pool and when getting the Oracle Client
233 // library version
234 if (cxoDpiContext) {
235 if (!params)
236 return 0;
237 cxoError_raiseFromString(cxoProgrammingErrorException,
238 "Oracle Client library has already been initialized");
239 return -1;
240 }
241
242 // set up parameters used for initializing ODPI-C
243 if (params) {
244 memcpy(&localParams, params, sizeof(dpiContextCreateParams));
245 } else {
246 memset(&localParams, 0, sizeof(dpiContextCreateParams));
247 }
248 localParams.defaultEncoding = "UTF-8";
249 if (!localParams.defaultDriverName)
250 localParams.defaultDriverName = CXO_DRIVER_NAME;
251 if (!localParams.loadErrorUrl)
252 localParams.loadErrorUrl = "https://cx-oracle.readthedocs.io/en/"
253 "latest/user_guide/installation.html";
254
255 // create ODPI-C context with the specified parameters
256 if (dpiContext_createWithParams(DPI_MAJOR_VERSION, DPI_MINOR_VERSION,
257 &localParams, &context, &errorInfo) < 0)
258 return cxoError_raiseFromInfo(&errorInfo);
259 if (dpiContext_getClientVersion(context, &cxoClientVersionInfo) < 0) {
260 cxoError_raiseAndReturnInt();
261 dpiContext_destroy(context);
262 return -1;
263 }
264
265 cxoDpiContext = context;
130266 return 0;
131267 }
132268
164300 // a key or media type specified.
165301 //-----------------------------------------------------------------------------
166302 int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg,
167 cxoSodaDoc **doc)
168 {
169 dpiSodaDoc *handle;
303 dpiSodaDoc **handle)
304 {
170305 cxoBuffer buffer;
306 cxoSodaDoc *doc;
171307
172308 if (PyObject_TypeCheck(arg, &cxoPyTypeSodaDoc)) {
173 Py_INCREF(arg);
174 *doc = (cxoSodaDoc*) arg;
309 doc = (cxoSodaDoc*) arg;
310 if (dpiSodaDoc_addRef(doc->handle) < 0)
311 return cxoError_raiseAndReturnInt();
312 *handle = doc->handle;
175313 } else if (PyDict_Check(arg) || PyList_Check(arg)) {
176314 arg = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, arg, NULL);
177315 if (!arg)
182320 }
183321 Py_DECREF(arg);
184322 if (dpiSodaDb_createDocument(db->handle, NULL, 0, buffer.ptr,
185 buffer.size, NULL, 0, DPI_SODA_FLAGS_DEFAULT, &handle) < 0) {
186 cxoError_raiseAndReturnNull();
323 buffer.size, NULL, 0, DPI_SODA_FLAGS_DEFAULT, handle) < 0) {
187324 cxoBuffer_clear(&buffer);
188 return -1;
325 return cxoError_raiseAndReturnInt();
189326 }
190327 cxoBuffer_clear(&buffer);
191 *doc = cxoSodaDoc_new(db, handle);
192 if (!*doc)
193 return -1;
194328 } else {
195329 PyErr_SetString(PyExc_TypeError,
196 "value must be a SODA document or dictionary");
197 return -1;
198 }
199
200 return 0;
201 }
202
330 "value must be a SODA document or a dictionary or list");
331 return -1;
332 }
333
334 return 0;
335 }
00 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
1 // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
22 //
33 // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
44 //
1414 #include "cxoModule.h"
1515
1616 //-----------------------------------------------------------------------------
17 // Declaration of common variable functions.
18 //-----------------------------------------------------------------------------
19 static void cxoVar_free(cxoVar*);
20 static PyObject *cxoVar_repr(cxoVar*);
21 static PyObject *cxoVar_externalCopy(cxoVar*, PyObject*);
22 static PyObject *cxoVar_externalSetValue(cxoVar*, PyObject*);
23 static PyObject *cxoVar_externalGetValue(cxoVar*, PyObject*, PyObject*);
24 static PyObject *cxoVar_externalGetActualElements(cxoVar*, void*);
25 static PyObject *cxoVar_externalGetValues(cxoVar*, void*);
26
27
28 //-----------------------------------------------------------------------------
29 // declaration of members for variables
30 //-----------------------------------------------------------------------------
31 static PyMemberDef cxoVarMembers[] = {
32 { "bufferSize", T_INT, offsetof(cxoVar, bufferSize), READONLY },
33 { "inconverter", T_OBJECT, offsetof(cxoVar, inConverter), 0 },
34 { "numElements", T_INT, offsetof(cxoVar, allocatedElements),
35 READONLY },
36 { "outconverter", T_OBJECT, offsetof(cxoVar, outConverter), 0 },
37 { "size", T_INT, offsetof(cxoVar, size), READONLY },
38 { "type", T_OBJECT, offsetof(cxoVar, objectType), READONLY },
39 { NULL }
40 };
41
42
43 //-----------------------------------------------------------------------------
44 // declaration of calculated members for variables
45 //-----------------------------------------------------------------------------
46 static PyGetSetDef cxoVarCalcMembers[] = {
47 { "actualElements", (getter) cxoVar_externalGetActualElements, 0, 0, 0 },
48 { "values", (getter) cxoVar_externalGetValues, 0, 0, 0 },
49 { NULL }
50 };
51
52
53 //-----------------------------------------------------------------------------
54 // declaration of methods for variables
55 //-----------------------------------------------------------------------------
56 static PyMethodDef cxoVarMethods[] = {
57 { "copy", (PyCFunction) cxoVar_externalCopy, METH_VARARGS },
58 { "setvalue", (PyCFunction) cxoVar_externalSetValue, METH_VARARGS },
59 { "getvalue", (PyCFunction) cxoVar_externalGetValue,
60 METH_VARARGS | METH_KEYWORDS },
61 { NULL }
62 };
63
64
65 //-----------------------------------------------------------------------------
66 // declaration of all variable types
67 //-----------------------------------------------------------------------------
68 #define DECLARE_VARIABLE_TYPE(INTERNAL_NAME, EXTERNAL_NAME) \
69 PyTypeObject INTERNAL_NAME = { \
70 PyVarObject_HEAD_INIT(NULL, 0) \
71 "cx_Oracle." #EXTERNAL_NAME, /* tp_name */ \
72 sizeof(cxoVar), /* tp_basicsize */ \
73 0, /* tp_itemsize */ \
74 (destructor) cxoVar_free, /* tp_dealloc */ \
75 0, /* tp_print */ \
76 0, /* tp_getattr */ \
77 0, /* tp_setattr */ \
78 0, /* tp_compare */ \
79 (reprfunc) cxoVar_repr, /* tp_repr */ \
80 0, /* tp_as_number */ \
81 0, /* tp_as_sequence */ \
82 0, /* tp_as_mapping */ \
83 0, /* tp_hash */ \
84 0, /* tp_call */ \
85 0, /* tp_str */ \
86 0, /* tp_getattro */ \
87 0, /* tp_setattro */ \
88 0, /* tp_as_buffer */ \
89 Py_TPFLAGS_DEFAULT, /* tp_flags */ \
90 0, /* tp_doc */ \
91 0, /* tp_traverse */ \
92 0, /* tp_clear */ \
93 0, /* tp_richcompare */ \
94 0, /* tp_weaklistoffset */ \
95 0, /* tp_iter */ \
96 0, /* tp_iternext */ \
97 cxoVarMethods, /* tp_methods */ \
98 cxoVarMembers, /* tp_members */ \
99 cxoVarCalcMembers /* tp_getset */ \
100 };
101
102 DECLARE_VARIABLE_TYPE(cxoPyTypeBfileVar, BFILE)
103 DECLARE_VARIABLE_TYPE(cxoPyTypeBinaryVar, BINARY)
104 DECLARE_VARIABLE_TYPE(cxoPyTypeBlobVar, BLOB)
105 DECLARE_VARIABLE_TYPE(cxoPyTypeBooleanVar, BOOLEAN)
106 DECLARE_VARIABLE_TYPE(cxoPyTypeClobVar, CLOB)
107 DECLARE_VARIABLE_TYPE(cxoPyTypeCursorVar, CURSOR)
108 DECLARE_VARIABLE_TYPE(cxoPyTypeDateTimeVar, DATETIME)
109 DECLARE_VARIABLE_TYPE(cxoPyTypeFixedCharVar, FIXED_CHAR)
110 DECLARE_VARIABLE_TYPE(cxoPyTypeFixedNcharVar, FIXED_NCHAR)
111 DECLARE_VARIABLE_TYPE(cxoPyTypeIntervalVar, INTERVAL)
112 DECLARE_VARIABLE_TYPE(cxoPyTypeLongBinaryVar, LONG_BINARY)
113 DECLARE_VARIABLE_TYPE(cxoPyTypeLongStringVar, LONG_STRING)
114 DECLARE_VARIABLE_TYPE(cxoPyTypeNativeFloatVar, NATIVE_FLOAT)
115 DECLARE_VARIABLE_TYPE(cxoPyTypeNativeIntVar, NATIVE_INT)
116 DECLARE_VARIABLE_TYPE(cxoPyTypeNcharVar, NCHAR)
117 DECLARE_VARIABLE_TYPE(cxoPyTypeNclobVar, NCLOB)
118 DECLARE_VARIABLE_TYPE(cxoPyTypeNumberVar, NUMBER)
119 DECLARE_VARIABLE_TYPE(cxoPyTypeObjectVar, OBJECT)
120 DECLARE_VARIABLE_TYPE(cxoPyTypeRowidVar, ROWID)
121 DECLARE_VARIABLE_TYPE(cxoPyTypeStringVar, STRING)
122 DECLARE_VARIABLE_TYPE(cxoPyTypeTimestampVar, TIMESTAMP)
123
124
125 //-----------------------------------------------------------------------------
12617 // cxoVar_new()
12718 // Allocate a new variable.
12819 //-----------------------------------------------------------------------------
129 cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
130 Py_ssize_t size, int isArray, cxoObjectType *objType)
20 cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements,
21 cxoTransformNum transformNum, Py_ssize_t size, int isArray,
22 cxoObjectType *objType)
13123 {
13224 dpiObjectType *typeHandle = NULL;
13325 dpiOracleTypeNum oracleTypeNum;
134 dpiNativeTypeNum nativeTypeNum;
13526 cxoVar *var;
13627
13728 // attempt to allocate the object
138 var = (cxoVar*) type->pythonType->tp_alloc(type->pythonType, 0);
29 var = (cxoVar*) cxoPyTypeVar.tp_alloc(&cxoPyTypeVar, 0);
13930 if (!var)
14031 return NULL;
14132
15041 if (numElements == 0)
15142 numElements = 1;
15243 var->allocatedElements = (uint32_t) numElements;
153 var->type = type;
154 var->size = (size == 0) ? type->size : (uint32_t) size;
44 var->transformNum = transformNum;
45 var->size = (uint32_t) size;
46 if (var->size == 0)
47 var->size = cxoTransform_getDefaultSize(transformNum);
15548 var->isArray = isArray;
15649
50 // determine database type
51 var->dbType = cxoDbType_fromTransformNum(var->transformNum);
52 if (!var->dbType) {
53 Py_DECREF(var);
54 return NULL;
55 }
56 Py_INCREF(var->dbType);
57
15758 // acquire and initialize DPI variable
158 cxoTransform_getTypeInfo(type->transformNum, &oracleTypeNum,
159 &nativeTypeNum);
59 cxoTransform_getTypeInfo(transformNum, &oracleTypeNum,
60 &var->nativeTypeNum);
16061 if (dpiConn_newVar(cursor->connection->handle, oracleTypeNum,
161 nativeTypeNum, var->allocatedElements, var->size, 0, isArray,
62 var->nativeTypeNum, var->allocatedElements, var->size, 0, isArray,
16263 typeHandle, &var->handle, &var->data) < 0) {
16364 cxoError_raiseAndReturnNull();
16465 Py_DECREF(var);
19495 Py_CLEAR(var->inConverter);
19596 Py_CLEAR(var->outConverter);
19697 Py_CLEAR(var->objectType);
98 Py_CLEAR(var->dbType);
19799 Py_TYPE(var)->tp_free((PyObject*) var);
198100 }
199101
204106 //-----------------------------------------------------------------------------
205107 int cxoVar_check(PyObject *object)
206108 {
207 PyTypeObject *objectType = Py_TYPE(object);
208
209 return (objectType == &cxoPyTypeBfileVar ||
210 objectType == &cxoPyTypeBinaryVar ||
211 objectType == &cxoPyTypeBlobVar ||
212 objectType == &cxoPyTypeBooleanVar ||
213 objectType == &cxoPyTypeClobVar ||
214 objectType == &cxoPyTypeCursorVar ||
215 objectType == &cxoPyTypeDateTimeVar ||
216 objectType == &cxoPyTypeFixedCharVar ||
217 objectType == &cxoPyTypeFixedNcharVar ||
218 objectType == &cxoPyTypeIntervalVar ||
219 objectType == &cxoPyTypeLongBinaryVar ||
220 objectType == &cxoPyTypeLongStringVar ||
221 objectType == &cxoPyTypeNativeFloatVar ||
222 objectType == &cxoPyTypeNativeIntVar ||
223 objectType == &cxoPyTypeNcharVar ||
224 objectType == &cxoPyTypeNclobVar ||
225 objectType == &cxoPyTypeNumberVar ||
226 objectType == &cxoPyTypeObjectVar ||
227 objectType == &cxoPyTypeRowidVar ||
228 objectType == &cxoPyTypeStringVar ||
229 objectType == &cxoPyTypeTimestampVar);
109 return (Py_TYPE(object) == &cxoPyTypeVar);
230110 }
231111
232112
239119 {
240120 PyObject *result, *inputTypeHandler = NULL;
241121 cxoObjectType *objType = NULL;
242 cxoVarType *varType;
122 cxoTransformNum transformNum;
243123 Py_ssize_t size;
244124 cxoObject *obj;
245125 int isArray;
273153 }
274154
275155 // default processing
276 varType = cxoVarType_fromPythonValue(value,
277 &isArray, &size, &numElements, cursor->stmtInfo.isPLSQL);
278 if (!varType)
279 return NULL;
280 if (varType->transformNum == CXO_TRANSFORM_OBJECT) {
156 if (cxoTransform_getNumFromValue(value, &isArray, &size, &numElements,
157 cursor->stmtInfo.isPLSQL, &transformNum) < 0)
158 return NULL;
159 if (transformNum == CXO_TRANSFORM_OBJECT) {
281160 obj = (cxoObject*) value;
282161 objType = obj->objectType;
283162 }
284 return cxoVar_new(cursor, numElements, varType, size, isArray, objType);
163 return cxoVar_new(cursor, numElements, transformNum, size, isArray,
164 objType);
285165 }
286166
287167
293173 PyObject *value)
294174 {
295175 PyObject *typeObj, *numElementsObj;
176 cxoTransformNum transformNum;
296177 cxoObjectType *objType;
297 cxoVarType *varType;
298178 uint32_t numElements;
299179 int ok;
300180
302182 ok = (PyList_GET_SIZE(value) == 2);
303183 if (ok) {
304184 typeObj = PyList_GET_ITEM(value, 0);
305 ok = PyType_Check(typeObj);
306 }
307 if (ok) {
308185 numElementsObj = PyList_GET_ITEM(value, 1);
309 ok = PyInt_Check(numElementsObj);
186 ok = PyLong_Check(numElementsObj);
310187 }
311188 if (!ok) {
312189 cxoError_raiseFromString(cxoProgrammingErrorException,
315192 }
316193
317194 // create variable
318 varType = cxoVarType_fromPythonType(typeObj, &objType);
319 if (!varType)
320 return NULL;
321 numElements = PyInt_AsLong(numElementsObj);
195 if (cxoTransform_getNumFromType(typeObj, &transformNum, &objType) < 0)
196 return NULL;
197 numElements = PyLong_AsLong(numElementsObj);
322198 if (PyErr_Occurred())
323199 return NULL;
324 return cxoVar_new(cursor, numElements, varType, varType->size, 1, objType);
200 return cxoVar_new(cursor, numElements, transformNum, 0, 1, objType);
325201 }
326202
327203
332208 cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
333209 uint32_t numElements)
334210 {
211 cxoTransformNum transformNum;
335212 cxoObjectType *objType;
336 cxoVarType *varType;
337213 long size;
338214
339215 // passing an integer is assumed to be a string
340 if (PyInt_Check(value)) {
341 size = PyInt_AsLong(value);
216 if (PyLong_Check(value)) {
217 size = PyLong_AsLong(value);
342218 if (PyErr_Occurred())
343219 return NULL;
344 varType = cxoVarType_fromPythonType((PyObject*) &cxoPyTypeString,
345 &objType);
346 return cxoVar_new(cursor, numElements, varType, size, 0, objType);
220 return cxoVar_new(cursor, numElements, CXO_TRANSFORM_STRING, size, 0,
221 NULL);
347222 }
348223
349224 // passing an array of two elements to define an array
356231 return (cxoVar*) value;
357232 }
358233
359 // everything else ought to be a Python type
360 if (PyType_Check(value)) {
361 varType = cxoVarType_fromPythonType(value, &objType);
362 if (!varType)
363 return NULL;
364 return cxoVar_new(cursor, numElements, varType, varType->size, 0,
365 objType);
366 }
367
368 PyErr_SetString(PyExc_TypeError, "expecting type");
369 return NULL;
234 // everything else ought to be a Python type, database type constant or
235 // object type
236 if (cxoTransform_getNumFromType(value, &transformNum, &objType) < 0)
237 return NULL;
238 return cxoVar_new(cursor, numElements, transformNum, 0, 0, objType);
370239 }
371240
372241
452321 else data = &var->data[arrayPos];
453322 if (data->isNull)
454323 Py_RETURN_NONE;
455 value = cxoTransform_toPython(var->type->transformNum, var->connection,
324 value = cxoTransform_toPython(var->transformNum, var->connection,
456325 var->objectType, &data->value, var->encodingErrors);
457326 if (value) {
458 switch (var->type->transformNum) {
327 switch (var->transformNum) {
459328 case CXO_TRANSFORM_BFILE:
460329 case CXO_TRANSFORM_BLOB:
461330 case CXO_TRANSFORM_CLOB:
517386 int status;
518387
519388 if (buffer->size > var->bufferSize) {
520 cxoTransform_getTypeInfo(var->type->transformNum, &oracleTypeNum,
389 cxoTransform_getTypeInfo(var->transformNum, &oracleTypeNum,
521390 &nativeTypeNum);
522391 if (dpiConn_newVar(var->connection->handle, oracleTypeNum,
523392 nativeTypeNum, var->allocatedElements, buffer->size, 0,
592461 cursor->handle = data->value.asStmt;
593462 dpiStmt_addRef(cursor->handle);
594463 }
464
465 if (dpiStmt_setPrefetchRows(cursor->handle, cursor->prefetchRows) < 0)
466 return cxoError_raiseAndReturnInt();
467
595468 cursor->fixupRefCursor = 1;
596469 return 0;
597470 }
606479 {
607480 dpiDataBuffer tempDbValue, *dbValue;
608481 PyObject *convertedValue = NULL;
482 dpiNativeTypeNum nativeTypeNum;
609483 cxoBuffer buffer;
610484 int result = 0;
611485 dpiData *data;
630504 data = &var->data[arrayPos];
631505 data->isNull = (value == Py_None);
632506 if (!data->isNull) {
633 if (var->type->transformNum == CXO_TRANSFORM_CURSOR)
507 if (var->transformNum == CXO_TRANSFORM_CURSOR)
634508 result = cxoVar_setValueCursor(var, arrayPos, data, value);
635509 else {
636510 cxoBuffer_init(&buffer);
637 if (var->type->size > 0)
511 if (var->nativeTypeNum == DPI_NATIVE_TYPE_BYTES)
638512 dbValue = &tempDbValue;
639513 else dbValue = &data->value;
640 result = cxoTransform_fromPython(var->type->transformNum, value,
641 dbValue, &buffer, var->connection->encodingInfo.encoding,
514 result = cxoTransform_fromPython(var->transformNum,
515 &nativeTypeNum, value, dbValue, &buffer,
516 var->connection->encodingInfo.encoding,
642517 var->connection->encodingInfo.nencoding, var, arrayPos);
643 if (result == 0 && var->type->size > 0)
518 if (result == 0 && var->nativeTypeNum == DPI_NATIVE_TYPE_BYTES)
644519 result = cxoVar_setValueBytes(var, arrayPos, data, &buffer);
645520 cxoBuffer_clear(&buffer);
646521 }
767642 if (var->isArray &&
768643 dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
769644 return cxoError_raiseAndReturnNull();
770 return PyInt_FromLong(numElements);
645 return PyLong_FromLong(numElements);
771646 }
772647
773648
787662
788663
789664 //-----------------------------------------------------------------------------
665 // cxoVar_getType()
666 // Return the type associated with the variable. This is either an object
667 // type or one of the database type constants.
668 //-----------------------------------------------------------------------------
669 static PyObject *cxoVar_getType(cxoVar *var, void *unused)
670 {
671 if (var->objectType) {
672 Py_INCREF(var->objectType);
673 return (PyObject*) var->objectType;
674 }
675
676 Py_INCREF(var->dbType);
677 return (PyObject*) var->dbType;
678 }
679
680
681 //-----------------------------------------------------------------------------
790682 // cxoVar_repr()
791683 // Return a string representation of the variable.
792684 //-----------------------------------------------------------------------------
793685 static PyObject *cxoVar_repr(cxoVar *var)
794686 {
795 PyObject *value, *module, *name, *result;
687 PyObject *value, *module, *name, *result, *typeName;
796688 uint32_t numElements;
797689
798690 if (var->isArray) {
804696 else value = cxoVar_getArrayValue(var, var->allocatedElements, NULL);
805697 if (!value)
806698 return NULL;
699 typeName = PyUnicode_DecodeASCII(var->dbType->name,
700 strlen(var->dbType->name), NULL);
701 if (!typeName) {
702 Py_DECREF(value);
703 return NULL;
704 }
807705 if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) {
706 Py_DECREF(typeName);
808707 Py_DECREF(value);
809708 return NULL;
810709 }
811 result = cxoUtils_formatString("<%s.%s with value %r>",
812 PyTuple_Pack(3, module, name, value));
710 result = cxoUtils_formatString("<%s.%s of type %s with value %r>",
711 PyTuple_Pack(4, module, name, typeName, value));
813712 Py_DECREF(module);
814713 Py_DECREF(name);
815714 Py_DECREF(value);
715 Py_DECREF(typeName);
816716 return result;
817717 }
818718
719
720 //-----------------------------------------------------------------------------
721 // declaration of members
722 //-----------------------------------------------------------------------------
723 static PyMemberDef cxoMembers[] = {
724 { "bufferSize", T_INT, offsetof(cxoVar, bufferSize), READONLY },
725 { "inconverter", T_OBJECT, offsetof(cxoVar, inConverter), 0 },
726 { "numElements", T_INT, offsetof(cxoVar, allocatedElements),
727 READONLY },
728 { "outconverter", T_OBJECT, offsetof(cxoVar, outConverter), 0 },
729 { "size", T_INT, offsetof(cxoVar, size), READONLY },
730 { NULL }
731 };
732
733
734 //-----------------------------------------------------------------------------
735 // declaration of calculated members
736 //-----------------------------------------------------------------------------
737 static PyGetSetDef cxoCalcMembers[] = {
738 { "actualElements", (getter) cxoVar_externalGetActualElements, 0, 0, 0 },
739 { "type", (getter) cxoVar_getType, 0, 0, 0 },
740 { "values", (getter) cxoVar_externalGetValues, 0, 0, 0 },
741 { NULL }
742 };
743
744
745 //-----------------------------------------------------------------------------
746 // declaration of methods
747 //-----------------------------------------------------------------------------
748 static PyMethodDef cxoVarMethods[] = {
749 { "copy", (PyCFunction) cxoVar_externalCopy, METH_VARARGS },
750 { "setvalue", (PyCFunction) cxoVar_externalSetValue, METH_VARARGS },
751 { "getvalue", (PyCFunction) cxoVar_externalGetValue,
752 METH_VARARGS | METH_KEYWORDS },
753 { NULL }
754 };
755
756
757 //-----------------------------------------------------------------------------
758 // declaration of Python type
759 //-----------------------------------------------------------------------------
760 PyTypeObject cxoPyTypeVar = {
761 PyVarObject_HEAD_INIT(NULL, 0)
762 .tp_name = "cx_Oracle.Var",
763 .tp_basicsize = sizeof(cxoVar),
764 .tp_dealloc = (destructor) cxoVar_free,
765 .tp_repr = (reprfunc) cxoVar_repr,
766 .tp_flags = Py_TPFLAGS_DEFAULT,
767 .tp_methods = cxoVarMethods,
768 .tp_members = cxoMembers,
769 .tp_getset = cxoCalcMembers
770 };
+0
-309
src/cxoVarType.c less more
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoVarType.c
6 // Defines variable types for various transformations.
7 //-----------------------------------------------------------------------------
8
9 #include "cxoModule.h"
10
11 // define variable types for each of the supported transforms
12 static cxoVarType cxoAllVarTypes[] = {
13 {
14 CXO_TRANSFORM_NONE,
15 &cxoPyTypeStringVar,
16 1
17 },
18 {
19 CXO_TRANSFORM_BINARY,
20 &cxoPyTypeBinaryVar,
21 4000
22 },
23 {
24 CXO_TRANSFORM_BFILE,
25 &cxoPyTypeBfileVar,
26 0
27 },
28 {
29 CXO_TRANSFORM_BLOB,
30 &cxoPyTypeBlobVar,
31 0
32 },
33 {
34 CXO_TRANSFORM_BOOLEAN,
35 &cxoPyTypeBooleanVar,
36 0
37 },
38 {
39 CXO_TRANSFORM_CLOB,
40 &cxoPyTypeClobVar,
41 0
42 },
43 {
44 CXO_TRANSFORM_CURSOR,
45 &cxoPyTypeCursorVar,
46 0
47 },
48 {
49 CXO_TRANSFORM_DATE,
50 &cxoPyTypeDateTimeVar,
51 0
52 },
53 {
54 CXO_TRANSFORM_DATETIME,
55 &cxoPyTypeDateTimeVar,
56 0
57 },
58 {
59 CXO_TRANSFORM_DECIMAL,
60 &cxoPyTypeNumberVar,
61 1000
62 },
63 {
64 CXO_TRANSFORM_FIXED_CHAR,
65 &cxoPyTypeFixedCharVar,
66 2000
67 },
68 {
69 CXO_TRANSFORM_FIXED_NCHAR,
70 &cxoPyTypeFixedNcharVar,
71 2000
72 },
73 {
74 CXO_TRANSFORM_FLOAT,
75 &cxoPyTypeNumberVar,
76 1000
77 },
78 {
79 CXO_TRANSFORM_INT,
80 &cxoPyTypeNumberVar,
81 1000
82 },
83 {
84 CXO_TRANSFORM_LONG_BINARY,
85 &cxoPyTypeLongBinaryVar,
86 128 * 1024
87 },
88 {
89 CXO_TRANSFORM_LONG_STRING,
90 &cxoPyTypeLongStringVar,
91 128 * 1024
92 },
93 {
94 CXO_TRANSFORM_NATIVE_DOUBLE,
95 &cxoPyTypeNativeFloatVar,
96 0
97 },
98 {
99 CXO_TRANSFORM_NATIVE_FLOAT,
100 &cxoPyTypeNativeFloatVar,
101 0
102 },
103 {
104 CXO_TRANSFORM_NATIVE_INT,
105 &cxoPyTypeNativeIntVar,
106 0
107 },
108 {
109 CXO_TRANSFORM_NCLOB,
110 &cxoPyTypeNclobVar,
111 0
112 },
113 {
114 CXO_TRANSFORM_NSTRING,
115 &cxoPyTypeNcharVar,
116 4000
117 },
118 {
119 CXO_TRANSFORM_OBJECT,
120 &cxoPyTypeObjectVar,
121 0
122 },
123 {
124 CXO_TRANSFORM_ROWID,
125 &cxoPyTypeRowidVar,
126 0
127 },
128 {
129 CXO_TRANSFORM_STRING,
130 &cxoPyTypeStringVar,
131 4000
132 },
133 {
134 CXO_TRANSFORM_TIMEDELTA,
135 &cxoPyTypeIntervalVar,
136 0
137 },
138 {
139 CXO_TRANSFORM_TIMESTAMP,
140 &cxoPyTypeTimestampVar,
141 0
142 },
143 {
144 CXO_TRANSFORM_TIMESTAMP_LTZ,
145 &cxoPyTypeTimestampVar,
146 0
147 }
148 };
149
150
151 //-----------------------------------------------------------------------------
152 // cxoVarType_fromDataTypeInfo()
153 // Return a variable type given query metadata, or NULL indicating that the
154 // data indicated by the query metadata is not supported.
155 //-----------------------------------------------------------------------------
156 cxoVarType *cxoVarType_fromDataTypeInfo(dpiDataTypeInfo *info)
157 {
158 cxoTransformNum transformNum;
159 char message[120];
160
161 transformNum = cxoTransform_getNumFromDataTypeInfo(info);
162 if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
163 snprintf(message, sizeof(message), "Oracle type %d not supported.",
164 info->oracleTypeNum);
165 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
166 return NULL;
167 }
168 return &cxoAllVarTypes[transformNum];
169 }
170
171
172 //-----------------------------------------------------------------------------
173 // cxoVarType_fromPythonType()
174 // Return a variable type given a Python type object or NULL if the Python
175 // type does not have a corresponding variable type. If the type provided is an
176 // object type, return that as well.
177 //-----------------------------------------------------------------------------
178 cxoVarType *cxoVarType_fromPythonType(PyObject *type, cxoObjectType **objType)
179 {
180 cxoTransformNum transformNum;
181 PyTypeObject *pyType;
182 char message[250];
183
184 if (Py_TYPE(type) == &cxoPyTypeObjectType) {
185 transformNum = CXO_TRANSFORM_OBJECT;
186 *objType = (cxoObjectType*) type;
187 } else if (Py_TYPE(type) != &PyType_Type) {
188 PyErr_SetString(PyExc_TypeError, "expecting type");
189 return NULL;
190 } else {
191 *objType = NULL;
192 pyType = (PyTypeObject*) type;
193 transformNum = cxoTransform_getNumFromType(pyType);
194 if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
195 snprintf(message, sizeof(message), "Python type %s not supported.",
196 pyType->tp_name);
197 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
198 return NULL;
199 }
200 }
201 return &cxoAllVarTypes[transformNum];
202 }
203
204
205 //-----------------------------------------------------------------------------
206 // cxoVarType_calculateSize()
207 // Calculate the size to use with the specified transform and Python value.
208 // This function is only called by cxoVarType_fromPythonValue() and no attempt
209 // is made to verify the value further.
210 //-----------------------------------------------------------------------------
211 static Py_ssize_t cxoVarType_calculateSize(PyObject *value,
212 cxoTransformNum transformNum)
213 {
214 Py_ssize_t size = 0;
215 #if PY_MAJOR_VERSION < 3
216 const void *ptr;
217 #endif
218
219 switch (transformNum) {
220 case CXO_TRANSFORM_NONE:
221 return 1;
222 case CXO_TRANSFORM_BINARY:
223 #if PY_MAJOR_VERSION >= 3
224 return PyBytes_GET_SIZE(value);
225 #else
226 PyObject_AsReadBuffer(value, &ptr, &size);
227 return size;
228 #endif
229 case CXO_TRANSFORM_NSTRING:
230 size = PyUnicode_GET_SIZE(value);
231 return (size == 0) ? 1 : size;
232 case CXO_TRANSFORM_STRING:
233 #if PY_MAJOR_VERSION >= 3
234 size = PyUnicode_GET_SIZE(value);
235 #else
236 size = PyString_GET_SIZE(value);
237 #endif
238 return (size == 0) ? 1 : size;
239 default:
240 break;
241 }
242 return 0;
243 }
244
245
246 //-----------------------------------------------------------------------------
247 // cxoVarType_fromPythonValue()
248 // Return a variable type given a Python object or NULL if the Python object
249 // does not have a corresponding variable type.
250 //-----------------------------------------------------------------------------
251 cxoVarType *cxoVarType_fromPythonValue(PyObject *value, int *isArray,
252 Py_ssize_t *size, Py_ssize_t *numElements, int plsql)
253 {
254 cxoTransformNum transformNum, tempTransformNum;
255 PyObject *elementValue;
256 Py_ssize_t i, tempSize;
257 char message[250];
258
259 // initialization (except numElements which always has a valid value and is
260 // only overridden when a an array is encountered)
261 *size = 0;
262 *isArray = 0;
263
264 // handle arrays
265 if (PyList_Check(value)) {
266 transformNum = CXO_TRANSFORM_NONE;
267 for (i = 0; i < PyList_GET_SIZE(value); i++) {
268 elementValue = PyList_GET_ITEM(value, i);
269 tempTransformNum = cxoTransform_getNumFromValue(elementValue, 1);
270 if (tempTransformNum == CXO_TRANSFORM_UNSUPPORTED) {
271 snprintf(message, sizeof(message),
272 "element %u value is unsupported", (unsigned) i);
273 cxoError_raiseFromString(cxoNotSupportedErrorException,
274 message);
275 return NULL;
276 } else if (transformNum == CXO_TRANSFORM_NONE) {
277 transformNum = tempTransformNum;
278 } else if (transformNum != tempTransformNum) {
279 snprintf(message, sizeof(message),
280 "element %u value is not the same type as previous "
281 "elements", (unsigned) i);
282 cxoError_raiseFromString(cxoNotSupportedErrorException,
283 message);
284 return NULL;
285 }
286 tempSize = cxoVarType_calculateSize(elementValue,
287 tempTransformNum);
288 if (tempSize > *size)
289 *size = tempSize;
290 }
291 *isArray = 1;
292 *numElements = PyList_GET_SIZE(value);
293 return &cxoAllVarTypes[transformNum];
294 }
295
296 // handle scalar values
297 transformNum = cxoTransform_getNumFromValue(value, plsql);
298 if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
299 snprintf(message, sizeof(message),
300 "Python value of type %s not supported.",
301 Py_TYPE(value)->tp_name);
302 cxoError_raiseFromString(cxoNotSupportedErrorException, message);
303 return NULL;
304 }
305 *size = cxoVarType_calculateSize(value, transformNum);
306 return &cxoAllVarTypes[transformNum];
307 }
308
+0
-371
test/AQ.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """Module for testing AQ objects."""
5
6 import TestEnv
7
8 import cx_Oracle
9 import decimal
10 import threading
11
12 class TestCase(TestEnv.BaseTestCase):
13 bookData = [
14 ("Wings of Fire", "A.P.J. Abdul Kalam",
15 decimal.Decimal("15.75")),
16 ("The Story of My Life", "Hellen Keller",
17 decimal.Decimal("10.50")),
18 ("The Chronicles of Narnia", "C.S. Lewis",
19 decimal.Decimal("25.25"))
20 ]
21
22 def __clearBooksQueue(self):
23 booksType = self.connection.gettype("UDT_BOOK")
24 book = booksType.newobject()
25 options = self.connection.deqoptions()
26 options.wait = cx_Oracle.DEQ_NO_WAIT
27 options.deliverymode = cx_Oracle.MSG_PERSISTENT_OR_BUFFERED
28 options.visibility = cx_Oracle.ENQ_IMMEDIATE
29 props = self.connection.msgproperties()
30 while self.connection.deq("BOOKS", options, props, book):
31 pass
32
33 def __deqInThread(self, results):
34 connection = TestEnv.GetConnection()
35 booksType = connection.gettype("UDT_BOOK")
36 book = booksType.newobject()
37 options = connection.deqoptions()
38 options.wait = 10
39 props = connection.msgproperties()
40 if connection.deq("BOOKS", options, props, book):
41 results.append((book.TITLE, book.AUTHORS, book.PRICE))
42 connection.commit()
43
44 def __verifyAttribute(self, obj, attrName, value):
45 setattr(obj, attrName, value)
46 self.assertEqual(getattr(obj, attrName), value)
47
48 def testDeqEmpty(self):
49 "test dequeuing an empty queue"
50 self.__clearBooksQueue()
51 booksType = self.connection.gettype("UDT_BOOK")
52 book = booksType.newobject()
53 options = self.connection.deqoptions()
54 options.wait = cx_Oracle.DEQ_NO_WAIT
55 props = self.connection.msgproperties()
56 messageId = self.connection.deq("BOOKS", options, props, book)
57 self.assertTrue(messageId is None)
58
59 def testDeqEnq(self):
60 "test enqueuing and dequeuing multiple messages"
61 self.__clearBooksQueue()
62 booksType = self.connection.gettype("UDT_BOOK")
63 options = self.connection.enqoptions()
64 props = self.connection.msgproperties()
65 for title, authors, price in self.bookData:
66 book = booksType.newobject()
67 book.TITLE = title
68 book.AUTHORS = authors
69 book.PRICE = price
70 self.connection.enq("BOOKS", options, props, book)
71 options = self.connection.deqoptions()
72 options.navigation = cx_Oracle.DEQ_FIRST_MSG
73 options.wait = cx_Oracle.DEQ_NO_WAIT
74 results = []
75 while self.connection.deq("BOOKS", options, props, book):
76 row = (book.TITLE, book.AUTHORS, book.PRICE)
77 results.append(row)
78 self.connection.commit()
79 self.assertEqual(results, self.bookData)
80
81 def testDeqModeRemoveNoData(self):
82 "test dequeuing with DEQ_REMOVE_NODATA option"
83 self.__clearBooksQueue()
84 booksType = self.connection.gettype("UDT_BOOK")
85 book = booksType.newobject()
86 title, authors, price = self.bookData[1]
87 book.TITLE = title
88 book.AUTHORS = authors
89 book.PRICE = price
90 options = self.connection.enqoptions()
91 props = self.connection.msgproperties()
92 self.connection.enq("BOOKS", options, props, book)
93 options = self.connection.deqoptions()
94 options.navigation = cx_Oracle.DEQ_FIRST_MSG
95 options.wait = cx_Oracle.DEQ_NO_WAIT
96 options.mode = cx_Oracle.DEQ_REMOVE_NODATA
97 book = booksType.newobject()
98 messageId = self.connection.deq("BOOKS", options, props, book)
99 self.connection.commit()
100 self.assertTrue(messageId is not None)
101 self.assertEqual(book.TITLE, "")
102
103 def testDeqOptions(self):
104 "test getting/setting dequeue options attributes"
105 options = self.connection.deqoptions()
106 self.__verifyAttribute(options, "condition", "TEST_CONDITION")
107 self.__verifyAttribute(options, "consumername", "TEST_CONSUMERNAME")
108 self.__verifyAttribute(options, "correlation", "TEST_CORRELATION")
109 self.__verifyAttribute(options, "mode", cx_Oracle.DEQ_LOCKED)
110 self.__verifyAttribute(options, "navigation",
111 cx_Oracle.DEQ_NEXT_TRANSACTION)
112 self.__verifyAttribute(options, "transformation",
113 "TEST_TRANSFORMATION")
114 self.__verifyAttribute(options, "visibility", cx_Oracle.ENQ_IMMEDIATE)
115 self.__verifyAttribute(options, "wait", 1287)
116 self.__verifyAttribute(options, "msgid", b'mID')
117
118 def testDeqWithWait(self):
119 "test waiting for dequeue"
120 self.__clearBooksQueue()
121 results = []
122 thread = threading.Thread(target = self.__deqInThread,
123 args = (results,))
124 thread.start()
125 booksType = self.connection.gettype("UDT_BOOK")
126 book = booksType.newobject()
127 title, authors, price = self.bookData[0]
128 book.TITLE = title
129 book.AUTHORS = authors
130 book.PRICE = price
131 options = self.connection.enqoptions()
132 props = self.connection.msgproperties()
133 self.connection.enq("BOOKS", options, props, book)
134 self.connection.commit()
135 thread.join()
136 self.assertEqual(results, [(title, authors, price)])
137
138 def testEnqOptions(self):
139 "test getting/setting enqueue options attributes"
140 options = self.connection.enqoptions()
141 self.__verifyAttribute(options, "visibility", cx_Oracle.ENQ_IMMEDIATE)
142
143 def testErrorsForInvalidValues(self):
144 "test errors for invalid values for options"
145 booksType = self.connection.gettype("UDT_BOOK")
146 book = booksType.newobject()
147 options = self.connection.enqoptions()
148 props = self.connection.msgproperties()
149 self.assertRaises(TypeError, self.connection.deq, "BOOKS", options,
150 props, book)
151 options = self.connection.deqoptions()
152 self.assertRaises(TypeError, self.connection.enq, "BOOKS", options,
153 props, book)
154
155 def testMsgProps(self):
156 "test getting/setting message properties attributes"
157 props = self.connection.msgproperties()
158 self.__verifyAttribute(props, "correlation", "TEST_CORRELATION")
159 self.__verifyAttribute(props, "delay", 60)
160 self.__verifyAttribute(props, "exceptionq", "TEST_EXCEPTIONQ")
161 self.__verifyAttribute(props, "expiration", 30)
162 self.assertEqual(props.attempts, 0)
163 self.__verifyAttribute(props, "priority", 1)
164 self.__verifyAttribute(props, "msgid", b'mID')
165 self.assertEqual(props.state, cx_Oracle.MSG_READY)
166 self.assertEqual(props.deliverymode, 0)
167
168 def testVisibilityModeCommit(self):
169 "test enqueue visibility option - ENQ_ON_COMMIT"
170 self.__clearBooksQueue()
171 booksType = self.connection.gettype("UDT_BOOK")
172 book = booksType.newobject()
173 book.TITLE, book.AUTHORS, book.PRICE = self.bookData[0]
174 enqOptions = self.connection.enqoptions()
175 enqOptions.visibility = cx_Oracle.ENQ_ON_COMMIT
176 props = self.connection.msgproperties()
177 self.connection.enq("BOOKS", enqOptions, props, book)
178
179 otherConnection = TestEnv.GetConnection()
180 deqOptions = otherConnection.deqoptions()
181 deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
182 deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
183 booksType = otherConnection.gettype("UDT_BOOK")
184 book = booksType.newobject()
185 props = otherConnection.msgproperties()
186 messageId = otherConnection.deq("BOOKS", deqOptions, props, book)
187 self.assertTrue(messageId is None)
188 self.connection.commit()
189 messageId = otherConnection.deq("BOOKS", deqOptions, props, book)
190 self.assertTrue(messageId is not None)
191
192 def testVisibilityModeImmediate(self):
193 "test enqueue visibility option - ENQ_IMMEDIATE"
194 self.__clearBooksQueue()
195 booksType = self.connection.gettype("UDT_BOOK")
196 book = booksType.newobject()
197 book.TITLE, book.AUTHORS, book.PRICE = self.bookData[0]
198 enqOptions = self.connection.enqoptions()
199 enqOptions.visibility = cx_Oracle.ENQ_IMMEDIATE
200 props = self.connection.msgproperties()
201 self.connection.enq("BOOKS", enqOptions, props, book)
202
203 otherConnection = TestEnv.GetConnection()
204 deqOptions = otherConnection.deqoptions()
205 deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
206 deqOptions.visibility = cx_Oracle.DEQ_ON_COMMIT
207 deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
208 booksType = otherConnection.gettype("UDT_BOOK")
209 book = booksType.newobject()
210 props = otherConnection.msgproperties()
211 otherConnection.deq("BOOKS", deqOptions, props, book)
212 results = (book.TITLE, book.AUTHORS, book.PRICE)
213 otherConnection.commit()
214 self.assertEqual(results, self.bookData[0])
215
216 def testDeliveryModeSameBuffered(self):
217 "test enqueue/dequeue delivery modes identical - buffered"
218 self.__clearBooksQueue()
219 booksType = self.connection.gettype("UDT_BOOK")
220 book = booksType.newobject()
221 book.TITLE, book.AUTHORS, book.PRICE = self.bookData[0]
222 enqOptions = self.connection.enqoptions()
223 enqOptions.deliverymode = cx_Oracle.MSG_BUFFERED
224 enqOptions.visibility = cx_Oracle.ENQ_IMMEDIATE
225 props = self.connection.msgproperties()
226 self.connection.enq("BOOKS", enqOptions, props, book)
227
228 otherConnection = TestEnv.GetConnection()
229 deqOptions = otherConnection.deqoptions()
230 deqOptions.deliverymode = cx_Oracle.MSG_BUFFERED
231 deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
232 deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
233 deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
234 booksType = otherConnection.gettype("UDT_BOOK")
235 book = booksType.newobject()
236 props = otherConnection.msgproperties()
237 otherConnection.deq("BOOKS", deqOptions, props, book)
238 results = (book.TITLE, book.AUTHORS, book.PRICE)
239 otherConnection.commit()
240 self.assertEqual(results, self.bookData[0])
241
242 def testDeliveryModeSamePersistent(self):
243 "test enqueue/dequeue delivery modes identical - persistent"
244 self.__clearBooksQueue()
245 booksType = self.connection.gettype("UDT_BOOK")
246 book = booksType.newobject()
247 book.TITLE, book.AUTHORS, book.PRICE = self.bookData[0]
248 enqOptions = self.connection.enqoptions()
249 enqOptions.deliverymode = cx_Oracle.MSG_PERSISTENT
250 enqOptions.visibility = cx_Oracle.ENQ_IMMEDIATE
251 props = self.connection.msgproperties()
252 self.connection.enq("BOOKS", enqOptions, props, book)
253
254 otherConnection = TestEnv.GetConnection()
255 deqOptions = otherConnection.deqoptions()
256 deqOptions.deliverymode = cx_Oracle.MSG_PERSISTENT
257 deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
258 deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
259 deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
260 booksType = otherConnection.gettype("UDT_BOOK")
261 book = booksType.newobject()
262 props = otherConnection.msgproperties()
263 otherConnection.deq("BOOKS", deqOptions, props, book)
264 results = (book.TITLE, book.AUTHORS, book.PRICE)
265 otherConnection.commit()
266 self.assertEqual(results, self.bookData[0])
267
268 def testDeliveryModeSamePersistentBuffered(self):
269 "test enqueue/dequeue delivery modes identical - persistent/buffered"
270 self.__clearBooksQueue()
271 booksType = self.connection.gettype("UDT_BOOK")
272 book = booksType.newobject()
273 book.TITLE, book.AUTHORS, book.PRICE = self.bookData[0]
274 enqOptions = self.connection.enqoptions()
275 enqOptions.deliverymode = cx_Oracle.MSG_PERSISTENT_OR_BUFFERED
276 enqOptions.visibility = cx_Oracle.ENQ_IMMEDIATE
277 props = self.connection.msgproperties()
278 self.connection.enq("BOOKS", enqOptions, props, book)
279
280 otherConnection = TestEnv.GetConnection()
281 deqOptions = otherConnection.deqoptions()
282 deqOptions.deliverymode = cx_Oracle.MSG_PERSISTENT_OR_BUFFERED
283 deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
284 deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
285 deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
286 booksType = otherConnection.gettype("UDT_BOOK")
287 book = booksType.newobject()
288 props = otherConnection.msgproperties()
289 otherConnection.deq("BOOKS", deqOptions, props, book)
290 results = (book.TITLE, book.AUTHORS, book.PRICE)
291 otherConnection.commit()
292 self.assertEqual(results, self.bookData[0])
293
294 def testDeliveryModeDifferent(self):
295 "test enqueue/dequeue delivery modes different"
296 self.__clearBooksQueue()
297 booksType = self.connection.gettype("UDT_BOOK")
298 book = booksType.newobject()
299 book.TITLE, book.AUTHORS, book.PRICE = self.bookData[0]
300 enqOptions = self.connection.enqoptions()
301 enqOptions.deliverymode = cx_Oracle.MSG_BUFFERED
302 enqOptions.visibility = cx_Oracle.ENQ_IMMEDIATE
303 props = self.connection.msgproperties()
304 self.connection.enq("BOOKS", enqOptions, props, book)
305
306 otherConnection = TestEnv.GetConnection()
307 deqOptions = otherConnection.deqoptions()
308 deqOptions.deliverymode = cx_Oracle.MSG_PERSISTENT
309 deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
310 deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
311 deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
312 booksType = otherConnection.gettype("UDT_BOOK")
313 book = booksType.newobject()
314 props = otherConnection.msgproperties()
315 messageId = otherConnection.deq("BOOKS", deqOptions, props, book)
316 self.assertTrue(messageId is None)
317
318 def testDequeueTransformation(self):
319 "test dequeue transformation"
320 self.__clearBooksQueue()
321 booksType = self.connection.gettype("UDT_BOOK")
322 book = booksType.newobject()
323 book.TITLE, book.AUTHORS, book.PRICE = self.bookData[0]
324 expectedPrice = book.PRICE + 10
325 enqOptions = self.connection.enqoptions()
326 props = self.connection.msgproperties()
327 self.connection.enq("BOOKS", enqOptions, props, book)
328 self.connection.commit()
329
330 otherConnection = TestEnv.GetConnection()
331 deqOptions = otherConnection.deqoptions()
332 deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
333 deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
334 deqOptions.transformation = "%s.transform2" % self.connection.username
335 deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
336 booksType = otherConnection.gettype("UDT_BOOK")
337 book = booksType.newobject()
338 props = otherConnection.msgproperties()
339 otherConnection.deq("BOOKS", deqOptions, props, book)
340 otherPrice = book.PRICE
341 self.assertEqual(otherPrice, expectedPrice)
342
343 def testEnqueueTransformation(self):
344 "test enqueue transformation"
345 self.__clearBooksQueue()
346 booksType = self.connection.gettype("UDT_BOOK")
347 book = booksType.newobject()
348 book.TITLE, book.AUTHORS, book.PRICE = self.bookData[0]
349 expectedPrice = book.PRICE + 5
350 enqOptions = self.connection.enqoptions()
351 enqOptions.transformation = "%s.transform1" % self.connection.username
352 props = self.connection.msgproperties()
353 self.connection.enq("BOOKS", enqOptions, props, book)
354 self.connection.commit()
355
356 otherConnection = TestEnv.GetConnection()
357 deqOptions = otherConnection.deqoptions()
358 deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
359 deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
360 deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
361 booksType = otherConnection.gettype("UDT_BOOK")
362 book = booksType.newobject()
363 props = otherConnection.msgproperties()
364 otherConnection.deq("BOOKS", deqOptions, props, book)
365 otherPrice = book.PRICE
366 self.assertEqual(otherPrice, expectedPrice)
367
368 if __name__ == "__main__":
369 TestEnv.RunTestCases()
370
+0
-51
test/BooleanVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing boolean variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14
15 class TestCase(TestEnv.BaseTestCase):
16
17 def testBindFalse(self):
18 "test binding in a False value"
19 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
20 (False,))
21 self.assertEqual(result, "FALSE")
22
23 def testBindNull(self):
24 "test binding in a null value"
25 self.cursor.setinputsizes(None, bool)
26 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
27 (None,))
28 self.assertEqual(result, "NULL")
29
30 def testBindOutFalse(self):
31 "test binding out a boolean value (False)"
32 result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10",
33 cx_Oracle.BOOLEAN, (15,))
34 self.assertEqual(result, False)
35
36 def testBindOutTrue(self):
37 "test binding out a boolean value (True)"
38 result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10", bool,
39 (5,))
40 self.assertEqual(result, True)
41
42 def testBindTrue(self):
43 "test binding in a True value"
44 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
45 (True,))
46 self.assertEqual(result, "TRUE")
47
48 if __name__ == "__main__":
49 TestEnv.RunTestCases()
50
+0
-350
test/Connection.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing connections."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import random
15 import string
16 import threading
17
18 class TestCase(TestEnv.BaseTestCase):
19
20 def __ConnectAndDrop(self):
21 """Connect to the database, perform a query and drop the connection."""
22 connection = TestEnv.GetConnection(threaded=True)
23 cursor = connection.cursor()
24 cursor.execute(u"select count(*) from TestNumbers")
25 count, = cursor.fetchone()
26 self.assertEqual(count, 10)
27
28 def __VerifyAttributes(self, connection, attrName, value, sql):
29 setattr(connection, attrName, value)
30 cursor = connection.cursor()
31 cursor.execute(sql)
32 result, = cursor.fetchone()
33 self.assertEqual(result, value, "%s value mismatch" % attrName)
34
35 def setUp(self):
36 pass
37
38 def tearDown(self):
39 pass
40
41 def verifyArgs(self, connection):
42 self.assertEqual(connection.username, TestEnv.GetMainUser(),
43 "user name differs")
44 self.assertEqual(connection.tnsentry, TestEnv.GetConnectString(),
45 "tnsentry differs")
46 self.assertEqual(connection.dsn, TestEnv.GetConnectString(),
47 "dsn differs")
48
49 def testAllArgs(self):
50 "connection to database with user, password, TNS separate"
51 connection = TestEnv.GetConnection()
52 self.verifyArgs(connection)
53
54 def testAppContext(self):
55 "test use of application context"
56 namespace = "CLIENTCONTEXT"
57 appContextEntries = [
58 ( namespace, "ATTR1", "VALUE1" ),
59 ( namespace, "ATTR2", "VALUE2" ),
60 ( namespace, "ATTR3", "VALUE3" )
61 ]
62 connection = TestEnv.GetConnection(appcontext=appContextEntries)
63 cursor = connection.cursor()
64 for namespace, name, value in appContextEntries:
65 cursor.execute("select sys_context(:1, :2) from dual",
66 (namespace, name))
67 actualValue, = cursor.fetchone()
68 self.assertEqual(actualValue, value)
69
70 def testAppContextNegative(self):
71 "test invalid use of application context"
72 self.assertRaises(TypeError, cx_Oracle.connect, TestEnv.GetMainUser(),
73 TestEnv.GetMainPassword(), TestEnv.GetConnectString(),
74 appcontext=[('userenv', 'action')])
75
76 def testAttributes(self):
77 "test connection end-to-end tracing attributes"
78 connection = TestEnv.GetConnection()
79 if TestEnv.GetClientVersion() >= (12, 1):
80 self.__VerifyAttributes(connection, "dbop", "cx_OracleTest_DBOP",
81 "select dbop_name from v$sql_monitor "
82 "where sid = sys_context('userenv', 'sid')"
83 "and status = 'EXECUTING'")
84 self.__VerifyAttributes(connection, "action", "cx_OracleTest_Action",
85 "select sys_context('userenv', 'action') from dual")
86 self.__VerifyAttributes(connection, "module", "cx_OracleTest_Module",
87 "select sys_context('userenv', 'module') from dual")
88 self.__VerifyAttributes(connection, "clientinfo",
89 "cx_OracleTest_CInfo",
90 "select sys_context('userenv', 'client_info') from dual")
91 self.__VerifyAttributes(connection, "client_identifier",
92 "cx_OracleTest_CID",
93 "select sys_context('userenv', 'client_identifier') from dual")
94
95 def testAutoCommit(self):
96 "test use of autocommit"
97 connection = TestEnv.GetConnection()
98 cursor = connection.cursor()
99 otherConnection = TestEnv.GetConnection()
100 otherCursor = otherConnection.cursor()
101 cursor.execute("truncate table TestTempTable")
102 cursor.execute("insert into TestTempTable (IntCol) values (1)")
103 otherCursor.execute("select IntCol from TestTempTable")
104 rows = otherCursor.fetchall()
105 self.assertEqual(rows, [])
106 connection.autocommit = True
107 cursor.execute("insert into TestTempTable (IntCol) values (2)")
108 otherCursor.execute("select IntCol from TestTempTable order by IntCol")
109 rows = otherCursor.fetchall()
110 self.assertEqual(rows, [(1,), (2,)])
111
112 def testBadConnectString(self):
113 "connection to database with bad connect string"
114 self.assertRaises(cx_Oracle.DatabaseError, cx_Oracle.connect,
115 TestEnv.GetMainUser())
116 self.assertRaises(cx_Oracle.DatabaseError, cx_Oracle.connect,
117 TestEnv.GetMainUser() + u"@" + TestEnv.GetConnectString())
118 self.assertRaises(cx_Oracle.DatabaseError, cx_Oracle.connect,
119 TestEnv.GetMainUser() + "@" + \
120 TestEnv.GetConnectString() + "/" + TestEnv.GetMainPassword())
121
122 def testBadPassword(self):
123 "connection to database with bad password"
124 self.assertRaises(cx_Oracle.DatabaseError, cx_Oracle.connect,
125 TestEnv.GetMainUser(), TestEnv.GetMainPassword() + "X",
126 TestEnv.GetConnectString())
127
128 def testChangePassword(self):
129 "test changing password"
130 sysRandom = random.SystemRandom()
131 newPassword = "".join(sysRandom.choice(string.ascii_letters) \
132 for i in range(20))
133 connection = TestEnv.GetConnection()
134 connection.changepassword(TestEnv.GetMainPassword(), newPassword)
135 cconnection = cx_Oracle.connect(TestEnv.GetMainUser(), newPassword,
136 TestEnv.GetConnectString())
137 connection.changepassword(newPassword, TestEnv.GetMainPassword())
138
139 def testChangePasswordNegative(self):
140 "test changing password to an invalid value"
141 newPassword = "1" * 150
142 connection = TestEnv.GetConnection()
143 self.assertRaises(cx_Oracle.DatabaseError, connection.changepassword,
144 TestEnv.GetMainPassword(), newPassword)
145
146 def testEncodings(self):
147 "connection with only encoding or nencoding specified should work"
148 connection = cx_Oracle.connect(TestEnv.GetMainUser(),
149 TestEnv.GetMainPassword(), TestEnv.GetConnectString())
150 encoding = connection.encoding
151 nencoding = connection.nencoding
152 altEncoding = "ISO-8859-1"
153 connection = cx_Oracle.connect(TestEnv.GetMainUser(),
154 TestEnv.GetMainPassword(), TestEnv.GetConnectString(),
155 encoding=altEncoding)
156 self.assertEqual(connection.encoding, altEncoding)
157 self.assertEqual(connection.nencoding, nencoding)
158 connection = cx_Oracle.connect(TestEnv.GetMainUser(),
159 TestEnv.GetMainPassword(), TestEnv.GetConnectString(),
160 nencoding=altEncoding)
161 self.assertEqual(connection.encoding, encoding)
162 self.assertEqual(connection.nencoding, altEncoding)
163
164 def testDifferentEncodings(self):
165 connection = cx_Oracle.connect(TestEnv.GetMainUser(),
166 TestEnv.GetMainPassword(), TestEnv.GetConnectString(),
167 encoding="UTF-8", nencoding="UTF-16")
168 value = u"\u03b4\u4e2a"
169 cursor = connection.cursor()
170 ncharVar = cursor.var(cx_Oracle.NCHAR, 100)
171 ncharVar.setvalue(0, value)
172 cursor.execute("select :value from dual", value = ncharVar)
173 result, = cursor.fetchone()
174 self.assertEqual(result, value)
175
176 def testExceptionOnClose(self):
177 "confirm an exception is raised after closing a connection"
178 connection = TestEnv.GetConnection()
179 connection.close()
180 self.assertRaises(cx_Oracle.DatabaseError, connection.rollback)
181
182 def testConnectWithHandle(self):
183 "test creating a connection using a handle"
184 connection = TestEnv.GetConnection()
185 cursor = connection.cursor()
186 cursor.execute("truncate table TestTempTable")
187 intValue = random.randint(1, 32768)
188 cursor.execute("""
189 insert into TestTempTable (IntCol, StringCol)
190 values (:val, null)""", val = intValue)
191 connection2 = cx_Oracle.connect(handle = connection.handle)
192 cursor = connection2.cursor()
193 cursor.execute("select IntCol from TestTempTable")
194 fetchedIntValue, = cursor.fetchone()
195 self.assertEqual(fetchedIntValue, intValue)
196 cursor.close()
197 self.assertRaises(cx_Oracle.DatabaseError, connection2.close)
198 connection.close()
199 cursor = connection2.cursor()
200 self.assertRaises(cx_Oracle.DatabaseError, cursor.execute,
201 "select count(*) from TestTempTable")
202
203 def testMakeDSN(self):
204 "test making a data source name from host, port and sid"
205 formatString = u"(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" + \
206 "(HOST=%s)(PORT=%d))(CONNECT_DATA=(SID=%s)))"
207 args = ("hostname", 1521, "TEST")
208 result = cx_Oracle.makedsn(*args)
209 self.assertEqual(result, formatString % args)
210 args = (u"hostname", 1521, u"TEST")
211 result = cx_Oracle.makedsn(*args)
212 self.assertEqual(result, formatString % args)
213
214 def testSingleArg(self):
215 "connection to database with user, password, DSN together"
216 connection = cx_Oracle.connect("%s/%s@%s" % \
217 (TestEnv.GetMainUser(), TestEnv.GetMainPassword(),
218 TestEnv.GetConnectString()))
219 self.verifyArgs(connection)
220
221 def testVersion(self):
222 "connection version is a string"
223 connection = TestEnv.GetConnection()
224 self.assertTrue(isinstance(connection.version, str))
225
226 def testRollbackOnClose(self):
227 "connection rolls back before close"
228 connection = TestEnv.GetConnection()
229 cursor = connection.cursor()
230 cursor.execute("truncate table TestTempTable")
231 otherConnection = TestEnv.GetConnection()
232 otherCursor = otherConnection.cursor()
233 otherCursor.execute("insert into TestTempTable (IntCol) values (1)")
234 otherCursor.close()
235 otherConnection.close()
236 cursor.execute("select count(*) from TestTempTable")
237 count, = cursor.fetchone()
238 self.assertEqual(count, 0)
239
240 def testRollbackOnDel(self):
241 "connection rolls back before destruction"
242 connection = TestEnv.GetConnection()
243 cursor = connection.cursor()
244 cursor.execute("truncate table TestTempTable")
245 otherConnection = TestEnv.GetConnection()
246 otherCursor = otherConnection.cursor()
247 otherCursor.execute("insert into TestTempTable (IntCol) values (1)")
248 del otherCursor
249 del otherConnection
250 cursor.execute("select count(*) from TestTempTable")
251 count, = cursor.fetchone()
252 self.assertEqual(count, 0)
253
254 def testThreading(self):
255 "connection to database with multiple threads"
256 threads = []
257 for i in range(20):
258 thread = threading.Thread(None, self.__ConnectAndDrop)
259 threads.append(thread)
260 thread.start()
261 for thread in threads:
262 thread.join()
263
264 def testStringFormat(self):
265 "test string format of connection"
266 connection = TestEnv.GetConnection()
267 expectedValue = "<cx_Oracle.Connection to %s@%s>" % \
268 (TestEnv.GetMainUser(), TestEnv.GetConnectString())
269 self.assertEqual(str(connection), expectedValue)
270
271 def testCtxMgrClose(self):
272 "test context manager - close"
273 connection = TestEnv.GetConnection()
274 with connection:
275 cursor = connection.cursor()
276 cursor.execute("truncate table TestTempTable")
277 cursor.execute("insert into TestTempTable (IntCol) values (1)")
278 connection.commit()
279 cursor.execute("insert into TestTempTable (IntCol) values (2)")
280 self.assertRaises(cx_Oracle.DatabaseError, connection.ping)
281 connection = TestEnv.GetConnection()
282 cursor = connection.cursor()
283 cursor.execute("select count(*) from TestTempTable")
284 count, = cursor.fetchone()
285 self.assertEqual(count, 1)
286
287 def testConnectionAttributes(self):
288 "test connection attribute values"
289 connection = cx_Oracle.connect(TestEnv.GetMainUser(),
290 TestEnv.GetMainPassword(), TestEnv.GetConnectString(),
291 encoding="ASCII")
292 self.assertEqual(connection.maxBytesPerCharacter, 1)
293 connection = cx_Oracle.connect(TestEnv.GetMainUser(),
294 TestEnv.GetMainPassword(), TestEnv.GetConnectString(),
295 encoding="UTF-8")
296 self.assertEqual(connection.maxBytesPerCharacter, 4)
297 if TestEnv.GetClientVersion() >= (12, 1):
298 self.assertEqual(connection.ltxid, b'')
299 self.assertEqual(connection.current_schema, None)
300 connection.current_schema = "test_schema"
301 self.assertEqual(connection.current_schema, "test_schema")
302 self.assertEqual(connection.edition, None)
303 connection.external_name = "test_external"
304 self.assertEqual(connection.external_name, "test_external")
305 connection.internal_name = "test_internal"
306 self.assertEqual(connection.internal_name, "test_internal")
307 connection.stmtcachesize = 30
308 self.assertEqual(connection.stmtcachesize, 30)
309 self.assertRaises(TypeError, connection.stmtcachesize, 20.5)
310 self.assertRaises(TypeError, connection.stmtcachesize, "value")
311
312 def testClosedConnectionAttributes(self):
313 "test closed connection attribute values"
314 connection = TestEnv.GetConnection()
315 connection.close()
316 attrNames = ["current_schema", "edition", "external_name",
317 "internal_name", "stmtcachesize"]
318 if TestEnv.GetClientVersion() >= (12, 1):
319 attrNames.append("ltxid")
320 for name in attrNames:
321 self.assertRaises(cx_Oracle.DatabaseError, getattr, connection,
322 name)
323
324 def testPing(self):
325 "test connection ping"
326 connection = TestEnv.GetConnection()
327 connection.ping()
328
329 def testTransactionBegin(self):
330 "test begin, prepare, cancel transaction"
331 connection = TestEnv.GetConnection()
332 cursor = connection.cursor()
333 cursor.execute("truncate table TestTempTable")
334 connection.begin(10, 'trxnId', 'branchId')
335 self.assertEqual(connection.prepare(), False)
336 connection.begin(10, 'trxnId', 'branchId')
337 cursor.execute("""
338 insert into TestTempTable (IntCol, StringCol)
339 values (1, 'tesName')""")
340 self.assertEqual(connection.prepare(), True)
341 connection.cancel()
342 connection.rollback()
343 cursor.execute("select count(*) from TestTempTable")
344 count, = cursor.fetchone()
345 self.assertEqual(count, 0)
346
347 if __name__ == "__main__":
348 TestEnv.RunTestCases()
349
+0
-676
test/Cursor.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing cursor objects."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import decimal
15 import sys
16
17 class TestCase(TestEnv.BaseTestCase):
18
19 def testCreateScrollableCursor(self):
20 """test creating a scrollable cursor"""
21 cursor = self.connection.cursor()
22 self.assertEqual(cursor.scrollable, False)
23 cursor = self.connection.cursor(True)
24 self.assertEqual(cursor.scrollable, True)
25 cursor = self.connection.cursor(scrollable = True)
26 self.assertEqual(cursor.scrollable, True)
27 cursor.scrollable = False
28 self.assertEqual(cursor.scrollable, False)
29
30 def testExecuteNoArgs(self):
31 """test executing a statement without any arguments"""
32 result = self.cursor.execute("begin null; end;")
33 self.assertEqual(result, None)
34
35 def testExecuteNoStatementWithArgs(self):
36 """test executing a None statement with bind variables"""
37 self.assertRaises(cx_Oracle.ProgrammingError, self.cursor.execute,
38 None, x = 5)
39
40 def testExecuteEmptyKeywordArgs(self):
41 """test executing a statement with args and empty keyword args"""
42 simpleVar = self.cursor.var(cx_Oracle.NUMBER)
43 args = [simpleVar]
44 kwArgs = {}
45 result = self.cursor.execute("begin :1 := 25; end;", args, **kwArgs)
46 self.assertEqual(result, None)
47 self.assertEqual(simpleVar.getvalue(), 25)
48
49 def testExecuteKeywordArgs(self):
50 """test executing a statement with keyword arguments"""
51 simpleVar = self.cursor.var(cx_Oracle.NUMBER)
52 result = self.cursor.execute("begin :value := 5; end;",
53 value = simpleVar)
54 self.assertEqual(result, None)
55 self.assertEqual(simpleVar.getvalue(), 5)
56
57 def testExecuteDictionaryArg(self):
58 """test executing a statement with a dictionary argument"""
59 simpleVar = self.cursor.var(cx_Oracle.NUMBER)
60 dictArg = { "value" : simpleVar }
61 result = self.cursor.execute("begin :value := 10; end;", dictArg)
62 self.assertEqual(result, None)
63 self.assertEqual(simpleVar.getvalue(), 10)
64 dictArg = { u"value" : simpleVar }
65 result = self.cursor.execute("begin :value := 25; end;", dictArg)
66 self.assertEqual(result, None)
67 self.assertEqual(simpleVar.getvalue(), 25)
68
69 def testExecuteMultipleMethod(self):
70 """test executing a statement with both a dict arg and keyword args"""
71 simpleVar = self.cursor.var(cx_Oracle.NUMBER)
72 dictArg = { "value" : simpleVar }
73 self.assertRaises(cx_Oracle.InterfaceError, self.cursor.execute,
74 "begin :value := 15; end;", dictArg, value = simpleVar)
75
76 def testExecuteAndModifyArraySize(self):
77 """test executing a statement and then changing the array size"""
78 self.cursor.execute("select IntCol from TestNumbers")
79 self.cursor.arraysize = 20
80 self.assertEqual(len(self.cursor.fetchall()), 10)
81
82 def testCallProc(self):
83 """test executing a stored procedure"""
84 var = self.cursor.var(cx_Oracle.NUMBER)
85 results = self.cursor.callproc("proc_Test", ("hi", 5, var))
86 self.assertEqual(results, ["hi", 10, 2.0])
87
88 def testCallProcNoArgs(self):
89 """test executing a stored procedure without any arguments"""
90 results = self.cursor.callproc(u"proc_TestNoArgs")
91 self.assertEqual(results, [])
92
93 def testCallFunc(self):
94 """test executing a stored function"""
95 results = self.cursor.callfunc(u"func_Test", cx_Oracle.NUMBER,
96 (u"hi", 5))
97 self.assertEqual(results, 7)
98
99 def testCallFuncNoArgs(self):
100 """test executing a stored function without any arguments"""
101 results = self.cursor.callfunc("func_TestNoArgs", cx_Oracle.NUMBER)
102 self.assertEqual(results, 712)
103
104 def testCallFuncNegative(self):
105 """test executing a stored function with wrong parameters"""
106 funcName = "func_Test"
107 self.assertRaises(TypeError, self.cursor.callfunc, cx_Oracle.NUMBER,
108 funcName, ("hi", 5))
109 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.callfunc,
110 funcName, cx_Oracle.NUMBER, ("hi", 5, 7))
111 self.assertRaises(TypeError, self.cursor.callfunc, funcName,
112 cx_Oracle.NUMBER, "hi", 7)
113 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.callfunc,
114 funcName, cx_Oracle.NUMBER, [5, "hi"])
115 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.callfunc,
116 funcName, cx_Oracle.NUMBER)
117 self.assertRaises(TypeError, self.cursor.callfunc, funcName,
118 cx_Oracle.NUMBER, 5)
119
120 def testExecuteManyByName(self):
121 """test executing a statement multiple times (named args)"""
122 self.cursor.execute("truncate table TestTempTable")
123 rows = [ { u"value" : n } for n in range(250) ]
124 self.cursor.arraysize = 100
125 statement = "insert into TestTempTable (IntCol) values (:value)"
126 self.cursor.executemany(statement, rows)
127 self.connection.commit()
128 self.cursor.execute("select count(*) from TestTempTable")
129 count, = self.cursor.fetchone()
130 self.assertEqual(count, len(rows))
131
132 def testExecuteManyByPosition(self):
133 """test executing a statement multiple times (positional args)"""
134 self.cursor.execute("truncate table TestTempTable")
135 rows = [ [n] for n in range(230) ]
136 self.cursor.arraysize = 100
137 statement = "insert into TestTempTable (IntCol) values (:1)"
138 self.cursor.executemany(statement, rows)
139 self.connection.commit()
140 self.cursor.execute("select count(*) from TestTempTable")
141 count, = self.cursor.fetchone()
142 self.assertEqual(count, len(rows))
143
144 def testExecuteManyWithPrepare(self):
145 """test executing a statement multiple times (with prepare)"""
146 self.cursor.execute("truncate table TestTempTable")
147 rows = [ [n] for n in range(225) ]
148 self.cursor.arraysize = 100
149 statement = "insert into TestTempTable (IntCol) values (:1)"
150 self.cursor.prepare(statement)
151 self.cursor.executemany(None, rows)
152 self.connection.commit()
153 self.cursor.execute("select count(*) from TestTempTable")
154 count, = self.cursor.fetchone()
155 self.assertEqual(count, len(rows))
156
157 def testExecuteManyWithRebind(self):
158 """test executing a statement multiple times (with rebind)"""
159 self.cursor.execute("truncate table TestTempTable")
160 rows = [ [n] for n in range(235) ]
161 self.cursor.arraysize = 100
162 statement = "insert into TestTempTable (IntCol) values (:1)"
163 self.cursor.executemany(statement, rows[:50])
164 self.cursor.executemany(statement, rows[50:])
165 self.connection.commit()
166 self.cursor.execute("select count(*) from TestTempTable")
167 count, = self.cursor.fetchone()
168 self.assertEqual(count, len(rows))
169
170 def testExecuteManyWithInputSizesWrong(self):
171 "test executing a statement multiple times (with input sizes wrong)"
172 cursor = self.connection.cursor()
173 cursor.setinputsizes(cx_Oracle.NUMBER)
174 data = [[decimal.Decimal("25.8")], [decimal.Decimal("30.0")]]
175 cursor.executemany("declare t number; begin t := :1; end;", data)
176
177 def testExecuteManyMultipleBatches(self):
178 "test executing a statement multiple times (with multiple batches)"
179 self.cursor.execute("truncate table TestTempTable")
180 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
181 self.cursor.executemany(sql, [(1, None), (2, None)])
182 self.cursor.executemany(sql, [(3, None), (4, "Testing")])
183
184 def testExecuteManyNumeric(self):
185 "test executemany() with various numeric types"
186 self.cursor.execute("truncate table TestTempTable")
187 data = [(1, 5), (2, 7.0), (3, 6.5), (4, 2 ** 65),
188 (5, decimal.Decimal("24.5"))]
189 sql = "insert into TestTempTable (IntCol, NumberCol) values (:1, :2)"
190 self.cursor.executemany(sql, data)
191 self.cursor.execute("""
192 select IntCol, NumberCol
193 from TestTempTable
194 order by IntCol""")
195 self.assertEqual(self.cursor.fetchall(), data)
196
197 def testExecuteManyWithResize(self):
198 """test executing a statement multiple times (with resize)"""
199 self.cursor.execute("truncate table TestTempTable")
200 rows = [ ( 1, "First" ),
201 ( 2, "Second" ),
202 ( 3, "Third" ),
203 ( 4, "Fourth" ),
204 ( 5, "Fifth" ),
205 ( 6, "Sixth" ),
206 ( 7, "Seventh and the longest one" ) ]
207 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
208 self.cursor.executemany(sql, rows)
209 self.cursor.execute("""
210 select IntCol, StringCol
211 from TestTempTable
212 order by IntCol""")
213 fetchedRows = self.cursor.fetchall()
214 self.assertEqual(fetchedRows, rows)
215
216 def testExecuteManyWithExecption(self):
217 """test executing a statement multiple times (with exception)"""
218 self.cursor.execute("truncate table TestTempTable")
219 rows = [ { "value" : n } for n in (1, 2, 3, 2, 5) ]
220 statement = "insert into TestTempTable (IntCol) values (:value)"
221 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.executemany,
222 statement, rows)
223 self.assertEqual(self.cursor.rowcount, 3)
224
225 def testExecuteManyWithInvalidParameters(self):
226 "test calling executemany() with invalid parameters"
227 self.assertRaises(TypeError, self.cursor.executemany,
228 "insert into TestTempTable (IntCol, StringCol) values (:1, :2)",
229 "These are not valid parameters")
230
231 def testExecuteManyNoParameters(self):
232 "test calling executemany() without any bind parameters"
233 numRows = 5
234 self.cursor.execute("truncate table TestTempTable")
235 self.cursor.executemany("""
236 declare
237 t_Id number;
238 begin
239 select nvl(count(*), 0) + 1 into t_Id
240 from TestTempTable;
241
242 insert into TestTempTable (IntCol, StringCol)
243 values (t_Id, 'Test String ' || t_Id);
244 end;""", numRows)
245 self.cursor.execute("select count(*) from TestTempTable")
246 count, = self.cursor.fetchone()
247 self.assertEqual(count, numRows)
248
249 def testExecuteManyBoundEarlier(self):
250 "test calling executemany() with binds performed earlier"
251 numRows = 9
252 self.cursor.execute("truncate table TestTempTable")
253 var = self.cursor.var(int, arraysize = numRows)
254 self.cursor.setinputsizes(var)
255 self.cursor.executemany("""
256 declare
257 t_Id number;
258 begin
259 select nvl(count(*), 0) + 1 into t_Id
260 from TestTempTable;
261
262 insert into TestTempTable (IntCol, StringCol)
263 values (t_Id, 'Test String ' || t_Id);
264
265 select sum(IntCol) into :1
266 from TestTempTable;
267 end;""", numRows)
268 expectedData = [1, 3, 6, 10, 15, 21, 28, 36, 45]
269 self.assertEqual(var.values, expectedData)
270
271 def testPrepare(self):
272 """test preparing a statement and executing it multiple times"""
273 self.assertEqual(self.cursor.statement, None)
274 statement = "begin :value := :value + 5; end;"
275 self.cursor.prepare(statement)
276 var = self.cursor.var(cx_Oracle.NUMBER)
277 self.assertEqual(self.cursor.statement, statement)
278 var.setvalue(0, 2)
279 self.cursor.execute(None, value = var)
280 self.assertEqual(var.getvalue(), 7)
281 self.cursor.execute(None, value = var)
282 self.assertEqual(var.getvalue(), 12)
283 self.cursor.execute("begin :value2 := 3; end;", value2 = var)
284 self.assertEqual(var.getvalue(), 3)
285
286 def testExceptionOnClose(self):
287 "confirm an exception is raised after closing a cursor"
288 self.cursor.close()
289 self.assertRaises(cx_Oracle.InterfaceError, self.cursor.execute,
290 "select 1 from dual")
291
292 def testIterators(self):
293 """test iterators"""
294 self.cursor.execute("""
295 select IntCol
296 from TestNumbers
297 where IntCol between 1 and 3
298 order by IntCol""")
299 rows = []
300 for row in self.cursor:
301 rows.append(row[0])
302 self.assertEqual(rows, [1, 2, 3])
303
304 def testIteratorsInterrupted(self):
305 """test iterators (with intermediate execute)"""
306 self.cursor.execute("truncate table TestTempTable")
307 self.cursor.execute("""
308 select IntCol
309 from TestNumbers
310 where IntCol between 1 and 3
311 order by IntCol""")
312 testIter = iter(self.cursor)
313 if sys.version_info[0] >= 3:
314 value, = next(testIter)
315 else:
316 value, = testIter.next()
317 self.cursor.execute("insert into TestTempTable (IntCol) values (1)")
318 if sys.version_info[0] >= 3:
319 self.assertRaises(cx_Oracle.InterfaceError, next, testIter)
320 else:
321 self.assertRaises(cx_Oracle.InterfaceError, testIter.next)
322
323 def testBindNames(self):
324 """test that bindnames() works correctly."""
325 self.assertRaises(cx_Oracle.ProgrammingError, self.cursor.bindnames)
326 self.cursor.prepare(u"begin null; end;")
327 self.assertEqual(self.cursor.bindnames(), [])
328 self.cursor.prepare("begin :retval := :inval + 5; end;")
329 self.assertEqual(self.cursor.bindnames(), ["RETVAL", "INVAL"])
330 self.cursor.prepare("begin :retval := :a * :a + :b * :b; end;")
331 self.assertEqual(self.cursor.bindnames(), ["RETVAL", "A", "B"])
332 self.cursor.prepare("begin :a := :b + :c + :d + :e + :f + :g + " + \
333 ":h + :i + :j + :k + :l; end;")
334 self.assertEqual(self.cursor.bindnames(),
335 ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"])
336 self.cursor.prepare("select :a * :a + :b * :b from dual")
337 self.assertEqual(self.cursor.bindnames(), ["A", "B"])
338
339 def testBadPrepare(self):
340 """test that subsequent executes succeed after bad prepare"""
341 self.assertRaises(cx_Oracle.DatabaseError,
342 self.cursor.execute,
343 "begin raise_application_error(-20000, 'this); end;")
344 self.cursor.execute("begin null; end;")
345
346 def testBadExecute(self):
347 """test that subsequent fetches fail after bad execute"""
348 self.assertRaises(cx_Oracle.DatabaseError,
349 self.cursor.execute, "select y from dual")
350 self.assertRaises(cx_Oracle.InterfaceError,
351 self.cursor.fetchall)
352
353 def testScrollAbsoluteExceptionAfter(self):
354 """test scrolling absolute yields an exception (after result set)"""
355 cursor = self.connection.cursor(scrollable = True)
356 cursor.arraysize = self.cursor.arraysize
357 cursor.execute("""
358 select NumberCol
359 from TestNumbers
360 order by IntCol""")
361 self.assertRaises(cx_Oracle.DatabaseError, cursor.scroll, 12,
362 "absolute")
363
364 def testScrollAbsoluteInBuffer(self):
365 """test scrolling absolute (when in buffers)"""
366 cursor = self.connection.cursor(scrollable = True)
367 cursor.arraysize = self.cursor.arraysize
368 cursor.execute("""
369 select NumberCol
370 from TestNumbers
371 order by IntCol""")
372 cursor.fetchmany()
373 self.assertTrue(cursor.arraysize > 1,
374 "array size must exceed 1 for this test to work correctly")
375 cursor.scroll(1, mode = "absolute")
376 row = cursor.fetchone()
377 self.assertEqual(row[0], 1.25)
378 self.assertEqual(cursor.rowcount, 1)
379
380 def testScrollAbsoluteNotInBuffer(self):
381 """test scrolling absolute (when not in buffers)"""
382 cursor = self.connection.cursor(scrollable = True)
383 cursor.arraysize = self.cursor.arraysize
384 cursor.execute("""
385 select NumberCol
386 from TestNumbers
387 order by IntCol""")
388 cursor.scroll(6, mode = "absolute")
389 row = cursor.fetchone()
390 self.assertEqual(row[0], 7.5)
391 self.assertEqual(cursor.rowcount, 6)
392
393 def testScrollFirstInBuffer(self):
394 """test scrolling to first row in result set (when in buffers)"""
395 cursor = self.connection.cursor(scrollable = True)
396 cursor.arraysize = self.cursor.arraysize
397 cursor.execute("""
398 select NumberCol
399 from TestNumbers
400 order by IntCol""")
401 cursor.fetchmany()
402 cursor.scroll(mode = "first")
403 row = cursor.fetchone()
404 self.assertEqual(row[0], 1.25)
405 self.assertEqual(cursor.rowcount, 1)
406
407 def testScrollFirstNotInBuffer(self):
408 """test scrolling to first row in result set (when not in buffers)"""
409 cursor = self.connection.cursor(scrollable = True)
410 cursor.arraysize = self.cursor.arraysize
411 cursor.execute("""
412 select NumberCol
413 from TestNumbers
414 order by IntCol""")
415 cursor.fetchmany()
416 cursor.fetchmany()
417 cursor.scroll(mode = "first")
418 row = cursor.fetchone()
419 self.assertEqual(row[0], 1.25)
420 self.assertEqual(cursor.rowcount, 1)
421
422 def testScrollLast(self):
423 """test scrolling to last row in result set"""
424 cursor = self.connection.cursor(scrollable = True)
425 cursor.arraysize = self.cursor.arraysize
426 cursor.execute("""
427 select NumberCol
428 from TestNumbers
429 order by IntCol""")
430 cursor.scroll(mode = "last")
431 row = cursor.fetchone()
432 self.assertEqual(row[0], 12.5)
433 self.assertEqual(cursor.rowcount, 10)
434
435 def testScrollRelativeExceptionAfter(self):
436 """test scrolling relative yields an exception (after result set)"""
437 cursor = self.connection.cursor(scrollable = True)
438 cursor.arraysize = self.cursor.arraysize
439 cursor.execute("""
440 select NumberCol
441 from TestNumbers
442 order by IntCol""")
443 self.assertRaises(cx_Oracle.DatabaseError, cursor.scroll, 15)
444
445 def testScrollRelativeExceptionBefore(self):
446 """test scrolling relative yields an exception (before result set)"""
447 cursor = self.connection.cursor(scrollable = True)
448 cursor.arraysize = self.cursor.arraysize
449 cursor.execute("""
450 select NumberCol
451 from TestNumbers
452 order by IntCol""")
453 self.assertRaises(cx_Oracle.DatabaseError, cursor.scroll, -5)
454
455 def testScrollRelativeInBuffer(self):
456 """test scrolling relative (when in buffers)"""
457 cursor = self.connection.cursor(scrollable = True)
458 cursor.arraysize = self.cursor.arraysize
459 cursor.execute("""
460 select NumberCol
461 from TestNumbers
462 order by IntCol""")
463 cursor.fetchmany()
464 self.assertTrue(cursor.arraysize > 1,
465 "array size must exceed 1 for this test to work correctly")
466 cursor.scroll(2 - cursor.rowcount)
467 row = cursor.fetchone()
468 self.assertEqual(row[0], 2.5)
469 self.assertEqual(cursor.rowcount, 2)
470
471 def testScrollRelativeNotInBuffer(self):
472 """test scrolling relative (when not in buffers)"""
473 cursor = self.connection.cursor(scrollable = True)
474 cursor.arraysize = self.cursor.arraysize
475 cursor.execute("""
476 select NumberCol
477 from TestNumbers
478 order by IntCol""")
479 cursor.fetchmany()
480 cursor.fetchmany()
481 self.assertTrue(cursor.arraysize > 1,
482 "array size must exceed 2 for this test to work correctly")
483 cursor.scroll(3 - cursor.rowcount)
484 row = cursor.fetchone()
485 self.assertEqual(row[0], 3.75)
486 self.assertEqual(cursor.rowcount, 3)
487
488 def testScrollNoRows(self):
489 """test scrolling when there are no rows"""
490 self.cursor.execute("truncate table TestTempTable")
491 cursor = self.connection.cursor(scrollable = True)
492 cursor.execute("select * from TestTempTable")
493 cursor.scroll(mode = "last")
494 self.assertEqual(cursor.fetchall(), [])
495 cursor.scroll(mode = "first")
496 self.assertEqual(cursor.fetchall(), [])
497 self.assertRaises(cx_Oracle.DatabaseError, cursor.scroll, 1,
498 mode = "absolute")
499
500 def testScrollDifferingArrayAndFetchSizes(self):
501 """test scrolling with differing array sizes and fetch array sizes"""
502 self.cursor.execute("truncate table TestTempTable")
503 for i in range(30):
504 self.cursor.execute("""
505 insert into TestTempTable (IntCol, StringCol)
506 values (:1, null)""",
507 (i + 1,))
508 for arraySize in range(1, 6):
509 cursor = self.connection.cursor(scrollable = True)
510 cursor.arraysize = arraySize
511 cursor.execute("select IntCol from TestTempTable order by IntCol")
512 for numRows in range(1, arraySize + 1):
513 cursor.scroll(15, "absolute")
514 rows = cursor.fetchmany(numRows)
515 self.assertEqual(rows[0][0], 15)
516 self.assertEqual(cursor.rowcount, 15 + numRows - 1)
517 cursor.scroll(9)
518 rows = cursor.fetchmany(numRows)
519 numRowsFetched = len(rows)
520 self.assertEqual(rows[0][0], 15 + numRows + 8)
521 self.assertEqual(cursor.rowcount,
522 15 + numRows + numRowsFetched + 7)
523 cursor.scroll(-12)
524 rows = cursor.fetchmany(numRows)
525 self.assertEqual(rows[0][0], 15 + numRows + numRowsFetched - 5)
526 self.assertEqual(cursor.rowcount,
527 15 + numRows + numRowsFetched + numRows - 6)
528
529 def testSetInputSizesNegative(self):
530 "test cursor.setinputsizes() with invalid parameters"
531 val = decimal.Decimal(5)
532 self.assertRaises(cx_Oracle.InterfaceError,
533 self.cursor.setinputsizes, val, x = val)
534 self.assertRaises(TypeError, self.cursor.setinputsizes, val)
535
536 def testSetInputSizesNoParameters(self):
537 "test setting input sizes without any parameters"
538 self.cursor.setinputsizes()
539 self.cursor.execute("select :val from dual", val = "Test Value")
540 self.assertEqual(self.cursor.fetchall(), [("Test Value",)])
541
542 def testSetInputSizesEmptyDict(self):
543 "test setting input sizes with an empty dictionary"
544 emptyDict = {}
545 self.cursor.prepare("select 236 from dual")
546 self.cursor.setinputsizes(**emptyDict)
547 self.cursor.execute(None, emptyDict)
548 self.assertEqual(self.cursor.fetchall(), [(236,)])
549
550 def testSetInputSizesEmptyList(self):
551 "test setting input sizes with an empty list"
552 emptyList = {}
553 self.cursor.prepare("select 239 from dual")
554 self.cursor.setinputsizes(*emptyList)
555 self.cursor.execute(None, emptyList)
556 self.assertEqual(self.cursor.fetchall(), [(239,)])
557
558 def testSetInputSizesByPosition(self):
559 """test setting input sizes with positional args"""
560 var = self.cursor.var(cx_Oracle.STRING, 100)
561 self.cursor.setinputsizes(None, 5, None, 10, None, cx_Oracle.NUMBER)
562 self.cursor.execute("""
563 begin
564 :1 := :2 || to_char(:3) || :4 || to_char(:5) || to_char(:6);
565 end;""", [var, 'test_', 5, '_second_', 3, 7])
566 self.assertEqual(var.getvalue(), u"test_5_second_37")
567
568 def testStringFormat(self):
569 """test string format of cursor"""
570 formatString = "<cx_Oracle.Cursor on <cx_Oracle.Connection to %s@%s>>"
571 expectedValue = formatString % \
572 (TestEnv.GetMainUser(), TestEnv.GetConnectString())
573 self.assertEqual(str(self.cursor), expectedValue)
574
575 def testCursorFetchRaw(self):
576 """test cursor.fetchraw()"""
577 cursor = self.connection.cursor()
578 cursor.arraysize = 25
579 cursor.execute("select LongIntCol from TestNumbers order by IntCol")
580 self.assertEqual(cursor.fetchraw(), 10)
581 self.assertEqual(cursor.fetchvars[0].getvalue(), 38)
582
583 def testParse(self):
584 """test parsing statements"""
585 sql = "select LongIntCol from TestNumbers where IntCol = :val"
586 self.cursor.parse(sql)
587 self.assertEqual(self.cursor.statement, sql)
588 self.assertEqual(self.cursor.description,
589 [ ('LONGINTCOL', cx_Oracle.NUMBER, 17, None, 16, 0, 0) ])
590
591 def testSetOutputSize(self):
592 "test cursor.setoutputsize() does not fail (but does nothing)"
593 self.cursor.setoutputsize(100, 2)
594
595 def testVarNegative(self):
596 "test cursor.var() with invalid parameters"
597 self.assertRaises(TypeError, self.cursor.var, 5)
598
599 def testArrayVarNegative(self):
600 "test cursor.arrayvar() with invalid parameters"
601 self.assertRaises(TypeError, self.cursor.arrayvar, 5, 1)
602
603 def testBooleanWithoutPlsql(self):
604 "test binding boolean data without the use of PL/SQL"
605 self.cursor.execute("truncate table TestTempTable")
606 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
607 self.cursor.execute(sql, (False, "Value should be 0"))
608 self.cursor.execute(sql, (True, "Value should be 1"))
609 self.cursor.execute("""
610 select IntCol, StringCol
611 from TestTempTable
612 order by IntCol""")
613 self.assertEqual(self.cursor.fetchall(),
614 [ (0, "Value should be 0"), (1, "Value should be 1") ])
615
616 def testAsContextManager(self):
617 "test using a cursor as a context manager"
618 with self.cursor as cursor:
619 cursor.execute("truncate table TestTempTable")
620 cursor.execute("select count(*) from TestTempTable")
621 count, = cursor.fetchone()
622 self.assertEqual(count, 0)
623 self.assertRaises(cx_Oracle.InterfaceError, self.cursor.close)
624
625 def testQueryRowCount(self):
626 "test that the rowcount attribute is reset to zero on query execute"
627 sql = "select * from dual where 1 = :s"
628 self.cursor.execute(sql, [0])
629 self.cursor.fetchone()
630 self.assertEqual(self.cursor.rowcount, 0)
631 self.cursor.execute(sql, [1])
632 self.cursor.fetchone()
633 self.assertEqual(self.cursor.rowcount, 1)
634 self.cursor.execute(sql, [1])
635 self.cursor.fetchone()
636 self.assertEqual(self.cursor.rowcount, 1)
637 self.cursor.execute(sql, [0])
638 self.cursor.fetchone()
639 self.assertEqual(self.cursor.rowcount, 0)
640
641 def testVarTypeNameNone(self):
642 "test that the typename attribute can be passed a value of None"
643 valueToSet = 5
644 var = self.cursor.var(int, typename=None)
645 var.setvalue(0, valueToSet)
646 self.assertEqual(var.getvalue(), valueToSet)
647
648 def testVarTypeWithObjectType(self):
649 "test that an object type can be used as type in cursor.var()"
650 objType = self.connection.gettype("UDT_OBJECT")
651 var = self.cursor.var(objType)
652 self.cursor.callproc("pkg_TestBindObject.BindObjectOut",
653 (28, "Bind obj out", var))
654 obj = var.getvalue()
655 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
656 (obj,))
657 self.assertEqual(result,
658 "udt_Object(28, 'Bind obj out', null, null, null, null, null)")
659
660 def testFetchXMLType(self):
661 "test that fetching an XMLType returns a string contains its contents"
662 intVal = 5
663 label = "IntCol"
664 expectedResult = "<%s>%s</%s>" % (label, intVal, label)
665 self.cursor.execute("""
666 select XMLElement("%s", IntCol)
667 from TestStrings
668 where IntCol = :intVal""" % label,
669 intVal = intVal)
670 result, = self.cursor.fetchone()
671 self.assertEqual(result, expectedResult)
672
673 if __name__ == "__main__":
674 TestEnv.RunTestCases()
675
+0
-93
test/CursorVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing cursor variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import sys
15
16 class TestCase(TestEnv.BaseTestCase):
17
18 def testBindCursor(self):
19 "test binding in a cursor"
20 cursor = self.connection.cursor()
21 self.assertEqual(cursor.description, None)
22 self.cursor.execute("""
23 begin
24 open :cursor for select 'X' StringValue from dual;
25 end;""",
26 cursor = cursor)
27 self.assertEqual(cursor.description,
28 [ ('STRINGVALUE', cx_Oracle.FIXED_CHAR, 1,
29 TestEnv.GetCharSetRatio(), None, None, 1) ])
30 self.assertEqual(cursor.fetchall(), [('X',)])
31
32 def testBindCursorInPackage(self):
33 "test binding in a cursor from a package"
34 cursor = self.connection.cursor()
35 self.assertEqual(cursor.description, None)
36 self.cursor.callproc("pkg_TestRefCursors.TestOutCursor", (2, cursor))
37 self.assertEqual(cursor.description,
38 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
39 ('STRINGCOL', cx_Oracle.STRING, 20, 20 *
40 TestEnv.GetCharSetRatio(), None, None, 0) ])
41 self.assertEqual(cursor.fetchall(),
42 [ (1, 'String 1'), (2, 'String 2') ])
43
44 def testBindSelf(self):
45 "test that binding the cursor itself is not supported"
46 cursor = self.connection.cursor()
47 sql = """
48 begin
49 open :pcursor for
50 select 1 from dual;
51 end;"""
52 self.assertRaises(cx_Oracle.DatabaseError, cursor.execute, sql,
53 pcursor = cursor)
54
55 def testExecuteAfterClose(self):
56 "test executing a statement returning a ref cursor after closing it"
57 outCursor = self.connection.cursor()
58 sql = """
59 begin
60 open :pcursor for
61 select IntCol
62 from TestNumbers
63 order by IntCol;
64 end;"""
65 self.cursor.execute(sql, pcursor = outCursor)
66 rows = outCursor.fetchall()
67 outCursor.close()
68 outCursor = self.connection.cursor()
69 self.cursor.execute(sql, pcursor = outCursor)
70 rows2 = outCursor.fetchall()
71 self.assertEqual(rows, rows2)
72
73 def testFetchCursor(self):
74 "test fetching a cursor"
75 self.cursor.execute("""
76 select
77 IntCol,
78 cursor(select IntCol + 1 from dual) CursorValue
79 from TestNumbers
80 order by IntCol""")
81 self.assertEqual(self.cursor.description,
82 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
83 ('CURSORVALUE', cx_Oracle.CURSOR, None, None, None, None,
84 1) ])
85 for i in range(1, 11):
86 number, cursor = self.cursor.fetchone()
87 self.assertEqual(number, i)
88 self.assertEqual(cursor.fetchall(), [(i + 1,)])
89
90 if __name__ == "__main__":
91 TestEnv.RunTestCases()
92
+0
-262
test/DMLReturning.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """Module for testing DML returning clauses."""
5
6 import TestEnv
7
8 import cx_Oracle
9
10 class TestCase(TestEnv.BaseTestCase):
11
12 def testInsert(self):
13 "test insert statement (single row) with DML returning"
14 self.cursor.execute("truncate table TestTempTable")
15 intVal = 5
16 strVal = "A test string"
17 intVar = self.cursor.var(cx_Oracle.NUMBER)
18 strVar = self.cursor.var(str)
19 self.cursor.execute("""
20 insert into TestTempTable (IntCol, StringCol)
21 values (:intVal, :strVal)
22 returning IntCol, StringCol into :intVar, :strVar""",
23 intVal = intVal,
24 strVal = strVal,
25 intVar = intVar,
26 strVar = strVar)
27 self.assertEqual(intVar.values, [[intVal]])
28 self.assertEqual(strVar.values, [[strVal]])
29
30 def testInsertMany(self):
31 "test insert statement (multiple rows) with DML returning"
32 self.cursor.execute("truncate table TestTempTable")
33 intValues = [5, 8, 17, 24, 6]
34 strValues = ["Test 5", "Test 8", "Test 17", "Test 24", "Test 6"]
35 intVar = self.cursor.var(cx_Oracle.NUMBER, arraysize = len(intValues))
36 strVar = self.cursor.var(str, arraysize = len(intValues))
37 self.cursor.setinputsizes(None, None, intVar, strVar)
38 data = list(zip(intValues, strValues))
39 self.cursor.executemany("""
40 insert into TestTempTable (IntCol, StringCol)
41 values (:intVal, :strVal)
42 returning IntCol, StringCol into :intVar, :strVar""", data)
43 self.assertEqual(intVar.values, [[v] for v in intValues])
44 self.assertEqual(strVar.values, [[v] for v in strValues])
45
46 def testInsertWithSmallSize(self):
47 "test insert statement with DML returning into too small a variable"
48 self.cursor.execute("truncate table TestTempTable")
49 intVal = 6
50 strVal = "A different test string"
51 intVar = self.cursor.var(cx_Oracle.NUMBER)
52 strVar = self.cursor.var(str, 2)
53 parameters = dict(intVal = intVal, strVal = strVal, intVar = intVar,
54 strVar = strVar)
55 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.execute, """
56 insert into TestTempTable (IntCol, StringCol)
57 values (:intVal, :strVal)
58 returning IntCol, StringCol into :intVar, :strVar""",
59 parameters)
60
61 def testUpdateSingleRow(self):
62 "test update single row statement with DML returning"
63 intVal = 7
64 strVal = "The updated value of the string"
65 self.cursor.execute("truncate table TestTempTable")
66 self.cursor.execute("""
67 insert into TestTempTable (IntCol, StringCol)
68 values (:1, :2)""",
69 (intVal, "The initial value of the string"))
70 intVar = self.cursor.var(cx_Oracle.NUMBER)
71 strVar = self.cursor.var(str)
72 self.cursor.execute("""
73 update TestTempTable set
74 StringCol = :strVal
75 where IntCol = :intVal
76 returning IntCol, StringCol into :intVar, :strVar""",
77 intVal = intVal,
78 strVal = strVal,
79 intVar = intVar,
80 strVar = strVar)
81 self.assertEqual(intVar.values, [[intVal]])
82 self.assertEqual(strVar.values, [[strVal]])
83
84 def testUpdateNoRows(self):
85 "test update no rows statement with DML returning"
86 intVal = 8
87 strVal = "The updated value of the string"
88 self.cursor.execute("truncate table TestTempTable")
89 self.cursor.execute("""
90 insert into TestTempTable (IntCol, StringCol)
91 values (:1, :2)""",
92 (intVal, "The initial value of the string"))
93 intVar = self.cursor.var(cx_Oracle.NUMBER)
94 strVar = self.cursor.var(str)
95 self.cursor.execute("""
96 update TestTempTable set
97 StringCol = :strVal
98 where IntCol = :intVal
99 returning IntCol, StringCol into :intVar, :strVar""",
100 intVal = intVal + 1,
101 strVal = strVal,
102 intVar = intVar,
103 strVar = strVar)
104 self.assertEqual(intVar.values, [[]])
105 self.assertEqual(strVar.values, [[]])
106 self.assertEqual(intVar.getvalue(), [])
107 self.assertEqual(strVar.getvalue(), [])
108
109 def testUpdateMultipleRows(self):
110 "test update multiple rows statement with DML returning"
111 self.cursor.execute("truncate table TestTempTable")
112 for i in (8, 9, 10):
113 self.cursor.execute("""
114 insert into TestTempTable (IntCol, StringCol)
115 values (:1, :2)""",
116 (i, "The initial value of string %d" % i))
117 intVar = self.cursor.var(cx_Oracle.NUMBER)
118 strVar = self.cursor.var(str)
119 self.cursor.execute("""
120 update TestTempTable set
121 IntCol = IntCol + 15,
122 StringCol = 'The final value of string ' || to_char(IntCol)
123 returning IntCol, StringCol into :intVar, :strVar""",
124 intVar = intVar,
125 strVar = strVar)
126 self.assertEqual(self.cursor.rowcount, 3)
127 self.assertEqual(intVar.values, [[23, 24, 25]])
128 self.assertEqual(strVar.values, [[
129 "The final value of string 8",
130 "The final value of string 9",
131 "The final value of string 10"
132 ]])
133
134 def testUpdateMultipleRowsExecuteMany(self):
135 "test update multiple rows with DML returning (executeMany)"
136 data = [(i, "The initial value of string %d" % i) \
137 for i in range(1, 11)]
138 self.cursor.execute("truncate table TestTempTable")
139 self.cursor.executemany("""
140 insert into TestTempTable (IntCol, StringCol)
141 values (:1, :2)""", data)
142 intVar = self.cursor.var(cx_Oracle.NUMBER, arraysize = 3)
143 strVar = self.cursor.var(str, arraysize = 3)
144 self.cursor.setinputsizes(None, intVar, strVar)
145 self.cursor.executemany("""
146 update TestTempTable set
147 IntCol = IntCol + 25,
148 StringCol = 'Updated value of string ' || to_char(IntCol)
149 where IntCol < :inVal
150 returning IntCol, StringCol into :intVar, :strVar""",
151 [[3], [8], [11]])
152 self.assertEqual(intVar.values, [
153 [26, 27],
154 [28, 29, 30, 31, 32],
155 [33, 34, 35]
156 ])
157 self.assertEqual(strVar.values, [
158 [ "Updated value of string 1",
159 "Updated value of string 2" ],
160 [ "Updated value of string 3",
161 "Updated value of string 4",
162 "Updated value of string 5",
163 "Updated value of string 6",
164 "Updated value of string 7" ],
165 [ "Updated value of string 8",
166 "Updated value of string 9",
167 "Updated value of string 10" ]
168 ])
169
170 def testInsertAndReturnObject(self):
171 "test inserting an object with DML returning"
172 typeObj = self.connection.gettype("UDT_OBJECT")
173 stringValue = "The string that will be verified"
174 obj = typeObj.newobject()
175 obj.STRINGVALUE = stringValue
176 outVar = self.cursor.var(cx_Oracle.OBJECT, typename = "UDT_OBJECT")
177 self.cursor.execute("""
178 insert into TestObjects (IntCol, ObjectCol)
179 values (4, :obj)
180 returning ObjectCol into :outObj""",
181 obj = obj, outObj = outVar)
182 result, = outVar.getvalue()
183 self.assertEqual(result.STRINGVALUE, stringValue)
184 self.connection.rollback()
185
186 def testInsertAndReturnRowid(self):
187 "test inserting a row and returning a rowid"
188 self.cursor.execute("truncate table TestTempTable")
189 var = self.cursor.var(cx_Oracle.ROWID)
190 self.cursor.execute("""
191 insert into TestTempTable (IntCol, StringCol)
192 values (278, 'String 278')
193 returning rowid into :1""", (var,))
194 rowid, = var.getvalue()
195 self.cursor.execute("""
196 select IntCol, StringCol
197 from TestTempTable
198 where rowid = :1""",
199 (rowid,))
200 self.assertEqual(self.cursor.fetchall(), [(278, 'String 278')])
201
202 def testInsertWithRefCursor(self):
203 "test inserting with a REF cursor and returning a rowid"
204 self.cursor.execute("truncate table TestTempTable")
205 var = self.cursor.var(cx_Oracle.ROWID)
206 inCursor = self.connection.cursor()
207 inCursor.execute("""
208 select StringCol
209 from TestStrings
210 where IntCol >= 5
211 order by IntCol""")
212 self.cursor.execute("""
213 insert into TestTempTable (IntCol, StringCol)
214 values (187, pkg_TestRefCursors.TestInCursor(:1))
215 returning rowid into :2""", (inCursor, var))
216 rowid, = var.getvalue()
217 self.cursor.execute("""
218 select IntCol, StringCol
219 from TestTempTable
220 where rowid = :1""",
221 (rowid,))
222 self.assertEqual(self.cursor.fetchall(),
223 [(187, 'String 7 (Modified)')])
224
225 def testDeleteReturningDecreasingRowsReturned(self):
226 "test delete returning multiple times with decreasing number of rows"
227 data = [(i, "Test String %d" % i) for i in range(1, 11)]
228 self.cursor.execute("truncate table TestTempTable")
229 self.cursor.executemany("""
230 insert into TestTempTable (IntCol, StringCol)
231 values (:1, :2)""", data)
232 results = []
233 intVar = self.cursor.var(int)
234 self.cursor.setinputsizes(None, intVar)
235 for intVal in (5, 8, 10):
236 self.cursor.execute("""
237 delete from TestTempTable
238 where IntCol < :1
239 returning IntCol into :2""", [intVal])
240 results.append(intVar.getvalue())
241 self.assertEqual(results, [ [1, 2, 3, 4], [5, 6, 7], [8, 9] ])
242
243 def testDeleteReturningNoRowsAfterManyRows(self):
244 "test delete returning no rows after initially returning many rows"
245 data = [(i, "Test String %d" % i) for i in range(1, 11)]
246 self.cursor.execute("truncate table TestTempTable")
247 self.cursor.executemany("""
248 insert into TestTempTable (IntCol, StringCol)
249 values (:1, :2)""", data)
250 intVar = self.cursor.var(int)
251 self.cursor.execute("""
252 delete from TestTempTable
253 where IntCol < :1
254 returning IntCol into :2""", [5, intVar])
255 self.assertEqual(intVar.getvalue(), [1, 2, 3, 4])
256 self.cursor.execute(None, [4, intVar])
257 self.assertEqual(intVar.getvalue(), [])
258
259 if __name__ == "__main__":
260 TestEnv.RunTestCases()
261
+0
-251
test/DateTimeVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing date/time variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import datetime
15 import time
16
17 class TestCase(TestEnv.BaseTestCase):
18
19 def setUp(self):
20 TestEnv.BaseTestCase.setUp(self)
21 self.rawData = []
22 self.dataByKey = {}
23 for i in range(1, 11):
24 timeTuple = (2002, 12, 9, 0, 0, 0, 0, 0, -1)
25 timeInTicks = time.mktime(timeTuple) + i * 86400 + i * 8640
26 dateCol = cx_Oracle.TimestampFromTicks(int(timeInTicks))
27 if i % 2:
28 timeInTicks = time.mktime(timeTuple) + i * 86400 * 2 + \
29 i * 12960
30 nullableCol = cx_Oracle.TimestampFromTicks(int(timeInTicks))
31 else:
32 nullableCol = None
33 tuple = (i, dateCol, nullableCol)
34 self.rawData.append(tuple)
35 self.dataByKey[i] = tuple
36
37 def testBindDate(self):
38 "test binding in a date"
39 self.cursor.execute("""
40 select * from TestDates
41 where DateCol = :value""",
42 value = cx_Oracle.Timestamp(2002, 12, 13, 9, 36, 0))
43 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[4]])
44
45 def testBindDateTime(self):
46 "test binding in a Python 2.3 and higher date time"
47 self.cursor.execute("""
48 select * from TestDates
49 where DateCol = :value""",
50 value = datetime.datetime(2002, 12, 13, 9, 36, 0))
51 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[4]])
52
53 def testBindDateInDateTimeVar(self):
54 "test binding date in a datetime variable"
55 var = self.cursor.var(cx_Oracle.DATETIME)
56 dateVal = datetime.date.today()
57 var.setvalue(0, dateVal)
58 self.assertEqual(var.getvalue().date(), dateVal)
59
60 def testBindDateAfterString(self):
61 "test binding in a date after setting input sizes to a string"
62 self.cursor.setinputsizes(value = 15)
63 self.cursor.execute("""
64 select * from TestDates
65 where DateCol = :value""",
66 value = cx_Oracle.Timestamp(2002, 12, 14, 12, 0, 0))
67 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[5]])
68
69 def testBindNull(self):
70 "test binding in a null"
71 self.cursor.setinputsizes(value = cx_Oracle.DATETIME)
72 self.cursor.execute("""
73 select * from TestDates
74 where DateCol = :value""",
75 value = None)
76 self.assertEqual(self.cursor.fetchall(), [])
77
78 def testBindDateArrayDirect(self):
79 "test binding in a date array"
80 returnValue = self.cursor.var(cx_Oracle.NUMBER)
81 array = [r[1] for r in self.rawData]
82 statement = """
83 begin
84 :returnValue := pkg_TestDateArrays.TestInArrays(
85 :startValue, :baseDate, :array);
86 end;"""
87 self.cursor.execute(statement,
88 returnValue = returnValue,
89 startValue = 5,
90 baseDate = cx_Oracle.Date(2002, 12, 12),
91 array = array)
92 self.assertEqual(returnValue.getvalue(), 35.5)
93 array = array + array[:5]
94 self.cursor.execute(statement,
95 startValue = 7,
96 baseDate = cx_Oracle.Date(2002, 12, 13),
97 array = array)
98 self.assertEqual(returnValue.getvalue(), 24.0)
99
100 def testBindDateArrayBySizes(self):
101 "test binding in a date array (with setinputsizes)"
102 returnValue = self.cursor.var(cx_Oracle.NUMBER)
103 self.cursor.setinputsizes(array = [cx_Oracle.DATETIME, 10])
104 array = [r[1] for r in self.rawData]
105 self.cursor.execute("""
106 begin
107 :returnValue := pkg_TestDateArrays.TestInArrays(
108 :startValue, :baseDate, :array);
109 end;""",
110 returnValue = returnValue,
111 startValue = 6,
112 baseDate = cx_Oracle.Date(2002, 12, 13),
113 array = array)
114 self.assertEqual(returnValue.getvalue(), 26.5)
115
116 def testBindDateArrayByVar(self):
117 "test binding in a date array (with arrayvar)"
118 returnValue = self.cursor.var(cx_Oracle.NUMBER)
119 array = self.cursor.arrayvar(cx_Oracle.DATETIME, 10, 20)
120 array.setvalue(0, [r[1] for r in self.rawData])
121 self.cursor.execute("""
122 begin
123 :returnValue := pkg_TestDateArrays.TestInArrays(
124 :startValue, :baseDate, :array);
125 end;""",
126 returnValue = returnValue,
127 startValue = 7,
128 baseDate = cx_Oracle.Date(2002, 12, 14),
129 array = array)
130 self.assertEqual(returnValue.getvalue(), 17.5)
131
132 def testBindInOutDateArrayByVar(self):
133 "test binding in/out a date array (with arrayvar)"
134 array = self.cursor.arrayvar(cx_Oracle.DATETIME, 10, 100)
135 originalData = [r[1] for r in self.rawData]
136 array.setvalue(0, originalData)
137 self.cursor.execute("""
138 begin
139 pkg_TestDateArrays.TestInOutArrays(:numElems, :array);
140 end;""",
141 numElems = 5,
142 array = array)
143 self.assertEqual(array.getvalue(),
144 [ cx_Oracle.Timestamp(2002, 12, 17, 2, 24, 0),
145 cx_Oracle.Timestamp(2002, 12, 18, 4, 48, 0),
146 cx_Oracle.Timestamp(2002, 12, 19, 7, 12, 0),
147 cx_Oracle.Timestamp(2002, 12, 20, 9, 36, 0),
148 cx_Oracle.Timestamp(2002, 12, 21, 12, 0, 0) ] + \
149 originalData[5:])
150
151 def testBindOutDateArrayByVar(self):
152 "test binding out a date array (with arrayvar)"
153 array = self.cursor.arrayvar(cx_Oracle.DATETIME, 6, 100)
154 self.cursor.execute("""
155 begin
156 pkg_TestDateArrays.TestOutArrays(:numElems, :array);
157 end;""",
158 numElems = 6,
159 array = array)
160 self.assertEqual(array.getvalue(),
161 [ cx_Oracle.Timestamp(2002, 12, 13, 4, 48, 0),
162 cx_Oracle.Timestamp(2002, 12, 14, 9, 36, 0),
163 cx_Oracle.Timestamp(2002, 12, 15, 14, 24, 0),
164 cx_Oracle.Timestamp(2002, 12, 16, 19, 12, 0),
165 cx_Oracle.Timestamp(2002, 12, 18, 0, 0, 0),
166 cx_Oracle.Timestamp(2002, 12, 19, 4, 48, 0) ])
167
168 def testBindOutSetInputSizes(self):
169 "test binding out with set input sizes defined"
170 vars = self.cursor.setinputsizes(value = cx_Oracle.DATETIME)
171 self.cursor.execute("""
172 begin
173 :value := to_date(20021209, 'YYYYMMDD');
174 end;""")
175 self.assertEqual(vars["value"].getvalue(),
176 cx_Oracle.Timestamp(2002, 12, 9))
177
178 def testBindInOutSetInputSizes(self):
179 "test binding in/out with set input sizes defined"
180 vars = self.cursor.setinputsizes(value = cx_Oracle.DATETIME)
181 self.cursor.execute("""
182 begin
183 :value := :value + 5.25;
184 end;""",
185 value = cx_Oracle.Timestamp(2002, 12, 12, 10, 0, 0))
186 self.assertEqual(vars["value"].getvalue(),
187 cx_Oracle.Timestamp(2002, 12, 17, 16, 0, 0))
188
189 def testBindOutVar(self):
190 "test binding out with cursor.var() method"
191 var = self.cursor.var(cx_Oracle.DATETIME)
192 self.cursor.execute("""
193 begin
194 :value := to_date('20021231 12:31:00',
195 'YYYYMMDD HH24:MI:SS');
196 end;""",
197 value = var)
198 self.assertEqual(var.getvalue(),
199 cx_Oracle.Timestamp(2002, 12, 31, 12, 31, 0))
200
201 def testBindInOutVarDirectSet(self):
202 "test binding in/out with cursor.var() method"
203 var = self.cursor.var(cx_Oracle.DATETIME)
204 var.setvalue(0, cx_Oracle.Timestamp(2002, 12, 9, 6, 0, 0))
205 self.cursor.execute("""
206 begin
207 :value := :value + 5.25;
208 end;""",
209 value = var)
210 self.assertEqual(var.getvalue(),
211 cx_Oracle.Timestamp(2002, 12, 14, 12, 0, 0))
212
213 def testCursorDescription(self):
214 "test cursor description is accurate"
215 self.cursor.execute("select * from TestDates")
216 self.assertEqual(self.cursor.description,
217 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
218 ('DATECOL', cx_Oracle.DATETIME, 23, None, None, None, 0),
219 ('NULLABLECOL', cx_Oracle.DATETIME, 23, None, None, None,
220 1) ])
221
222 def testFetchAll(self):
223 "test that fetching all of the data returns the correct results"
224 self.cursor.execute("select * From TestDates order by IntCol")
225 self.assertEqual(self.cursor.fetchall(), self.rawData)
226 self.assertEqual(self.cursor.fetchall(), [])
227
228 def testFetchMany(self):
229 "test that fetching data in chunks returns the correct results"
230 self.cursor.execute("select * From TestDates order by IntCol")
231 self.assertEqual(self.cursor.fetchmany(3), self.rawData[0:3])
232 self.assertEqual(self.cursor.fetchmany(2), self.rawData[3:5])
233 self.assertEqual(self.cursor.fetchmany(4), self.rawData[5:9])
234 self.assertEqual(self.cursor.fetchmany(3), self.rawData[9:])
235 self.assertEqual(self.cursor.fetchmany(3), [])
236
237 def testFetchOne(self):
238 "test that fetching a single row returns the correct results"
239 self.cursor.execute("""
240 select *
241 from TestDates
242 where IntCol in (3, 4)
243 order by IntCol""")
244 self.assertEqual(self.cursor.fetchone(), self.dataByKey[3])
245 self.assertEqual(self.cursor.fetchone(), self.dataByKey[4])
246 self.assertEqual(self.cursor.fetchone(), None)
247
248 if __name__ == "__main__":
249 TestEnv.RunTestCases()
250
00 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
22 #------------------------------------------------------------------------------
33
44 #------------------------------------------------------------------------------
77 # Drops the database objects used for the cx_Oracle test suite.
88 #------------------------------------------------------------------------------
99
10 from __future__ import print_function
10 import cx_Oracle
11 import test_env
1112
12 import cx_Oracle
13 import TestEnv
14
15 def DropTests(conn):
13 def drop_tests(conn):
1614 print("Dropping test schemas...")
17 TestEnv.RunSqlScript(conn, "DropTest",
18 main_user = TestEnv.GetMainUser(),
19 proxy_user = TestEnv.GetProxyUser())
15 test_env.run_sql_script(conn, "DropTest",
16 main_user=test_env.get_main_user(),
17 proxy_user=test_env.get_proxy_user())
2018
2119 if __name__ == "__main__":
22 conn = cx_Oracle.connect(TestEnv.GetSysdbaConnectString(),
23 mode = cx_Oracle.SYSDBA)
24 DropTests(conn)
20 conn = cx_Oracle.connect(test_env.get_admin_connect_string())
21 drop_tests(conn)
2522 print("Done.")
26
+0
-45
test/Error.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing error objects."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import pickle
15
16 class TestCase(TestEnv.BaseTestCase):
17
18 def testPickleError(self):
19 "test picking/unpickling an error object"
20 errorObj = None
21 try:
22 self.cursor.execute("""
23 begin
24 raise_application_error(-20101, 'Test!');
25 end;""")
26 except cx_Oracle.Error as e:
27 errorObj, = e.args
28 self.assertEqual(type(errorObj), cx_Oracle._Error)
29 self.assertTrue("Test!" in errorObj.message)
30 self.assertEqual(errorObj.code, 20101)
31 self.assertEqual(errorObj.offset, 0)
32 self.assertTrue(isinstance(errorObj.isrecoverable, bool))
33 pickledData = pickle.dumps(errorObj)
34 newErrorObj = pickle.loads(pickledData)
35 self.assertEqual(type(newErrorObj), cx_Oracle._Error)
36 self.assertTrue(newErrorObj.message == errorObj.message)
37 self.assertTrue(newErrorObj.code == errorObj.code)
38 self.assertTrue(newErrorObj.offset == errorObj.offset)
39 self.assertTrue(newErrorObj.context == errorObj.context)
40 self.assertTrue(newErrorObj.isrecoverable == errorObj.isrecoverable)
41
42 if __name__ == "__main__":
43 TestEnv.RunTestCases()
44
+0
-424
test/Features12_1.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing features introduced in 12.1"""
10
11 import TestEnv
12
13 import cx_Oracle
14 import datetime
15 # import sys
16
17 # if sys.version_info > (3,):
18 # long = int
19
20 class TestCase(TestEnv.BaseTestCase):
21
22 def testArrayDMLRowCountsOff(self):
23 "test executing with arraydmlrowcounts mode disabled"
24 self.cursor.execute("truncate table TestArrayDML")
25 rows = [ (1, "First"),
26 (2, "Second") ]
27 sql = "insert into TestArrayDML (IntCol,StringCol) values (:1,:2)"
28 self.cursor.executemany(sql, rows, arraydmlrowcounts = False)
29 self.assertRaises(cx_Oracle.DatabaseError,
30 self.cursor.getarraydmlrowcounts)
31 rows = [ (3, "Third"),
32 (4, "Fourth") ]
33 self.cursor.executemany(sql, rows)
34 self.assertRaises(cx_Oracle.DatabaseError,
35 self.cursor.getarraydmlrowcounts)
36
37 def testArrayDMLRowCountsOn(self):
38 "test executing with arraydmlrowcounts mode enabled"
39 self.cursor.execute("truncate table TestArrayDML")
40 rows = [ ( 1, "First", 100),
41 ( 2, "Second", 200),
42 ( 3, "Third", 300),
43 ( 4, "Fourth", 300),
44 ( 5, "Fifth", 300) ]
45 sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \
46 "values (:1,:2,:3)"
47 self.cursor.executemany(sql, rows, arraydmlrowcounts = True)
48 self.connection.commit()
49 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 1, 1, 1])
50 self.cursor.execute("select count(*) from TestArrayDML")
51 count, = self.cursor.fetchone()
52 self.assertEqual(count, len(rows))
53
54 def testBindPLSQLBooleanCollectionIn(self):
55 "test binding a boolean collection (in)"
56 typeObj = self.connection.gettype("PKG_TESTBOOLEANS.UDT_BOOLEANLIST")
57 obj = typeObj.newobject()
58 obj.setelement(1, True)
59 obj.extend([True, False, True, True, False, True])
60 result = self.cursor.callfunc("pkg_TestBooleans.TestInArrays", int,
61 (obj,))
62 self.assertEqual(result, 5)
63
64 def testBindPLSQLBooleanCollectionOut(self):
65 "test binding a boolean collection (out)"
66 typeObj = self.connection.gettype("PKG_TESTBOOLEANS.UDT_BOOLEANLIST")
67 obj = typeObj.newobject()
68 self.cursor.callproc("pkg_TestBooleans.TestOutArrays", (6, obj))
69 self.assertEqual(obj.aslist(), [True, False, True, False, True, False])
70
71 def testBindPLSQLDateCollectionIn(self):
72 "test binding a PL/SQL date collection (in)"
73 typeObj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST")
74 obj = typeObj.newobject()
75 obj.setelement(1, datetime.datetime(2016, 2, 5))
76 obj.append(datetime.datetime(2016, 2, 8, 12, 15, 30))
77 obj.append(datetime.datetime(2016, 2, 12, 5, 44, 30))
78 result = self.cursor.callfunc("pkg_TestDateArrays.TestInArrays",
79 cx_Oracle.NUMBER, (2, datetime.datetime(2016, 2, 1), obj))
80 self.assertEqual(result, 24.75)
81
82 def testBindPLSQLDateCollectionInOut(self):
83 "test binding a PL/SQL date collection (in/out)"
84 typeObj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST")
85 obj = typeObj.newobject()
86 obj.setelement(1, datetime.datetime(2016, 1, 1))
87 obj.append(datetime.datetime(2016, 1, 7))
88 obj.append(datetime.datetime(2016, 1, 13))
89 obj.append(datetime.datetime(2016, 1, 19))
90 self.cursor.callproc("pkg_TestDateArrays.TestInOutArrays", (4, obj))
91 self.assertEqual(obj.aslist(),
92 [datetime.datetime(2016, 1, 8),
93 datetime.datetime(2016, 1, 14),
94 datetime.datetime(2016, 1, 20),
95 datetime.datetime(2016, 1, 26)])
96
97 def testBindPLSQLDateCollectionOut(self):
98 "test binding a PL/SQL date collection (out)"
99 typeObj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST")
100 obj = typeObj.newobject()
101 self.cursor.callproc("pkg_TestDateArrays.TestOutArrays", (3, obj))
102 self.assertEqual(obj.aslist(),
103 [datetime.datetime(2002, 12, 13, 4, 48),
104 datetime.datetime(2002, 12, 14, 9, 36),
105 datetime.datetime(2002, 12, 15, 14, 24)])
106
107 def testBindPLSQLNumberCollectionIn(self):
108 "test binding a PL/SQL number collection (in)"
109 typeObj = self.connection.gettype("PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST")
110 obj = typeObj.newobject()
111 obj.setelement(1, 10)
112 obj.extend([20, 30, 40, 50])
113 result = self.cursor.callfunc("pkg_TestNumberArrays.TestInArrays", int,
114 (5, obj))
115 self.assertEqual(result, 155)
116
117 def testBindPLSQLNumberCollectionInOut(self):
118 "test binding a PL/SQL number collection (in/out)"
119 typeObj = self.connection.gettype("PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST")
120 obj = typeObj.newobject()
121 obj.setelement(1, 5)
122 obj.extend([8, 3, 2])
123 self.cursor.callproc("pkg_TestNumberArrays.TestInOutArrays", (4, obj))
124 self.assertEqual(obj.aslist(), [50, 80, 30, 20])
125
126 def testBindPLSQLNumberCollectionOut(self):
127 "test binding a PL/SQL number collection (out)"
128 typeObj = self.connection.gettype("PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST")
129 obj = typeObj.newobject()
130 self.cursor.callproc("pkg_TestNumberArrays.TestOutArrays", (3, obj))
131 self.assertEqual(obj.aslist(), [100, 200, 300])
132
133 def testBindPLSQLRecordArray(self):
134 "test binding an array of PL/SQL records (in)"
135 recType = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD")
136 arrayType = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORDARRAY")
137 arrayObj = arrayType.newobject()
138 for i in range(3):
139 obj = recType.newobject()
140 obj.NUMBERVALUE = i + 1
141 obj.STRINGVALUE = "String in record #%d" % (i + 1)
142 obj.DATEVALUE = datetime.datetime(2017, i + 1, 1)
143 obj.TIMESTAMPVALUE = datetime.datetime(2017, 1, i + 1)
144 obj.BOOLEANVALUE = (i % 2) == 1
145 arrayObj.append(obj)
146 result = self.cursor.callfunc("pkg_TestRecords.TestInArrays", str,
147 (arrayObj,))
148 self.assertEqual(result,
149 "udt_Record(1, 'String in record #1', " \
150 "to_date('2017-01-01', 'YYYY-MM-DD'), " \
151 "to_timestamp('2017-01-01 00:00:00', " \
152 "'YYYY-MM-DD HH24:MI:SS'), false); " \
153 "udt_Record(2, 'String in record #2', " \
154 "to_date('2017-02-01', 'YYYY-MM-DD'), " \
155 "to_timestamp('2017-01-02 00:00:00', " \
156 "'YYYY-MM-DD HH24:MI:SS'), true); " \
157 "udt_Record(3, 'String in record #3', " \
158 "to_date('2017-03-01', 'YYYY-MM-DD'), " \
159 "to_timestamp('2017-01-03 00:00:00', " \
160 "'YYYY-MM-DD HH24:MI:SS'), false)")
161
162 def testBindPLSQLRecordIn(self):
163 "test binding a PL/SQL record (in)"
164 typeObj = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD")
165 obj = typeObj.newobject()
166 obj.NUMBERVALUE = 18
167 obj.STRINGVALUE = "A string in a record"
168 obj.DATEVALUE = datetime.datetime(2016, 2, 15)
169 obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 12, 14, 25, 36)
170 obj.BOOLEANVALUE = False
171 result = self.cursor.callfunc("pkg_TestRecords.GetStringRep", str,
172 (obj,))
173 self.assertEqual(result,
174 "udt_Record(18, 'A string in a record', " \
175 "to_date('2016-02-15', 'YYYY-MM-DD'), " \
176 "to_timestamp('2016-02-12 14:25:36', " \
177 "'YYYY-MM-DD HH24:MI:SS'), false)")
178
179 def testBindPLSQLRecordOut(self):
180 "test binding a PL/SQL record (out)"
181 typeObj = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD")
182 obj = typeObj.newobject()
183 obj.NUMBERVALUE = 5
184 obj.STRINGVALUE = "Test value"
185 obj.DATEVALUE = datetime.datetime.today()
186 obj.TIMESTAMPVALUE = datetime.datetime.today()
187 obj.BOOLEANVALUE = False
188 self.cursor.callproc("pkg_TestRecords.TestOut", (obj,))
189 self.assertEqual(obj.NUMBERVALUE, 25)
190 self.assertEqual(obj.STRINGVALUE, "String in record")
191 self.assertEqual(obj.DATEVALUE, datetime.datetime(2016, 2, 16))
192 self.assertEqual(obj.TIMESTAMPVALUE,
193 datetime.datetime(2016, 2, 16, 18, 23, 55))
194 self.assertEqual(obj.BOOLEANVALUE, True)
195
196 def testBindPLSQLStringCollectionIn(self):
197 "test binding a PL/SQL string collection (in)"
198 typeObj = self.connection.gettype("PKG_TESTSTRINGARRAYS.UDT_STRINGLIST")
199 obj = typeObj.newobject()
200 obj.setelement(1, "First element")
201 obj.setelement(2, "Second element")
202 obj.setelement(3, "Third element")
203 result = self.cursor.callfunc("pkg_TestStringArrays.TestInArrays", int,
204 (5, obj))
205 self.assertEqual(result, 45)
206
207 def testBindPLSQLStringCollectionInOut(self):
208 "test binding a PL/SQL string collection (in/out)"
209 typeObj = self.connection.gettype("PKG_TESTSTRINGARRAYS.UDT_STRINGLIST")
210 obj = typeObj.newobject()
211 obj.setelement(1, "The first element")
212 obj.append("The second element")
213 obj.append("The third and final element")
214 self.cursor.callproc("pkg_TestStringArrays.TestInOutArrays", (3, obj))
215 self.assertEqual(obj.aslist(),
216 ['Converted element # 1 originally had length 17',
217 'Converted element # 2 originally had length 18',
218 'Converted element # 3 originally had length 27'])
219
220 def testBindPLSQLStringCollectionOut(self):
221 "test binding a PL/SQL string collection (out)"
222 typeObj = self.connection.gettype("PKG_TESTSTRINGARRAYS.UDT_STRINGLIST")
223 obj = typeObj.newobject()
224 self.cursor.callproc("pkg_TestStringArrays.TestOutArrays", (4, obj))
225 self.assertEqual(obj.aslist(),
226 ['Test out element # 1',
227 'Test out element # 2',
228 'Test out element # 3',
229 'Test out element # 4'])
230
231 def testBindPLSQLStringCollectionOutWithHoles(self):
232 "test binding a PL/SQL string collection (out with holes)"
233 typeObj = self.connection.gettype("PKG_TESTSTRINGARRAYS.UDT_STRINGLIST")
234 obj = typeObj.newobject()
235 self.cursor.callproc("pkg_TestStringArrays.TestIndexBy", (obj,))
236 self.assertEqual(obj.first(), -1048576)
237 self.assertEqual(obj.last(), 8388608)
238 self.assertEqual(obj.next(-576), 284)
239 self.assertEqual(obj.prev(284), -576)
240 self.assertEqual(obj.size(), 4)
241 self.assertEqual(obj.exists(-576), True)
242 self.assertEqual(obj.exists(-577), False)
243 self.assertEqual(obj.getelement(284), 'Third element')
244 self.assertEqual(obj.aslist(),
245 ["First element", "Second element", "Third element",
246 "Fourth element"])
247 self.assertEqual(obj.asdict(),
248 { -1048576 : 'First element',
249 -576 : 'Second element',
250 284 : 'Third element',
251 8388608: 'Fourth element' })
252 obj.delete(-576)
253 obj.delete(284)
254 self.assertEqual(obj.aslist(), ["First element", "Fourth element"])
255 self.assertEqual(obj.asdict(),
256 { -1048576 : 'First element',
257 8388608: 'Fourth element' })
258
259 def testExceptionInIteration(self):
260 "test executing with arraydmlrowcounts with exception"
261 self.cursor.execute("truncate table TestArrayDML")
262 rows = [ (1, "First"),
263 (2, "Second"),
264 (2, "Third"),
265 (4, "Fourth") ]
266 sql = "insert into TestArrayDML (IntCol,StringCol) values (:1,:2)"
267 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.executemany,
268 sql, rows, arraydmlrowcounts = True)
269 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1])
270
271 def testExecutingDelete(self):
272 "test executing delete statement with arraydmlrowcount mode"
273 self.cursor.execute("truncate table TestArrayDML")
274 rows = [ (1, "First", 100),
275 (2, "Second", 200),
276 (3, "Third", 300),
277 (4, "Fourth", 300),
278 (5, "Fifth", 300),
279 (6, "Sixth", 400),
280 (7, "Seventh", 400),
281 (8, "Eighth", 500) ]
282 sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \
283 "values (:1, :2, :3)"
284 self.cursor.executemany(sql, rows)
285 rows = [ (200,), (300,), (400,) ]
286 statement = "delete from TestArrayDML where IntCol2 = :1"
287 self.cursor.executemany(statement, rows, arraydmlrowcounts = True)
288 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 3, 2])
289
290 def testExecutingUpdate(self):
291 "test executing update statement with arraydmlrowcount mode"
292 self.cursor.execute("truncate table TestArrayDML")
293 rows = [ (1, "First",100),
294 (2, "Second",200),
295 (3, "Third",300),
296 (4, "Fourth",300),
297 (5, "Fifth",300),
298 (6, "Sixth",400),
299 (7, "Seventh",400),
300 (8, "Eighth",500) ]
301 sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \
302 "values (:1, :2, :3)"
303 self.cursor.executemany(sql, rows)
304 rows = [ ("One", 100),
305 ("Two", 200),
306 ("Three", 300),
307 ("Four", 400) ]
308 sql = "update TestArrayDML set StringCol = :1 where IntCol2 = :2"
309 self.cursor.executemany(sql, rows, arraydmlrowcounts = True)
310 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 3, 2])
311
312 def testImplicitResults(self):
313 "test getimplicitresults() returns the correct data"
314 self.cursor.execute("""
315 declare
316 c1 sys_refcursor;
317 c2 sys_refcursor;
318 begin
319
320 open c1 for
321 select NumberCol
322 from TestNumbers
323 where IntCol between 3 and 5;
324
325 dbms_sql.return_result(c1);
326
327 open c2 for
328 select NumberCol
329 from TestNumbers
330 where IntCol between 7 and 10;
331
332 dbms_sql.return_result(c2);
333
334 end;""")
335 results = self.cursor.getimplicitresults()
336 self.assertEqual(len(results), 2)
337 self.assertEqual([n for n, in results[0]], [3.75, 5, 6.25])
338 self.assertEqual([n for n, in results[1]], [8.75, 10, 11.25, 12.5])
339
340 def testImplicitResultsNoStatement(self):
341 "test getimplicitresults() without executing a statement"
342 self.assertRaises(cx_Oracle.InterfaceError,
343 self.cursor.getimplicitresults)
344
345 def testInsertWithBatchError(self):
346 "test executing insert with multiple distinct batch errors"
347 self.cursor.execute("truncate table TestArrayDML")
348 rows = [ (1, "First", 100),
349 (2, "Second", 200),
350 (2, "Third", 300),
351 (4, "Fourth", 400),
352 (5, "Fourth", 1000)]
353 sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \
354 "values (:1, :2, :3)"
355 self.cursor.executemany(sql, rows, batcherrors = True,
356 arraydmlrowcounts = True)
357 user = TestEnv.GetMainUser()
358 expectedErrors = [
359 ( 4, 1438, "ORA-01438: value larger than specified " \
360 "precision allowed for this column" ),
361 ( 2, 1, "ORA-00001: unique constraint " \
362 "(%s.TESTARRAYDML_PK) violated" % user.upper())
363 ]
364 actualErrors = [(e.offset, e.code, e.message) \
365 for e in self.cursor.getbatcherrors()]
366 self.assertEqual(actualErrors, expectedErrors)
367 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 0, 1, 0])
368
369 def testBatchErrorFalse(self):
370 "test batcherrors mode set to False"
371 self.cursor.execute("truncate table TestArrayDML")
372 rows = [ (1, "First", 100),
373 (2, "Second", 200),
374 (2, "Third", 300) ]
375 sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \
376 "values (:1, :2, :3)"
377 self.assertRaises(cx_Oracle.IntegrityError,
378 self.cursor.executemany, sql, rows, batcherrors = False)
379
380 def testUpdatewithBatchError(self):
381 "test executing in succession with batch error"
382 self.cursor.execute("truncate table TestArrayDML")
383 rows = [ (1, "First", 100),
384 (2, "Second", 200),
385 (3, "Third", 300),
386 (4, "Second", 300),
387 (5, "Fifth", 300),
388 (6, "Sixth", 400),
389 (6, "Seventh", 400),
390 (8, "Eighth", 100) ]
391 sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \
392 "values (:1, :2, :3)"
393 self.cursor.executemany(sql, rows, batcherrors = True)
394 user = TestEnv.GetMainUser()
395 expectedErrors = [
396 ( 6, 1, "ORA-00001: unique constraint " \
397 "(%s.TESTARRAYDML_PK) violated" % user.upper())
398 ]
399 actualErrors = [(e.offset, e.code, e.message) \
400 for e in self.cursor.getbatcherrors()]
401 self.assertEqual(actualErrors, expectedErrors)
402 rows = [ (101, "First"),
403 (201, "Second"),
404 (3000, "Third"),
405 (900, "Ninth"),
406 (301, "Third") ]
407 sql = "update TestArrayDML set IntCol2 = :1 where StringCol = :2"
408 self.cursor.executemany(sql, rows, arraydmlrowcounts = True,
409 batcherrors = True)
410 expectedErrors = [
411 ( 2, 1438, "ORA-01438: value larger than specified " \
412 "precision allowed for this column" )
413 ]
414 actualErrors = [(e.offset, e.code, e.message) \
415 for e in self.cursor.getbatcherrors()]
416 self.assertEqual(actualErrors, expectedErrors)
417 self.assertEqual(self.cursor.getarraydmlrowcounts(),
418 [1, 2, 0, 0, 1])
419 self.assertEqual(self.cursor.rowcount, 4)
420
421 if __name__ == "__main__":
422 TestEnv.RunTestCases()
423
+0
-148
test/IntervalVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing interval variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import datetime
15
16 class TestCase(TestEnv.BaseTestCase):
17
18 def setUp(self):
19 TestEnv.BaseTestCase.setUp(self)
20 self.rawData = []
21 self.dataByKey = {}
22 for i in range(1, 11):
23 delta = datetime.timedelta(days = i, hours = i, minutes = i * 2,
24 seconds = i * 3)
25 if i % 2 == 0:
26 nullableDelta = None
27 else:
28 nullableDelta = datetime.timedelta(days = i + 5, hours = i + 2,
29 minutes = i * 2 + 5, seconds = i * 3 + 5)
30 tuple = (i, delta, nullableDelta)
31 self.rawData.append(tuple)
32 self.dataByKey[i] = tuple
33
34 def testBindInterval(self):
35 "test binding in an interval"
36 self.cursor.setinputsizes(value = cx_Oracle.INTERVAL)
37 self.cursor.execute("""
38 select * from TestIntervals
39 where IntervalCol = :value""",
40 value = datetime.timedelta(days = 5, hours = 5, minutes = 10,
41 seconds = 15))
42 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[5]])
43
44 def testBindNull(self):
45 "test binding in a null"
46 self.cursor.setinputsizes(value = cx_Oracle.INTERVAL)
47 self.cursor.execute("""
48 select * from TestIntervals
49 where IntervalCol = :value""",
50 value = None)
51 self.assertEqual(self.cursor.fetchall(), [])
52
53 def testBindOutSetInputSizes(self):
54 "test binding out with set input sizes defined"
55 vars = self.cursor.setinputsizes(value = cx_Oracle.INTERVAL)
56 self.cursor.execute("""
57 begin
58 :value := to_dsinterval('8 09:24:18.123789');
59 end;""")
60 self.assertEqual(vars["value"].getvalue(),
61 datetime.timedelta(days = 8, hours = 9, minutes = 24,
62 seconds = 18, microseconds = 123789))
63
64 def testBindInOutSetInputSizes(self):
65 "test binding in/out with set input sizes defined"
66 vars = self.cursor.setinputsizes(value = cx_Oracle.INTERVAL)
67 self.cursor.execute("""
68 begin
69 :value := :value + to_dsinterval('5 08:30:00');
70 end;""",
71 value = datetime.timedelta(days = 5, hours = 2, minutes = 15))
72 self.assertEqual(vars["value"].getvalue(),
73 datetime.timedelta(days = 10, hours = 10, minutes = 45))
74
75 def testBindInOutFractionalSecond(self):
76 "test binding in/out with set input sizes defined"
77 vars = self.cursor.setinputsizes(value = cx_Oracle.INTERVAL)
78 self.cursor.execute("""
79 begin
80 :value := :value + to_dsinterval('5 08:30:00');
81 end;""",
82 value = datetime.timedelta(days = 5, seconds=12.123789))
83 self.assertEqual(vars["value"].getvalue(),
84 datetime.timedelta(days = 10, hours = 8, minutes = 30,
85 seconds=12, microseconds=123789))
86
87 def testBindOutVar(self):
88 "test binding out with cursor.var() method"
89 var = self.cursor.var(cx_Oracle.INTERVAL)
90 self.cursor.execute("""
91 begin
92 :value := to_dsinterval('15 18:35:45.586');
93 end;""",
94 value = var)
95 self.assertEqual(var.getvalue(),
96 datetime.timedelta(days = 15, hours = 18, minutes = 35,
97 seconds = 45, milliseconds = 586))
98
99 def testBindInOutVarDirectSet(self):
100 "test binding in/out with cursor.var() method"
101 var = self.cursor.var(cx_Oracle.INTERVAL)
102 var.setvalue(0, datetime.timedelta(days = 1, minutes = 50))
103 self.cursor.execute("""
104 begin
105 :value := :value + to_dsinterval('8 05:15:00');
106 end;""",
107 value = var)
108 self.assertEqual(var.getvalue(),
109 datetime.timedelta(days = 9, hours = 6, minutes = 5))
110
111 def testCursorDescription(self):
112 "test cursor description is accurate"
113 self.cursor.execute("select * from TestIntervals")
114 self.assertEqual(self.cursor.description,
115 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
116 ('INTERVALCOL', cx_Oracle.INTERVAL, None, None, 2, 6, 0),
117 ('NULLABLECOL', cx_Oracle.INTERVAL, None, None, 2, 6, 1) ])
118
119 def testFetchAll(self):
120 "test that fetching all of the data returns the correct results"
121 self.cursor.execute("select * From TestIntervals order by IntCol")
122 self.assertEqual(self.cursor.fetchall(), self.rawData)
123 self.assertEqual(self.cursor.fetchall(), [])
124
125 def testFetchMany(self):
126 "test that fetching data in chunks returns the correct results"
127 self.cursor.execute("select * From TestIntervals order by IntCol")
128 self.assertEqual(self.cursor.fetchmany(3), self.rawData[0:3])
129 self.assertEqual(self.cursor.fetchmany(2), self.rawData[3:5])
130 self.assertEqual(self.cursor.fetchmany(4), self.rawData[5:9])
131 self.assertEqual(self.cursor.fetchmany(3), self.rawData[9:])
132 self.assertEqual(self.cursor.fetchmany(3), [])
133
134 def testFetchOne(self):
135 "test that fetching a single row returns the correct results"
136 self.cursor.execute("""
137 select *
138 from TestIntervals
139 where IntCol in (3, 4)
140 order by IntCol""")
141 self.assertEqual(self.cursor.fetchone(), self.dataByKey[3])
142 self.assertEqual(self.cursor.fetchone(), self.dataByKey[4])
143 self.assertEqual(self.cursor.fetchone(), None)
144
145 if __name__ == "__main__":
146 TestEnv.RunTestCases()
147
+0
-280
test/LobVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing LOB (CLOB and BLOB) variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import sys
15
16 class TestCase(TestEnv.BaseTestCase):
17
18 def __GetTempLobs(self, sid):
19 cursor = self.connection.cursor()
20 cursor.execute("""
21 select abstract_lobs
22 from v$temporary_lobs
23 where sid = :sid""", sid = sid)
24 row = cursor.fetchone()
25 if row is None:
26 return 0
27 return int(row[0])
28
29 def __PerformTest(self, lobType, inputType):
30 longString = ""
31 directType = getattr(cx_Oracle, lobType)
32 self.cursor.execute("truncate table Test%ss" % lobType)
33 for i in range(0, 11):
34 if i > 0:
35 char = chr(ord('A') + i - 1)
36 longString += char * 25000
37 elif inputType != directType:
38 continue
39 self.cursor.setinputsizes(longString = inputType)
40 if lobType == "BLOB" and sys.version_info[0] >= 3:
41 bindValue = longString.encode("ascii")
42 else:
43 bindValue = longString
44 self.cursor.execute("""
45 insert into Test%ss (
46 IntCol,
47 %sCol
48 ) values (
49 :integerValue,
50 :longString
51 )""" % (lobType, lobType),
52 integerValue = i,
53 longString = bindValue)
54 self.connection.commit()
55 self.cursor.execute("""
56 select *
57 from Test%ss
58 order by IntCol""" % lobType)
59 self.__ValidateQuery(self.cursor, lobType)
60
61 def __TestLobOperations(self, lobType):
62 self.cursor.execute("truncate table Test%ss" % lobType)
63 self.cursor.setinputsizes(longString = getattr(cx_Oracle, lobType))
64 longString = "X" * 75000
65 writeValue = "TEST"
66 if lobType == "BLOB" and sys.version_info[0] >= 3:
67 longString = longString.encode("ascii")
68 writeValue = writeValue.encode("ascii")
69 self.cursor.execute("""
70 insert into Test%ss (
71 IntCol,
72 %sCol
73 ) values (
74 :integerValue,
75 :longString
76 )""" % (lobType, lobType),
77 integerValue = 1,
78 longString = longString)
79 self.cursor.execute("""
80 select %sCol
81 from Test%ss
82 where IntCol = 1""" % (lobType, lobType))
83 lob, = self.cursor.fetchone()
84 self.assertEqual(lob.isopen(), False)
85 lob.open()
86 self.assertEqual(lob.isopen(), True)
87 lob.close()
88 self.assertEqual(lob.isopen(), False)
89 self.assertEqual(lob.size(), 75000)
90 lob.write(writeValue, 75001)
91 self.assertEqual(lob.size(), 75000 + len(writeValue))
92 self.assertEqual(lob.read(), longString + writeValue)
93 lob.write(writeValue, 1)
94 self.assertEqual(lob.read(), writeValue + longString[4:] + writeValue)
95 lob.trim(25000)
96 self.assertEqual(lob.size(), 25000)
97 lob.trim()
98 self.assertEqual(lob.size(), 0)
99
100 def __TestTemporaryLOB(self, lobType):
101 self.cursor.execute("truncate table Test%ss" % lobType)
102 value = "A test string value"
103 if lobType == "BLOB" and sys.version_info[0] >= 3:
104 value = value.encode("ascii")
105 lobTypeObj = getattr(cx_Oracle, lobType)
106 lob = self.connection.createlob(lobTypeObj)
107 lob.write(value)
108 self.cursor.execute("""
109 insert into Test%ss (IntCol, %sCol)
110 values (:intVal, :lobVal)""" % (lobType, lobType),
111 intVal = 1,
112 lobVal = lob)
113 self.cursor.execute("select %sCol from Test%ss" % (lobType, lobType))
114 lob, = self.cursor.fetchone()
115 self.assertEqual(lob.read(), value)
116
117 def __ValidateQuery(self, rows, lobType):
118 longString = ""
119 for row in rows:
120 integerValue, lob = row
121 if integerValue == 0:
122 self.assertEqual(lob.size(), 0)
123 expectedValue = ""
124 if lobType == "BLOB" and sys.version_info[0] >= 3:
125 expectedValue = expectedValue.encode("ascii")
126 self.assertEqual(lob.read(), expectedValue)
127 else:
128 char = chr(ord('A') + integerValue - 1)
129 prevChar = chr(ord('A') + integerValue - 2)
130 longString += char * 25000
131 if lobType == "BLOB" and sys.version_info[0] >= 3:
132 actualValue = longString.encode("ascii")
133 char = char.encode("ascii")
134 prevChar = prevChar.encode("ascii")
135 else:
136 actualValue = longString
137 self.assertEqual(lob.size(), len(actualValue))
138 self.assertEqual(lob.read(), actualValue)
139 if lobType == "CLOB":
140 self.assertEqual(str(lob), actualValue)
141 self.assertEqual(lob.read(len(actualValue)), char)
142 if integerValue > 1:
143 offset = (integerValue - 1) * 25000 - 4
144 string = prevChar * 5 + char * 5
145 self.assertEqual(lob.read(offset, 10), string)
146
147 def testBindLobValue(self):
148 "test binding a LOB value directly"
149 self.cursor.execute("truncate table TestCLOBs")
150 self.cursor.execute("insert into TestCLOBs values (1, 'Short value')")
151 self.cursor.execute("select ClobCol from TestCLOBs")
152 lob, = self.cursor.fetchone()
153 self.cursor.execute("insert into TestCLOBs values (2, :value)",
154 value = lob)
155
156 def testBLOBCursorDescription(self):
157 "test cursor description is accurate for BLOBs"
158 self.cursor.execute("select * from TestBLOBs")
159 self.assertEqual(self.cursor.description,
160 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
161 ('BLOBCOL', cx_Oracle.BLOB, None, None, None, None, 0) ])
162
163 def testBLOBsDirect(self):
164 "test binding and fetching BLOB data (directly)"
165 self.__PerformTest("BLOB", cx_Oracle.BLOB)
166
167 def testBLOBsIndirect(self):
168 "test binding and fetching BLOB data (indirectly)"
169 self.__PerformTest("BLOB", cx_Oracle.LONG_BINARY)
170
171 def testBLOBOperations(self):
172 "test operations on BLOBs"
173 self.__TestLobOperations("BLOB")
174
175 def testCLOBCursorDescription(self):
176 "test cursor description is accurate for CLOBs"
177 self.cursor.execute("select * from TestCLOBs")
178 self.assertEqual(self.cursor.description,
179 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
180 ('CLOBCOL', cx_Oracle.CLOB, None, None, None, None, 0) ])
181
182 def testCLOBsDirect(self):
183 "test binding and fetching CLOB data (directly)"
184 self.__PerformTest("CLOB", cx_Oracle.CLOB)
185
186 def testCLOBsIndirect(self):
187 "test binding and fetching CLOB data (indirectly)"
188 self.__PerformTest("CLOB", cx_Oracle.LONG_STRING)
189
190 def testCLOBOperations(self):
191 "test operations on CLOBs"
192 self.__TestLobOperations("CLOB")
193
194 def testCreateBlob(self):
195 "test creating a temporary BLOB"
196 self.__TestTemporaryLOB("BLOB")
197
198 def testCreateClob(self):
199 "test creating a temporary CLOB"
200 self.__TestTemporaryLOB("CLOB")
201
202 def testCreateNclob(self):
203 "test creating a temporary NCLOB"
204 self.__TestTemporaryLOB("NCLOB")
205
206 def testMultipleFetch(self):
207 "test retrieving data from a CLOB after multiple fetches"
208 self.cursor.arraysize = 1
209 self.cursor.execute("select * from TestCLOBS")
210 rows = self.cursor.fetchall()
211 self.__ValidateQuery(rows, "CLOB")
212
213 def testNCLOBCursorDescription(self):
214 "test cursor description is accurate for NCLOBs"
215 self.cursor.execute("select * from TestNCLOBs")
216 self.assertEqual(self.cursor.description,
217 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
218 ('NCLOBCOL', cx_Oracle.NCLOB, None, None, None, None, 0) ])
219
220 def testNCLOBsDirect(self):
221 "test binding and fetching NCLOB data (directly)"
222 self.__PerformTest("NCLOB", cx_Oracle.NCLOB)
223
224 def testNCLOBDifferentEncodings(self):
225 "test binding and fetching NCLOB data (different encodings)"
226 connection = cx_Oracle.connect(TestEnv.GetMainUser(),
227 TestEnv.GetMainPassword(), TestEnv.GetConnectString(),
228 encoding = "UTF-8", nencoding = "UTF-16")
229 value = u"\u03b4\u4e2a"
230 cursor = connection.cursor()
231 cursor.execute("truncate table TestNCLOBs")
232 cursor.setinputsizes(val = cx_Oracle.NCHAR)
233 cursor.execute("insert into TestNCLOBs values (1, :val)", val = value)
234 cursor.execute("select NCLOBCol from TestNCLOBs")
235 nclob, = cursor.fetchone()
236 cursor.setinputsizes(val = cx_Oracle.NCHAR)
237 cursor.execute("update TestNCLOBs set NCLOBCol = :val",
238 val = nclob.read() + value)
239 cursor.execute("select NCLOBCol from TestNCLOBs")
240 nclob, = cursor.fetchone()
241 self.assertEqual(nclob.read(), value + value)
242
243 def testNCLOBsIndirect(self):
244 "test binding and fetching NCLOB data (indirectly)"
245 self.__PerformTest("NCLOB", cx_Oracle.LONG_STRING)
246
247 def testNCLOBOperations(self):
248 "test operations on NCLOBs"
249 self.__TestLobOperations("NCLOB")
250
251 def testTemporaryLobs(self):
252 "test temporary LOBs"
253 cursor = self.connection.cursor()
254 cursor.arraysize = self.cursor.arraysize
255 cursor.execute("""
256 select sid
257 from v$session
258 where audsid = userenv('sessionid')""")
259 sid, = cursor.fetchone()
260 tempLobs = self.__GetTempLobs(sid)
261 self.assertEqual(tempLobs, 0)
262 cursor.execute("""
263 select extract(xmlcol, '/').getclobval()
264 from TestXML""")
265 for lob, in cursor:
266 value = lob.read()
267 del lob
268 cursor.close()
269 tempLobs = self.__GetTempLobs(sid)
270 self.assertEqual(tempLobs, 0)
271
272 def testAssignStringBeyondArraySize(self):
273 "test assign string to NCLOB beyond array size"
274 nclobVar = self.cursor.var(cx_Oracle.NCLOB)
275 self.assertRaises(IndexError, nclobVar.setvalue, 1, "test char")
276
277 if __name__ == "__main__":
278 TestEnv.RunTestCases()
279
+0
-106
test/LongVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing long and long raw variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import sys
15
16 class TestCase(TestEnv.BaseTestCase):
17
18 def __PerformTest(self, a_Type, a_InputType):
19 self.cursor.execute("truncate table Test%ss" % a_Type)
20 longString = ""
21 for i in range(1, 11):
22 char = chr(ord('A') + i - 1)
23 longString += char * 25000
24 self.cursor.setinputsizes(longString = a_InputType)
25 if a_Type == "LongRaw" and sys.version_info[0] >= 3:
26 bindValue = longString.encode("ascii")
27 else:
28 bindValue = longString
29 self.cursor.execute("""
30 insert into Test%ss (
31 IntCol,
32 %sCol
33 ) values (
34 :integerValue,
35 :longString
36 )""" % (a_Type, a_Type),
37 integerValue = i,
38 longString = bindValue)
39 self.connection.commit()
40 self.cursor.execute("""
41 select *
42 from Test%ss
43 order by IntCol""" % a_Type)
44 longString = ""
45 while 1:
46 row = self.cursor.fetchone()
47 if row is None:
48 break
49 integerValue, fetchedValue = row
50 char = chr(ord('A') + integerValue - 1)
51 longString += char * 25000
52 if a_Type == "LongRaw" and sys.version_info[0] >= 3:
53 actualValue = longString.encode("ascii")
54 else:
55 actualValue = longString
56 self.assertEqual(len(fetchedValue), integerValue * 25000)
57 self.assertEqual(fetchedValue, actualValue)
58
59 def testLongs(self):
60 "test binding and fetching long data"
61 self.__PerformTest("Long", cx_Oracle.LONG_STRING)
62
63 def testLongWithExecuteMany(self):
64 "test binding long data with executemany()"
65 data = []
66 self.cursor.execute("truncate table TestLongs")
67 for i in range(5):
68 char = chr(ord('A') + i)
69 longStr = char * (32768 * (i + 1))
70 data.append((i + 1, longStr))
71 self.cursor.executemany("insert into TestLongs values (:1, :2)", data)
72 self.connection.commit()
73 self.cursor.execute("select * from TestLongs order by IntCol")
74 fetchedData = self.cursor.fetchall()
75 self.assertEqual(fetchedData, data)
76
77 def testLongRaws(self):
78 "test binding and fetching long raw data"
79 self.__PerformTest("LongRaw", cx_Oracle.LONG_BINARY)
80
81 def testLongCursorDescription(self):
82 "test cursor description is accurate for longs"
83 self.cursor.execute("select * from TestLongs")
84 self.assertEqual(self.cursor.description,
85 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
86 ('LONGCOL', cx_Oracle.LONG_STRING, None, None, None, None,
87 0) ])
88
89 def testLongRawCursorDescription(self):
90 "test cursor description is accurate for long raws"
91 self.cursor.execute("select * from TestLongRaws")
92 self.assertEqual(self.cursor.description,
93 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
94 ('LONGRAWCOL', cx_Oracle.LONG_BINARY, None, None, None, None,
95 0) ])
96
97 def testArraySizeTooLarge(self):
98 "test array size too large generates an exception"
99 self.cursor.arraysize = 268435456
100 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.execute,
101 "select * from TestLongRaws")
102
103 if __name__ == "__main__":
104 TestEnv.RunTestCases()
105
+0
-44
test/Module.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """Module for testing module methods."""
5
6 import TestEnv
7
8 import cx_Oracle
9 import datetime
10 import time
11
12 class TestCase(TestEnv.BaseTestCase):
13
14 def testDateFromTicks(self):
15 "test DateFromTicks()"
16 today = datetime.datetime.today()
17 timestamp = time.mktime(today.timetuple())
18 date = cx_Oracle.DateFromTicks(timestamp)
19 self.assertEqual(date, today.date())
20
21 def testFutureObj(self):
22 "test management of __future__ object"
23 self.assertEqual(cx_Oracle.__future__.dummy, None)
24 cx_Oracle.__future__.dummy = "Unimportant"
25 self.assertEqual(cx_Oracle.__future__.dummy, None)
26
27 def testTimestampFromTicks(self):
28 "test TimestampFromTicks()"
29 timestamp = time.mktime(datetime.datetime.today().timetuple())
30 today = datetime.datetime.fromtimestamp(timestamp)
31 date = cx_Oracle.TimestampFromTicks(timestamp)
32 self.assertEqual(date, today)
33
34 def testUnsupportedFunctions(self):
35 "test unsupported time functions"
36 self.assertRaises(cx_Oracle.NotSupportedError, cx_Oracle.Time,
37 12, 0, 0)
38 self.assertRaises(cx_Oracle.NotSupportedError, cx_Oracle.TimeFromTicks,
39 100)
40
41 if __name__ == "__main__":
42 TestEnv.RunTestCases()
43
+0
-243
test/NCharVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing NCHAR variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14
15 class TestCase(TestEnv.BaseTestCase):
16
17 def setUp(self):
18 TestEnv.BaseTestCase.setUp(self)
19 self.rawData = []
20 self.dataByKey = {}
21 for i in range(1, 11):
22 unicodeCol = u"Unicode \u3042 %d" % i
23 fixedCharCol = (u"Fixed Unicode %d" % i).ljust(40)
24 if i % 2:
25 nullableCol = u"Nullable %d" % i
26 else:
27 nullableCol = None
28 dataTuple = (i, unicodeCol, fixedCharCol, nullableCol)
29 self.rawData.append(dataTuple)
30 self.dataByKey[i] = dataTuple
31
32 def testUnicodeLength(self):
33 "test value length"
34 returnValue = self.cursor.var(int)
35 self.cursor.execute("""
36 begin
37 :retval := LENGTH(:value);
38 end;""",
39 value = u"InVal \u3042",
40 retval = returnValue)
41 self.assertEqual(returnValue.getvalue(), 7)
42
43 def testBindUnicode(self):
44 "test binding in a unicode"
45 self.cursor.setinputsizes(value = cx_Oracle.NCHAR)
46 self.cursor.execute("""
47 select * from TestUnicodes
48 where UnicodeCol = :value""",
49 value = u"Unicode \u3042 5")
50 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[5]])
51
52 def testBindDifferentVar(self):
53 "test binding a different variable on second execution"
54 retval_1 = self.cursor.var(cx_Oracle.NCHAR, 30)
55 retval_2 = self.cursor.var(cx_Oracle.NCHAR, 30)
56 self.cursor.execute(r"begin :retval := unistr('Called \3042'); end;",
57 retval = retval_1)
58 self.assertEqual(retval_1.getvalue(), u"Called \u3042")
59 self.cursor.execute("begin :retval := 'Called'; end;",
60 retval = retval_2)
61 self.assertEqual(retval_2.getvalue(), "Called")
62
63 def testBindUnicodeAfterNumber(self):
64 "test binding in a unicode after setting input sizes to a number"
65 unicodeVal = self.cursor.var(cx_Oracle.NCHAR)
66 unicodeVal.setvalue(0, u"Unicode \u3042 6")
67 self.cursor.setinputsizes(value = cx_Oracle.NUMBER)
68 self.cursor.execute("""
69 select * from TestUnicodes
70 where UnicodeCol = :value""",
71 value = unicodeVal)
72 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[6]])
73
74 def testBindUnicodeArrayDirect(self):
75 "test binding in a unicode array"
76 returnValue = self.cursor.var(cx_Oracle.NUMBER)
77 array = [r[1] for r in self.rawData]
78 arrayVar = self.cursor.arrayvar(cx_Oracle.NCHAR, array)
79 statement = """
80 begin
81 :retval := pkg_TestUnicodeArrays.TestInArrays(
82 :integerValue, :array);
83 end;"""
84 self.cursor.execute(statement,
85 retval = returnValue,
86 integerValue = 5,
87 array = arrayVar)
88 self.assertEqual(returnValue.getvalue(), 116)
89 array = [ u"Unicode - \u3042 %d" % i for i in range(15) ]
90 arrayVar = self.cursor.arrayvar(cx_Oracle.NCHAR, array)
91 self.cursor.execute(statement,
92 integerValue = 8,
93 array = arrayVar)
94 self.assertEqual(returnValue.getvalue(), 208)
95
96 def testBindUnicodeArrayBySizes(self):
97 "test binding in a unicode array (with setinputsizes)"
98 returnValue = self.cursor.var(cx_Oracle.NUMBER)
99 self.cursor.setinputsizes(array = [cx_Oracle.NCHAR, 10])
100 array = [r[1] for r in self.rawData]
101 self.cursor.execute("""
102 begin
103 :retval := pkg_TestUnicodeArrays.TestInArrays(:integerValue,
104 :array);
105 end;""",
106 retval = returnValue,
107 integerValue = 6,
108 array = array)
109 self.assertEqual(returnValue.getvalue(), 117)
110
111 def testBindUnicodeArrayByVar(self):
112 "test binding in a unicode array (with arrayvar)"
113 returnValue = self.cursor.var(cx_Oracle.NUMBER)
114 array = self.cursor.arrayvar(cx_Oracle.NCHAR, 10, 20)
115 array.setvalue(0, [r[1] for r in self.rawData])
116 self.cursor.execute("""
117 begin
118 :retval := pkg_TestUnicodeArrays.TestInArrays(:integerValue,
119 :array);
120 end;""",
121 retval = returnValue,
122 integerValue = 7,
123 array = array)
124 self.assertEqual(returnValue.getvalue(), 118)
125
126 def testBindInOutUnicodeArrayByVar(self):
127 "test binding in/out a unicode array (with arrayvar)"
128 array = self.cursor.arrayvar(cx_Oracle.NCHAR, 10, 100)
129 originalData = [r[1] for r in self.rawData]
130 format = u"Converted element \u3042 # %d originally had length %d"
131 expectedData = [format % (i, len(originalData[i - 1])) \
132 for i in range(1, 6)] + originalData[5:]
133 array.setvalue(0, originalData)
134 self.cursor.execute("""
135 begin
136 pkg_TestUnicodeArrays.TestInOutArrays(:numElems, :array);
137 end;""",
138 numElems = 5,
139 array = array)
140 self.assertEqual(array.getvalue(), expectedData)
141
142 def testBindOutUnicodeArrayByVar(self):
143 "test binding out a unicode array (with arrayvar)"
144 array = self.cursor.arrayvar(cx_Oracle.NCHAR, 6, 100)
145 format = u"Test out element \u3042 # %d"
146 expectedData = [format % i for i in range(1, 7)]
147 self.cursor.execute("""
148 begin
149 pkg_TestUnicodeArrays.TestOutArrays(:numElems, :array);
150 end;""",
151 numElems = 6,
152 array = array)
153 self.assertEqual(array.getvalue(), expectedData)
154
155 def testBindNull(self):
156 "test binding in a null"
157 self.cursor.execute("""
158 select * from TestUnicodes
159 where UnicodeCol = :value""",
160 value = None)
161 self.assertEqual(self.cursor.fetchall(), [])
162
163 def testBindOutSetInputSizesByType(self):
164 "test binding out with set input sizes defined (by type)"
165 vars = self.cursor.setinputsizes(value = cx_Oracle.NCHAR)
166 self.cursor.execute(r"""
167 begin
168 :value := unistr('TSI \3042');
169 end;""")
170 self.assertEqual(vars["value"].getvalue(), u"TSI \u3042")
171
172 def testBindInOutSetInputSizesByType(self):
173 "test binding in/out with set input sizes defined (by type)"
174 vars = self.cursor.setinputsizes(value = cx_Oracle.NCHAR)
175 self.cursor.execute(r"""
176 begin
177 :value := :value || unistr(' TSI \3042');
178 end;""",
179 value = u"InVal \u3041")
180 self.assertEqual(vars["value"].getvalue(),
181 u"InVal \u3041 TSI \u3042")
182
183 def testBindOutVar(self):
184 "test binding out with cursor.var() method"
185 var = self.cursor.var(cx_Oracle.NCHAR)
186 self.cursor.execute(r"""
187 begin
188 :value := unistr('TSI (VAR) \3042');
189 end;""",
190 value = var)
191 self.assertEqual(var.getvalue(), u"TSI (VAR) \u3042")
192
193 def testBindInOutVarDirectSet(self):
194 "test binding in/out with cursor.var() method"
195 var = self.cursor.var(cx_Oracle.NCHAR)
196 var.setvalue(0, u"InVal \u3041")
197 self.cursor.execute(r"""
198 begin
199 :value := :value || unistr(' TSI (VAR) \3042');
200 end;""",
201 value = var)
202 self.assertEqual(var.getvalue(), u"InVal \u3041 TSI (VAR) \u3042")
203
204 def testCursorDescription(self):
205 "test cursor description is accurate"
206 self.cursor.execute("select * from TestUnicodes")
207 self.assertEqual(self.cursor.description,
208 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
209 ('UNICODECOL', cx_Oracle.NCHAR, 20, 80, None, None, 0),
210 ('FIXEDUNICODECOL', cx_Oracle.FIXED_NCHAR, 40, 160, None,
211 None, 0),
212 ('NULLABLECOL', cx_Oracle.NCHAR, 50, 200, None, None, 1) ])
213
214 def testFetchAll(self):
215 "test that fetching all of the data returns the correct results"
216 self.cursor.execute("select * From TestUnicodes order by IntCol")
217 self.assertEqual(self.cursor.fetchall(), self.rawData)
218 self.assertEqual(self.cursor.fetchall(), [])
219
220 def testFetchMany(self):
221 "test that fetching data in chunks returns the correct results"
222 self.cursor.execute("select * From TestUnicodes order by IntCol")
223 self.assertEqual(self.cursor.fetchmany(3), self.rawData[0:3])
224 self.assertEqual(self.cursor.fetchmany(2), self.rawData[3:5])
225 self.assertEqual(self.cursor.fetchmany(4), self.rawData[5:9])
226 self.assertEqual(self.cursor.fetchmany(3), self.rawData[9:])
227 self.assertEqual(self.cursor.fetchmany(3), [])
228
229 def testFetchOne(self):
230 "test that fetching a single row returns the correct results"
231 self.cursor.execute("""
232 select *
233 from TestUnicodes
234 where IntCol in (3, 4)
235 order by IntCol""")
236 self.assertEqual(self.cursor.fetchone(), self.dataByKey[3])
237 self.assertEqual(self.cursor.fetchone(), self.dataByKey[4])
238 self.assertEqual(self.cursor.fetchone(), None)
239
240 if __name__ == "__main__":
241 TestEnv.RunTestCases()
242
+0
-390
test/NumberVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing number variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import decimal
15 import sys
16
17 class TestCase(TestEnv.BaseTestCase):
18
19 def outputTypeHandlerNativeInt(self, cursor, name, defaultType, size,
20 precision, scale):
21 return cursor.var(cx_Oracle.NATIVE_INT, arraysize=cursor.arraysize)
22
23 def outputTypeHandlerDecimal(self, cursor, name, defaultType, size,
24 precision, scale):
25 if defaultType == cx_Oracle.NUMBER:
26 return cursor.var(str, 255, outconverter = decimal.Decimal,
27 arraysize = cursor.arraysize)
28
29 def setUp(self):
30 TestEnv.BaseTestCase.setUp(self)
31 self.rawData = []
32 self.dataByKey = {}
33 for i in range(1, 11):
34 numberCol = i + i * 0.25
35 floatCol = i + i * 0.75
36 unconstrainedCol = i ** 3 + i * 0.5
37 if i % 2:
38 nullableCol = 143 ** i
39 else:
40 nullableCol = None
41 dataTuple = (i, 38 ** i, numberCol, floatCol,
42 unconstrainedCol, nullableCol)
43 self.rawData.append(dataTuple)
44 self.dataByKey[i] = dataTuple
45
46 def testBindBoolean(self):
47 "test binding in a boolean"
48 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
49 (True,))
50 self.assertEqual(result, "TRUE")
51
52 def testBindDecimal(self):
53 "test binding in a decimal.Decimal"
54 self.cursor.execute("""
55 select * from TestNumbers
56 where NumberCol - :value1 - :value2 = trunc(NumberCol)""",
57 value1 = decimal.Decimal("0.20"),
58 value2 = decimal.Decimal("0.05"))
59 self.assertEqual(self.cursor.fetchall(),
60 [self.dataByKey[1], self.dataByKey[5], self.dataByKey[9]])
61
62 def testBindFloat(self):
63 "test binding in a float"
64 self.cursor.execute("""
65 select * from TestNumbers
66 where NumberCol - :value = trunc(NumberCol)""",
67 value = 0.25)
68 self.assertEqual(self.cursor.fetchall(),
69 [self.dataByKey[1], self.dataByKey[5], self.dataByKey[9]])
70
71 def testBindInteger(self):
72 "test binding in an integer"
73 self.cursor.execute("""
74 select * from TestNumbers
75 where IntCol = :value""",
76 value = 2)
77 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[2]])
78
79 def testBindLargeLongAsOracleNumber(self):
80 "test binding in a large long integer as Oracle number"
81 valueVar = self.cursor.var(cx_Oracle.NUMBER)
82 valueVar.setvalue(0, 6088343244)
83 self.cursor.execute("""
84 begin
85 :value := :value + 5;
86 end;""",
87 value = valueVar)
88 value = valueVar.getvalue()
89 self.assertEqual(value, 6088343249)
90
91 def testBindLargeLongAsInteger(self):
92 "test binding in a large long integer as Python integer"
93 longValue = -9999999999999999999
94 self.cursor.execute("select :value from dual", value = longValue)
95 result, = self.cursor.fetchone()
96 self.assertEqual(result, longValue)
97
98 def testBindIntegerAfterString(self):
99 "test binding in an number after setting input sizes to a string"
100 self.cursor.setinputsizes(value = 15)
101 self.cursor.execute("""
102 select * from TestNumbers
103 where IntCol = :value""",
104 value = 3)
105 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[3]])
106
107 def testBindDecimalAfterNumber(self):
108 "test binding in a decimal value after setting input sizes to a number"
109 cursor = self.connection.cursor()
110 value = decimal.Decimal("319438950232418390.273596")
111 cursor.setinputsizes(value = cx_Oracle.NUMBER)
112 cursor.outputtypehandler = self.outputTypeHandlerDecimal
113 cursor.execute("select :value from dual",
114 value = value)
115 outValue, = cursor.fetchone()
116 self.assertEqual(outValue, value)
117
118 def testBindNull(self):
119 "test binding in a null"
120 self.cursor.execute("""
121 select * from TestNumbers
122 where IntCol = :value""",
123 value = None)
124 self.assertEqual(self.cursor.fetchall(), [])
125
126 def testBindNumberArrayDirect(self):
127 "test binding in a number array"
128 returnValue = self.cursor.var(cx_Oracle.NUMBER)
129 array = [r[2] for r in self.rawData]
130 statement = """
131 begin
132 :returnValue := pkg_TestNumberArrays.TestInArrays(
133 :startValue, :array);
134 end;"""
135 self.cursor.execute(statement,
136 returnValue = returnValue,
137 startValue = 5,
138 array = array)
139 self.assertEqual(returnValue.getvalue(), 73.75)
140 array = list(range(15))
141 self.cursor.execute(statement,
142 startValue = 10,
143 array = array)
144 self.assertEqual(returnValue.getvalue(), 115.0)
145
146 def testBindNumberArrayBySizes(self):
147 "test binding in a number array (with setinputsizes)"
148 returnValue = self.cursor.var(cx_Oracle.NUMBER)
149 self.cursor.setinputsizes(array = [cx_Oracle.NUMBER, 10])
150 array = [r[2] for r in self.rawData]
151 self.cursor.execute("""
152 begin
153 :returnValue := pkg_TestNumberArrays.TestInArrays(
154 :startValue, :array);
155 end;""",
156 returnValue = returnValue,
157 startValue = 6,
158 array = array)
159 self.assertEqual(returnValue.getvalue(), 74.75)
160
161 def testBindNumberArrayByVar(self):
162 "test binding in a number array (with arrayvar)"
163 returnValue = self.cursor.var(cx_Oracle.NUMBER)
164 array = self.cursor.arrayvar(cx_Oracle.NUMBER,
165 [r[2] for r in self.rawData])
166 self.cursor.execute("""
167 begin
168 :returnValue := pkg_TestNumberArrays.TestInArrays(
169 :integerValue, :array);
170 end;""",
171 returnValue = returnValue,
172 integerValue = 7,
173 array = array)
174 self.assertEqual(returnValue.getvalue(), 75.75)
175
176 def testBindZeroLengthNumberArrayByVar(self):
177 "test binding in a zero length number array (with arrayvar)"
178 returnValue = self.cursor.var(cx_Oracle.NUMBER)
179 array = self.cursor.arrayvar(cx_Oracle.NUMBER, 0)
180 self.cursor.execute("""
181 begin
182 :returnValue := pkg_TestNumberArrays.TestInArrays(
183 :integerValue, :array);
184 end;""",
185 returnValue = returnValue,
186 integerValue = 8,
187 array = array)
188 self.assertEqual(returnValue.getvalue(), 8.0)
189 self.assertEqual(array.getvalue(), [])
190
191 def testBindInOutNumberArrayByVar(self):
192 "test binding in/out a number array (with arrayvar)"
193 array = self.cursor.arrayvar(cx_Oracle.NUMBER, 10)
194 originalData = [r[2] for r in self.rawData]
195 expectedData = [originalData[i - 1] * 10 for i in range(1, 6)] + \
196 originalData[5:]
197 array.setvalue(0, originalData)
198 self.cursor.execute("""
199 begin
200 pkg_TestNumberArrays.TestInOutArrays(:numElems, :array);
201 end;""",
202 numElems = 5,
203 array = array)
204 self.assertEqual(array.getvalue(), expectedData)
205
206 def testBindOutNumberArrayByVar(self):
207 "test binding out a Number array (with arrayvar)"
208 array = self.cursor.arrayvar(cx_Oracle.NUMBER, 6)
209 expectedData = [i * 100 for i in range(1, 7)]
210 self.cursor.execute("""
211 begin
212 pkg_TestNumberArrays.TestOutArrays(:numElems, :array);
213 end;""",
214 numElems = 6,
215 array = array)
216 self.assertEqual(array.getvalue(), expectedData)
217
218 def testBindOutSetInputSizes(self):
219 "test binding out with set input sizes defined"
220 vars = self.cursor.setinputsizes(value = cx_Oracle.NUMBER)
221 self.cursor.execute("""
222 begin
223 :value := 5;
224 end;""")
225 self.assertEqual(vars["value"].getvalue(), 5)
226
227 def testBindInOutSetInputSizes(self):
228 "test binding in/out with set input sizes defined"
229 vars = self.cursor.setinputsizes(value = cx_Oracle.NUMBER)
230 self.cursor.execute("""
231 begin
232 :value := :value + 5;
233 end;""",
234 value = 1.25)
235 self.assertEqual(vars["value"].getvalue(), 6.25)
236
237 def testBindOutVar(self):
238 "test binding out with cursor.var() method"
239 var = self.cursor.var(cx_Oracle.NUMBER)
240 self.cursor.execute("""
241 begin
242 :value := 5;
243 end;""",
244 value = var)
245 self.assertEqual(var.getvalue(), 5)
246
247 def testBindInOutVarDirectSet(self):
248 "test binding in/out with cursor.var() method"
249 var = self.cursor.var(cx_Oracle.NUMBER)
250 var.setvalue(0, 2.25)
251 self.cursor.execute("""
252 begin
253 :value := :value + 5;
254 end;""",
255 value = var)
256 self.assertEqual(var.getvalue(), 7.25)
257
258 def testCursorDescription(self):
259 "test cursor description is accurate"
260 self.cursor.execute("select * from TestNumbers")
261 self.assertEqual(self.cursor.description,
262 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
263 ('LONGINTCOL', cx_Oracle.NUMBER, 17, None, 16, 0, 0),
264 ('NUMBERCOL', cx_Oracle.NUMBER, 13, None, 9, 2, 0),
265 ('FLOATCOL', cx_Oracle.NUMBER, 127, None, 126, -127, 0),
266 ('UNCONSTRAINEDCOL', cx_Oracle.NUMBER, 127, None, 0, -127, 0),
267 ('NULLABLECOL', cx_Oracle.NUMBER, 39, None, 38, 0, 1) ])
268
269 def testFetchAll(self):
270 "test that fetching all of the data returns the correct results"
271 self.cursor.execute("select * From TestNumbers order by IntCol")
272 self.assertEqual(self.cursor.fetchall(), self.rawData)
273 self.assertEqual(self.cursor.fetchall(), [])
274
275 def testFetchMany(self):
276 "test that fetching data in chunks returns the correct results"
277 self.cursor.execute("select * From TestNumbers order by IntCol")
278 self.assertEqual(self.cursor.fetchmany(3), self.rawData[0:3])
279 self.assertEqual(self.cursor.fetchmany(2), self.rawData[3:5])
280 self.assertEqual(self.cursor.fetchmany(4), self.rawData[5:9])
281 self.assertEqual(self.cursor.fetchmany(3), self.rawData[9:])
282 self.assertEqual(self.cursor.fetchmany(3), [])
283
284 def testFetchOne(self):
285 "test that fetching a single row returns the correct results"
286 self.cursor.execute("""
287 select *
288 from TestNumbers
289 where IntCol in (3, 4)
290 order by IntCol""")
291 self.assertEqual(self.cursor.fetchone(), self.dataByKey[3])
292 self.assertEqual(self.cursor.fetchone(), self.dataByKey[4])
293 self.assertEqual(self.cursor.fetchone(), None)
294
295 def testReturnAsLong(self):
296 "test that fetching a long integer returns such in Python"
297 self.cursor.execute("""
298 select NullableCol
299 from TestNumbers
300 where IntCol = 9""")
301 col, = self.cursor.fetchone()
302 self.assertEqual(col, 25004854810776297743)
303
304 def testReturnConstantFloat(self):
305 "test that fetching a floating point number returns such in Python"
306 self.cursor.execute("select 1.25 from dual")
307 result, = self.cursor.fetchone()
308 self.assertEqual(result, 1.25)
309
310 def testReturnConstantInteger(self):
311 "test that fetching an integer returns such in Python"
312 self.cursor.execute("select 148 from dual")
313 result, = self.cursor.fetchone()
314 self.assertEqual(result, 148)
315 self.assertTrue(isinstance(result, int), "integer not returned")
316
317 def testAcceptableBoundaryNumbers(self):
318 "test that acceptable boundary numbers are handled properly"
319 inValues = [decimal.Decimal("9.99999999999999e+125"),
320 decimal.Decimal("-9.99999999999999e+125"), 0.0, 1e-130,
321 -1e-130]
322 outValues = [int("9" * 15 + "0" * 111), -int("9" * 15 + "0" * 111),
323 0, decimal.Decimal("1e-130"), decimal.Decimal("-1e-130")]
324 for inValue, outValue in zip(inValues, outValues):
325 self.cursor.execute("select :1 from dual", (inValue,))
326 result, = self.cursor.fetchone()
327 self.assertEqual(result, outValue)
328
329 def testUnacceptableBoundaryNumbers(self):
330 "test that unacceptable boundary numbers are rejected"
331 inValues = [1e126, -1e126, float("inf"), float("-inf"),
332 float("NaN"), decimal.Decimal("1e126"),
333 decimal.Decimal("-1e126"), decimal.Decimal("inf"),
334 decimal.Decimal("-inf"), decimal.Decimal("NaN")]
335 noRepErr = "DPI-1044: value cannot be represented as an Oracle number"
336 invalidErr = ""
337 expectedErrors = [noRepErr, noRepErr, invalidErr, invalidErr,
338 invalidErr, noRepErr, noRepErr, invalidErr, invalidErr,
339 invalidErr]
340 for inValue, error in zip(inValues, expectedErrors):
341 method = self.assertRaisesRegex if sys.version_info[0] == 3 \
342 else self.assertRaisesRegexp
343 method(cx_Oracle.DatabaseError, error, self.cursor.execute,
344 "select :1 from dual", (inValue,))
345
346 def testReturnFloatFromDivision(self):
347 "test that fetching the result of division returns a float"
348 self.cursor.execute("""
349 select IntCol / 7
350 from TestNumbers
351 where IntCol = 1""")
352 result, = self.cursor.fetchone()
353 self.assertAlmostEqual(result,
354 decimal.Decimal("1") / decimal.Decimal("7"))
355
356 def testStringFormat(self):
357 "test that string format is returned properly"
358 var = self.cursor.var(cx_Oracle.NUMBER)
359 self.assertEqual(str(var), "<cx_Oracle.NUMBER with value None>")
360 var.setvalue(0, 4)
361 self.assertEqual(str(var), "<cx_Oracle.NUMBER with value 4.0>")
362
363 def testBindNativeFloat(self):
364 "test that binding native float is possible"
365 self.cursor.setinputsizes(cx_Oracle.NATIVE_FLOAT)
366 self.cursor.execute("select :1 from dual", (5,))
367 self.assertEqual(type(self.cursor.bindvars[0]), cx_Oracle.NATIVE_FLOAT)
368 value, = self.cursor.fetchone()
369 self.assertEqual(value, 5)
370 self.cursor.execute("select :1 from dual", (1.5,))
371 self.assertEqual(type(self.cursor.bindvars[0]), cx_Oracle.NATIVE_FLOAT)
372 value, = self.cursor.fetchone()
373 self.assertEqual(value, 1.5)
374 self.cursor.execute("select :1 from dual", (decimal.Decimal("NaN"),))
375 self.assertEqual(type(self.cursor.bindvars[0]), cx_Oracle.NATIVE_FLOAT)
376 value, = self.cursor.fetchone()
377 self.assertEqual(str(value), str(float("NaN")))
378
379 def testFetchNativeInt(self):
380 "test fetching numbers as native integers"
381 self.cursor.outputtypehandler = self.outputTypeHandlerNativeInt
382 for value in (1, 2 ** 31, 2 ** 63 - 1, -1, -2 ** 31, -2 ** 63 + 1):
383 self.cursor.execute("select :1 from dual", [str(value)])
384 fetchedValue, = self.cursor.fetchone()
385 self.assertEqual(value, fetchedValue)
386
387 if __name__ == "__main__":
388 TestEnv.RunTestCases()
389
+0
-334
test/ObjectVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing object variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import datetime
15 import decimal
16
17 class TestCase(TestEnv.BaseTestCase):
18
19 def __GetObjectAsTuple(self, obj):
20 if obj.type.iscollection:
21 value = []
22 for v in obj.aslist():
23 if isinstance(v, cx_Oracle.Object):
24 v = self.__GetObjectAsTuple(v)
25 elif isinstance(value, cx_Oracle.LOB):
26 v = v.read()
27 value.append(v)
28 return value
29 attributeValues = []
30 for attribute in obj.type.attributes:
31 value = getattr(obj, attribute.name)
32 if isinstance(value, cx_Oracle.Object):
33 value = self.__GetObjectAsTuple(value)
34 elif isinstance(value, cx_Oracle.LOB):
35 value = value.read()
36 attributeValues.append(value)
37 return tuple(attributeValues)
38
39 def __TestData(self, expectedIntValue, expectedObjectValue,
40 expectedArrayValue):
41 intValue, objectValue, arrayValue = self.cursor.fetchone()
42 if objectValue is not None:
43 objectValue = self.__GetObjectAsTuple(objectValue)
44 if arrayValue is not None:
45 arrayValue = arrayValue.aslist()
46 self.assertEqual(intValue, expectedIntValue)
47 self.assertEqual(objectValue, expectedObjectValue)
48 self.assertEqual(arrayValue, expectedArrayValue)
49
50 def testBindNullIn(self):
51 "test binding a null value (IN)"
52 var = self.cursor.var(cx_Oracle.OBJECT, typename = "UDT_OBJECT")
53 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
54 (var,))
55 self.assertEqual(result, "null")
56
57 def testBindObjectIn(self):
58 "test binding an object (IN)"
59 typeObj = self.connection.gettype("UDT_OBJECT")
60 obj = typeObj.newobject()
61 obj.NUMBERVALUE = 13
62 obj.STRINGVALUE = "Test String"
63 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
64 (obj,))
65 self.assertEqual(result,
66 "udt_Object(13, 'Test String', null, null, null, null, null)")
67 obj.NUMBERVALUE = None
68 obj.STRINGVALUE = "Test With Dates"
69 obj.DATEVALUE = datetime.datetime(2016, 2, 10)
70 obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 10, 14, 13, 50)
71 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
72 (obj,))
73 self.assertEqual(result,
74 "udt_Object(null, 'Test With Dates', null, " \
75 "to_date('2016-02-10', 'YYYY-MM-DD'), " \
76 "to_timestamp('2016-02-10 14:13:50', " \
77 "'YYYY-MM-DD HH24:MI:SS'), " \
78 "null, null)")
79 obj.DATEVALUE = None
80 obj.TIMESTAMPVALUE = None
81 subTypeObj = self.connection.gettype("UDT_SUBOBJECT")
82 subObj = subTypeObj.newobject()
83 subObj.SUBNUMBERVALUE = decimal.Decimal("18.25")
84 subObj.SUBSTRINGVALUE = "Sub String"
85 obj.SUBOBJECTVALUE = subObj
86 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
87 (obj,))
88 self.assertEqual(result,
89 "udt_Object(null, 'Test With Dates', null, null, null, " \
90 "udt_SubObject(18.25, 'Sub String'), null)")
91
92 def testCopyObject(self):
93 "test copying an object"
94 typeObj = self.connection.gettype("UDT_OBJECT")
95 obj = typeObj()
96 obj.NUMBERVALUE = 5124
97 obj.STRINGVALUE = "A test string"
98 obj.DATEVALUE = datetime.datetime(2016, 2, 24)
99 obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 24, 13, 39, 10)
100 copiedObj = obj.copy()
101 self.assertEqual(obj.NUMBERVALUE, copiedObj.NUMBERVALUE)
102 self.assertEqual(obj.STRINGVALUE, copiedObj.STRINGVALUE)
103 self.assertEqual(obj.DATEVALUE, copiedObj.DATEVALUE)
104 self.assertEqual(obj.TIMESTAMPVALUE, copiedObj.TIMESTAMPVALUE)
105
106 def testEmptyCollectionAsList(self):
107 "test getting an empty collection as a list"
108 typeName = "UDT_ARRAY"
109 typeObj = self.connection.gettype(typeName)
110 obj = typeObj.newobject()
111 self.assertEqual(obj.aslist(), [])
112
113 def testFetchData(self):
114 "test fetching objects"
115 self.cursor.execute("""
116 select
117 IntCol,
118 ObjectCol,
119 ArrayCol
120 from TestObjects
121 order by IntCol""")
122 self.assertEqual(self.cursor.description,
123 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
124 ('OBJECTCOL', cx_Oracle.OBJECT, None, None, None, None, 1),
125 ('ARRAYCOL', cx_Oracle.OBJECT, None, None, None, None, 1) ])
126 self.__TestData(1, (1, 'First row', 'First ', 'N First Row',
127 'N First ', b'Raw Data 1', 2, 5, 12.125, 0.5, 12.5, 25.25,
128 50.125, cx_Oracle.Timestamp(2007, 3, 6, 0, 0, 0),
129 cx_Oracle.Timestamp(2008, 9, 12, 16, 40),
130 cx_Oracle.Timestamp(2009, 10, 13, 17, 50),
131 cx_Oracle.Timestamp(2010, 11, 14, 18, 55),
132 'Short CLOB value', 'Short NCLOB Value', b'Short BLOB value',
133 (11, 'Sub object 1'),
134 [(5, 'first element'), (6, 'second element')]),
135 [5, 10, None, 20])
136 self.__TestData(2, None, [3, None, 9, 12, 15])
137 self.__TestData(3, (3, 'Third row', 'Third ', 'N Third Row',
138 'N Third ', b'Raw Data 3', 4, 10, 6.5, 0.75, 43.25, 86.5,
139 192.125, cx_Oracle.Timestamp(2007, 6, 21, 0, 0, 0),
140 cx_Oracle.Timestamp(2007, 12, 13, 7, 30, 45),
141 cx_Oracle.Timestamp(2017, 6, 21, 23, 18, 45),
142 cx_Oracle.Timestamp(2017, 7, 21, 8, 27, 13),
143 'Another short CLOB value', 'Another short NCLOB Value',
144 b'Yet another short BLOB value',
145 (13, 'Sub object 3'),
146 [(10, 'element #1'), (20, 'element #2'),
147 (30, 'element #3'), (40, 'element #4')]), None)
148
149 def testGetObjectType(self):
150 "test getting object type"
151 typeObj = self.connection.gettype("UDT_OBJECT")
152 self.assertEqual(typeObj.iscollection, False)
153 self.assertEqual(typeObj.schema, self.connection.username.upper())
154 self.assertEqual(typeObj.name, "UDT_OBJECT")
155 expectedAttributeNames = ["NUMBERVALUE", "STRINGVALUE",
156 "FIXEDCHARVALUE", "NSTRINGVALUE", "NFIXEDCHARVALUE",
157 "RAWVALUE", "INTVALUE", "SMALLINTVALUE", "REALVALUE",
158 "DOUBLEPRECISIONVALUE", "FLOATVALUE", "BINARYFLOATVALUE",
159 "BINARYDOUBLEVALUE", "DATEVALUE", "TIMESTAMPVALUE",
160 "TIMESTAMPTZVALUE", "TIMESTAMPLTZVALUE", "CLOBVALUE",
161 "NCLOBVALUE", "BLOBVALUE", "SUBOBJECTVALUE", "SUBOBJECTARRAY"]
162 actualAttributeNames = [a.name for a in typeObj.attributes]
163 self.assertEqual(actualAttributeNames, expectedAttributeNames)
164 typeObj = self.connection.gettype("UDT_OBJECTARRAY")
165 self.assertEqual(typeObj.iscollection, True)
166 self.assertEqual(typeObj.attributes, [])
167
168 def testObjectType(self):
169 "test object type data"
170 self.cursor.execute("""
171 select ObjectCol
172 from TestObjects
173 where ObjectCol is not null
174 and rownum <= 1""")
175 objValue, = self.cursor.fetchone()
176 self.assertEqual(objValue.type.schema,
177 self.connection.username.upper())
178 self.assertEqual(objValue.type.name, u"UDT_OBJECT")
179 self.assertEqual(objValue.type.attributes[0].name, "NUMBERVALUE")
180
181 def testRoundTripObject(self):
182 "test inserting and then querying object with all data types"
183 self.cursor.execute("truncate table TestClobs")
184 self.cursor.execute("truncate table TestNClobs")
185 self.cursor.execute("truncate table TestBlobs")
186 self.cursor.execute("insert into TestClobs values " \
187 "(1, 'A short CLOB')")
188 self.cursor.execute("insert into TestNClobs values " \
189 "(1, 'A short NCLOB')")
190 self.cursor.execute("insert into TestBlobs values " \
191 "(1, utl_raw.cast_to_raw('A short BLOB'))")
192 self.connection.commit()
193 self.cursor.execute("select CLOBCol from TestClobs")
194 clob, = self.cursor.fetchone()
195 self.cursor.execute("select NCLOBCol from TestNClobs")
196 nclob, = self.cursor.fetchone()
197 self.cursor.execute("select BLOBCol from TestBlobs")
198 blob, = self.cursor.fetchone()
199 typeObj = self.connection.gettype(u"UDT_OBJECT")
200 obj = typeObj.newobject()
201 obj.NUMBERVALUE = 5
202 obj.STRINGVALUE = "A string"
203 obj.FIXEDCHARVALUE = "Fixed str"
204 obj.NSTRINGVALUE = "A NCHAR string"
205 obj.NFIXEDCHARVALUE = "Fixed N"
206 obj.RAWVALUE = b"Raw Value"
207 obj.INTVALUE = 27
208 obj.SMALLINTVALUE = 13
209 obj.REALVALUE = 184.875
210 obj.DOUBLEPRECISIONVALUE = 1.375
211 obj.FLOATVALUE = 23.75
212 obj.DATEVALUE = datetime.date(2017, 5, 9)
213 obj.TIMESTAMPVALUE = datetime.datetime(2017, 5, 9, 9, 41, 13)
214 obj.TIMESTAMPTZVALUE = datetime.datetime(1986, 8, 2, 15, 27, 38)
215 obj.TIMESTAMPLTZVALUE = datetime.datetime(1999, 11, 12, 23, 5, 2)
216 obj.BINARYFLOATVALUE = 14.25
217 obj.BINARYDOUBLEVALUE = 29.1625
218 obj.CLOBVALUE = clob
219 obj.NCLOBVALUE = nclob
220 obj.BLOBVALUE = blob
221 subTypeObj = self.connection.gettype("UDT_SUBOBJECT")
222 subObj = subTypeObj.newobject()
223 subObj.SUBNUMBERVALUE = 23
224 subObj.SUBSTRINGVALUE = "Substring value"
225 obj.SUBOBJECTVALUE = subObj
226 self.cursor.execute("insert into TestObjects (IntCol, ObjectCol) " \
227 "values (4, :obj)", obj = obj)
228 self.cursor.execute("""
229 select IntCol, ObjectCol, ArrayCol
230 from TestObjects
231 where IntCol = 4""")
232 self.__TestData(4, (5, 'A string', 'Fixed str ', 'A NCHAR string',
233 'Fixed N ', b'Raw Value', 27, 13, 184.875, 1.375, 23.75,
234 14.25, 29.1625, cx_Oracle.Timestamp(2017, 5, 9, 0, 0, 0),
235 cx_Oracle.Timestamp(2017, 5, 9, 9, 41, 13),
236 cx_Oracle.Timestamp(1986, 8, 2, 15, 27, 38),
237 cx_Oracle.Timestamp(1999, 11, 12, 23, 5, 2),
238 'A short CLOB', 'A short NCLOB', b'A short BLOB',
239 (23, 'Substring value'), None), None)
240 self.connection.rollback()
241
242 def testInvalidTypeObject(self):
243 "test trying to find an object type that does not exist"
244 self.assertRaises(cx_Oracle.DatabaseError, self.connection.gettype,
245 "A TYPE THAT DOES NOT EXIST")
246
247 def testAppendingWrongObjectType(self):
248 "test appending an object of the wrong type to a collection"
249 collectionObjType = self.connection.gettype("UDT_OBJECTARRAY")
250 collectionObj = collectionObjType.newobject()
251 arrayObjType = self.connection.gettype("UDT_ARRAY")
252 arrayObj = arrayObjType.newobject()
253 self.assertRaises(cx_Oracle.DatabaseError, collectionObj.append,
254 arrayObj)
255
256 def testReferencingSubObj(self):
257 "test that referencing a sub object affects the parent object"
258 objType = self.connection.gettype("UDT_OBJECT")
259 subObjType = self.connection.gettype("UDT_SUBOBJECT")
260 obj = objType.newobject()
261 obj.SUBOBJECTVALUE = subObjType.newobject()
262 obj.SUBOBJECTVALUE.SUBNUMBERVALUE = 5
263 obj.SUBOBJECTVALUE.SUBSTRINGVALUE = "Substring"
264 self.assertEqual(obj.SUBOBJECTVALUE.SUBNUMBERVALUE, 5)
265 self.assertEqual(obj.SUBOBJECTVALUE.SUBSTRINGVALUE, "Substring")
266
267 def testAccessSubObjectParentObjectDestroyed(self):
268 "test that accessing sub object after parent object destroyed works"
269 objType = self.connection.gettype("UDT_OBJECT")
270 subObjType = self.connection.gettype("UDT_SUBOBJECT")
271 arrayType = self.connection.gettype("UDT_OBJECTARRAY")
272 subObj1 = subObjType.newobject()
273 subObj1.SUBNUMBERVALUE = 2
274 subObj1.SUBSTRINGVALUE = "AB"
275 subObj2 = subObjType.newobject()
276 subObj2.SUBNUMBERVALUE = 3
277 subObj2.SUBSTRINGVALUE = "CDE"
278 obj = objType.newobject()
279 obj.SUBOBJECTARRAY = arrayType.newobject([subObj1, subObj2])
280 subObjArray = obj.SUBOBJECTARRAY
281 del obj
282 self.assertEqual(self.__GetObjectAsTuple(subObjArray),
283 [(2, "AB"), (3, "CDE")])
284
285 def testSettingAttrWrongObjectType(self):
286 "test assigning an object of the wrong type to an object attribute"
287 objType = self.connection.gettype("UDT_OBJECT")
288 obj = objType.newobject()
289 wrongObjType = self.connection.gettype("UDT_OBJECTARRAY")
290 wrongObj = wrongObjType.newobject()
291 self.assertRaises(cx_Oracle.DatabaseError, setattr, obj,
292 "SUBOBJECTVALUE", wrongObj)
293
294 def testSettingVarWrongObjectType(self):
295 "test setting value of object variable to wrong object type"
296 wrongObjType = self.connection.gettype("UDT_OBJECTARRAY")
297 wrongObj = wrongObjType.newobject()
298 var = self.cursor.var(cx_Oracle.OBJECT, typename = "UDT_OBJECT")
299 self.assertRaises(cx_Oracle.DatabaseError, var.setvalue, 0, wrongObj)
300
301 def testStringFormat(self):
302 "test object string format"
303 objType = self.connection.gettype("UDT_OBJECT")
304 user = TestEnv.GetMainUser()
305 self.assertEqual(str(objType),
306 "<cx_Oracle.ObjectType %s.UDT_OBJECT>" % user.upper())
307 self.assertEqual(str(objType.attributes[0]),
308 "<cx_Oracle.ObjectAttribute NUMBERVALUE>")
309
310 def testTrimCollectionList(self):
311 "test Trim number of elements from collection"
312 subObjType = self.connection.gettype("UDT_SUBOBJECT")
313 arrayType = self.connection.gettype("UDT_OBJECTARRAY")
314 data = [(1, "AB"), (2, "CDE"), (3, "FGH"), (4, "IJK")]
315 arrayObj = arrayType()
316 for numVal, strVal in data:
317 subObj = subObjType()
318 subObj.SUBNUMBERVALUE = numVal
319 subObj.SUBSTRINGVALUE = strVal
320 arrayObj.append(subObj)
321 self.assertEqual(self.__GetObjectAsTuple(arrayObj), data)
322 arrayObj.trim(2)
323 self.assertEqual(self.__GetObjectAsTuple(arrayObj), data[:2])
324 arrayObj.trim(1)
325 self.assertEqual(self.__GetObjectAsTuple(arrayObj), data[:1])
326 arrayObj.trim(0)
327 self.assertEqual(self.__GetObjectAsTuple(arrayObj), data[:1])
328 arrayObj.trim(1)
329 self.assertEqual(self.__GetObjectAsTuple(arrayObj), [])
330
331 if __name__ == "__main__":
332 TestEnv.RunTestCases()
333
11
22 1. The schemas and SQL objects that are referenced in the test suite can be
33 created by running the Python script [SetupTest.py][1]. The script requires
4 SYSDBA privileges and will prompt for these credentials as well as the
5 names of the schemas that will be created, unless a number of environment
6 variables are set as documented in the Python script [TestEnv.py][2]. Run
7 the script using the following command:
4 administrative privileges and will prompt for these credentials as well as
5 the names of the schemas that will be created, unless a number of
6 environment variables are set as documented in the Python script
7 [TestEnv.py][2]. Run the script using the following command:
88
99 python SetupTest.py
1010
1212 will always prompt for the names of the schemas that will be created. Run
1313 the script using the following command:
1414
15 sqlplus sys/syspassword@hostname/servicename @sql/SetupTest.sql
15 sqlplus system/systempassword@hostname/servicename @sql/SetupTest.sql
1616
1717 2. Run the test suite by issuing the following command in the top-level
1818 directory of your cx_Oracle installation:
1919
20 python setup.py test
20 tox
2121
2222 Alternatively, you can run the test suite directly within this directory:
2323
24 python test.py
24 python TestEnv.py
2525
2626 3. After running the test suite, the schemas can be dropped by running the
27 Python script [DropTest.py][4]. The script requires SYSDBA privileges and
28 will prompt for these credentials as well as the names of the schemas
29 that will be dropped, unless a number of environment variables are set as
30 documented in the Python script [TestEnv.py][2]. Run the script using the
31 following command:
27 Python script [DropTest.py][4]. The script requires administrative
28 privileges and will prompt for these credentials as well as the names of the
29 schemas that will be dropped, unless a number of environment variables are
30 set as documented in the Python script [TestEnv.py][2]. Run the script using
31 the following command:
3232
3333 python DropTest.py
3434
3636 will always prompt for the names of the schemas that will be dropped. Run
3737 the script using the following command:
3838
39 sqlplus sys/syspassword@hostname/servicename @sql/DropTest.sql
39 sqlplus system/systempassword@hostname/servicename @sql/DropTest.sql
4040
4141 [1]: https://github.com/oracle/python-cx_Oracle/blob/master/test/SetupTest.py
4242 [2]: https://github.com/oracle/python-cx_Oracle/blob/master/test/TestEnv.py
4343 [3]: https://github.com/oracle/python-cx_Oracle/blob/master/test/sql/SetupTest.sql
4444 [4]: https://github.com/oracle/python-cx_Oracle/blob/master/test/DropTest.py
4545 [5]: https://github.com/oracle/python-cx_Oracle/blob/master/test/sql/DropTest.sql
46
+0
-62
test/Rowid.py less more
0 # -*- coding: utf-8 -*-
1 #------------------------------------------------------------------------------
2 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
3 #------------------------------------------------------------------------------
4
5 """Module for testing Rowids"""
6
7 import TestEnv
8
9 import cx_Oracle
10
11 class TestCase(TestEnv.BaseTestCase):
12
13 def __TestSelectRowids(self, tableName):
14 self.cursor.execute("select rowid, IntCol from %s""" % tableName)
15 rowidDict = dict(self.cursor)
16 sql = "select IntCol from %s where rowid = :val" % tableName
17 for rowid, intVal in rowidDict.items():
18 self.cursor.execute(sql, val = rowid)
19 rows = self.cursor.fetchall()
20 self.assertEqual(len(rows), 1)
21 self.assertEqual(rows[0][0], intVal)
22
23 def testSelectRowidsRegular(self):
24 "test selecting all rowids from a regular table"
25 self.__TestSelectRowids("TestNumbers")
26
27 def testSelectRowidsIndexOrganised(self):
28 "test selecting all rowids from an index organised table"
29 self.__TestSelectRowids("TestUniversalRowids")
30
31 def testInsertInvalidRowid(self):
32 "test inserting an invalid rowid"
33 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.execute,
34 "insert into TestRowids (IntCol, RowidCol) values (1, :rid)",
35 rid = 12345)
36 self.assertRaises(cx_Oracle.DatabaseError, self.cursor.execute,
37 "insert into TestRowids (IntCol, RowidCol) values (1, :rid)",
38 rid = "523lkhlf")
39
40 def testInsertRowids(self):
41 "test inserting rowids and verify they are inserted correctly"
42 self.cursor.execute("select IntCol, rowid from TestNumbers")
43 rows = self.cursor.fetchall()
44 self.cursor.execute("truncate table TestRowids")
45 self.cursor.executemany("""
46 insert into TestRowids
47 (IntCol, RowidCol)
48 values (:1, :2)""", rows)
49 self.connection.commit()
50 self.cursor.execute("select IntCol, RowidCol from TestRowids")
51 rows = self.cursor.fetchall()
52 sql = "select IntCol from TestNumbers where rowid = :val"
53 for intVal, rowid in rows:
54 self.cursor.execute(sql, val = rowid)
55 rows = self.cursor.fetchall()
56 self.assertEqual(len(rows), 1)
57 self.assertEqual(rows[0][0], intVal)
58
59 if __name__ == "__main__":
60 TestEnv.RunTestCases()
61
+0
-245
test/SessionPool.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing session pools."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import threading
15
16 class TestCase(TestEnv.BaseTestCase):
17
18 def __ConnectAndDrop(self):
19 """Connect to the database, perform a query and drop the connection."""
20 connection = self.pool.acquire()
21 cursor = connection.cursor()
22 cursor.execute("select count(*) from TestNumbers")
23 count, = cursor.fetchone()
24 self.assertEqual(count, 10)
25
26 def __ConnectAndGenerateError(self):
27 """Connect to the database, perform a query which raises an error"""
28 connection = self.pool.acquire()
29 cursor = connection.cursor()
30 self.assertRaises(cx_Oracle.DatabaseError, cursor.execute,
31 "select 1 / 0 from dual")
32
33 def __VerifyConnection(self, connection, expectedUser,
34 expectedProxyUser = None):
35 cursor = connection.cursor()
36 cursor.execute("""
37 select
38 sys_context('userenv', 'session_user'),
39 sys_context('userenv', 'proxy_user')
40 from dual""")
41 actualUser, actualProxyUser = cursor.fetchone()
42 self.assertEqual(actualUser, expectedUser.upper())
43 self.assertEqual(actualProxyUser,
44 expectedProxyUser and expectedProxyUser.upper())
45
46 def setUp(self):
47 pass
48
49 def tearDown(self):
50 pass
51
52 def testPool(self):
53 """test that the pool is created and has the right attributes"""
54 pool = TestEnv.GetPool(min=2, max=8, increment=3,
55 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT)
56 self.assertEqual(pool.username, TestEnv.GetMainUser(),
57 "user name differs")
58 self.assertEqual(pool.tnsentry, TestEnv.GetConnectString(),
59 "tnsentry differs")
60 self.assertEqual(pool.dsn, TestEnv.GetConnectString(), "dsn differs")
61 self.assertEqual(pool.max, 8, "max differs")
62 self.assertEqual(pool.min, 2, "min differs")
63 self.assertEqual(pool.increment, 3, "increment differs")
64 self.assertEqual(pool.opened, 2, "opened differs")
65 self.assertEqual(pool.busy, 0, "busy not 0 at start")
66 connection_1 = pool.acquire()
67 self.assertEqual(pool.busy, 1, "busy not 1 after acquire")
68 self.assertEqual(pool.opened, 2, "opened not unchanged (1)")
69 connection_2 = pool.acquire()
70 self.assertEqual(pool.busy, 2, "busy not 2 after acquire")
71 self.assertEqual(pool.opened, 2, "opened not unchanged (2)")
72 connection_3 = pool.acquire()
73 self.assertEqual(pool.busy, 3, "busy not 3 after acquire")
74 self.assertEqual(pool.opened, 5, "opened not changed (3)")
75 pool.release(connection_3)
76 self.assertEqual(pool.busy, 2, "busy not 2 after release")
77 del connection_2
78 self.assertEqual(pool.busy, 1, "busy not 1 after del")
79 pool.getmode = cx_Oracle.SPOOL_ATTRVAL_NOWAIT
80 self.assertEqual(pool.getmode, cx_Oracle.SPOOL_ATTRVAL_NOWAIT)
81 if TestEnv.GetClientVersion() >= (12, 2):
82 pool.getmode = cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT
83 self.assertEqual(pool.getmode, cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT)
84 pool.stmtcachesize = 50
85 self.assertEqual(pool.stmtcachesize, 50)
86 pool.timeout = 10
87 self.assertEqual(pool.timeout, 10)
88 if TestEnv.GetClientVersion() >= (12, 1):
89 pool.max_lifetime_session = 10
90 self.assertEqual(pool.max_lifetime_session, 10)
91
92 def testProxyAuth(self):
93 """test that proxy authentication is possible"""
94 pool = TestEnv.GetPool(min=2, max=8, increment=3,
95 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT)
96 self.assertEqual(pool.homogeneous, 1,
97 "homogeneous should be 1 by default")
98 self.assertRaises(cx_Oracle.ProgrammingError, pool.acquire,
99 user = u"missing_proxyuser")
100 pool = TestEnv.GetPool(min=2, max=8, increment=3,
101 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT, homogeneous=False)
102 self.assertEqual(pool.homogeneous, 0,
103 "homogeneous should be 0 after setting it in the constructor")
104 connection = pool.acquire(user = TestEnv.GetProxyUser())
105 cursor = connection.cursor()
106 cursor.execute('select user from dual')
107 result, = cursor.fetchone()
108 self.assertEqual(result, TestEnv.GetProxyUser().upper())
109
110 def testRollbackOnDel(self):
111 "connection rolls back before being destroyed"
112 pool = TestEnv.GetPool()
113 connection = pool.acquire()
114 cursor = connection.cursor()
115 cursor.execute("truncate table TestTempTable")
116 cursor.execute("insert into TestTempTable (IntCol) values (1)")
117 pool = TestEnv.GetPool()
118 connection = pool.acquire()
119 cursor = connection.cursor()
120 cursor.execute("select count(*) from TestTempTable")
121 count, = cursor.fetchone()
122 self.assertEqual(count, 0)
123
124 def testRollbackOnRelease(self):
125 "connection rolls back before released back to the pool"
126 pool = TestEnv.GetPool()
127 connection = pool.acquire()
128 cursor = connection.cursor()
129 cursor.execute("truncate table TestTempTable")
130 cursor.execute("insert into TestTempTable (IntCol) values (1)")
131 cursor.close()
132 pool.release(connection)
133 pool = TestEnv.GetPool()
134 connection = pool.acquire()
135 cursor = connection.cursor()
136 cursor.execute("select count(*) from TestTempTable")
137 count, = cursor.fetchone()
138 self.assertEqual(count, 0)
139
140 def testThreading(self):
141 """test session pool to database with multiple threads"""
142 self.pool = TestEnv.GetPool(min=5, max=20, increment=2, threaded=True,
143 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT)
144 threads = []
145 for i in range(20):
146 thread = threading.Thread(None, self.__ConnectAndDrop)
147 threads.append(thread)
148 thread.start()
149 for thread in threads:
150 thread.join()
151
152 def testThreadingWithErrors(self):
153 """test session pool to database with multiple threads (with errors)"""
154 self.pool = TestEnv.GetPool(min=5, max=20, increment=2, threaded=True,
155 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT)
156 threads = []
157 for i in range(20):
158 thread = threading.Thread(None, self.__ConnectAndGenerateError)
159 threads.append(thread)
160 thread.start()
161 for thread in threads:
162 thread.join()
163
164 def testPurity(self):
165 """test session pool with various types of purity"""
166 action = "TEST_ACTION"
167 pool = TestEnv.GetPool(min=1, max=8, increment=1,
168 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT)
169
170 # get connection and set the action
171 connection = pool.acquire()
172 connection.action = action
173 cursor = connection.cursor()
174 cursor.execute("select 1 from dual")
175 cursor.close()
176 pool.release(connection)
177 self.assertEqual(pool.opened, 1, "opened (1)")
178
179 # verify that the connection still has the action set on it
180 connection = pool.acquire()
181 cursor = connection.cursor()
182 cursor.execute("select sys_context('userenv', 'action') from dual")
183 result, = cursor.fetchone()
184 self.assertEqual(result, action)
185 cursor.close()
186 pool.release(connection)
187 self.assertEqual(pool.opened, 1, "opened (2)")
188
189 # get a new connection with new purity (should not have state)
190 connection = pool.acquire(purity = cx_Oracle.ATTR_PURITY_NEW)
191 cursor = connection.cursor()
192 cursor.execute("select sys_context('userenv', 'action') from dual")
193 result, = cursor.fetchone()
194 self.assertEqual(result, None)
195 cursor.close()
196 self.assertEqual(pool.opened, 2, "opened (3)")
197 pool.drop(connection)
198 self.assertEqual(pool.opened, 1, "opened (4)")
199
200 def testHeterogeneous(self):
201 """test heterogeneous pool with user and password specified"""
202 pool = TestEnv.GetPool(min=2, max=8, increment=3, homogeneous=False,
203 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT)
204 self.assertEqual(pool.homogeneous, 0)
205 self.__VerifyConnection(pool.acquire(), TestEnv.GetMainUser())
206 self.__VerifyConnection(pool.acquire(TestEnv.GetMainUser(),
207 TestEnv.GetMainPassword()), TestEnv.GetMainUser())
208 self.__VerifyConnection(pool.acquire(TestEnv.GetProxyUser(),
209 TestEnv.GetProxyPassword()), TestEnv.GetProxyUser())
210 userStr = "%s[%s]" % (TestEnv.GetMainUser(), TestEnv.GetProxyUser())
211 self.__VerifyConnection(pool.acquire(userStr,
212 TestEnv.GetMainPassword()), TestEnv.GetProxyUser(),
213 TestEnv.GetMainUser())
214
215 def testHeterogenousWithoutUser(self):
216 """test heterogeneous pool without user and password specified"""
217 pool = TestEnv.GetPool(user="", password="", min=2, max=8, increment=3,
218 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT, homogeneous=False)
219 self.__VerifyConnection(pool.acquire(TestEnv.GetMainUser(),
220 TestEnv.GetMainPassword()), TestEnv.GetMainUser())
221 self.__VerifyConnection(pool.acquire(TestEnv.GetProxyUser(),
222 TestEnv.GetProxyPassword()), TestEnv.GetProxyUser())
223 userStr = "%s[%s]" % (TestEnv.GetMainUser(), TestEnv.GetProxyUser())
224 self.__VerifyConnection(pool.acquire(userStr,
225 TestEnv.GetMainPassword()), TestEnv.GetProxyUser(),
226 TestEnv.GetMainUser())
227
228 def testHeterogenousWithoutPassword(self):
229 """test heterogeneous pool without password"""
230 pool = TestEnv.GetPool(min=2, max=8, increment=3,
231 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT, homogeneous=False)
232 self.assertRaises(cx_Oracle.DatabaseError, pool.acquire,
233 TestEnv.GetMainUser())
234
235 def testHeterogeneousWrongPassword(self):
236 """test heterogeneous pool with wrong password specified"""
237 pool = TestEnv.GetPool(min=2, max=8, increment=3,
238 getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT, homogeneous=False)
239 self.assertRaises(cx_Oracle.DatabaseError, pool.acquire,
240 TestEnv.GetProxyUser(), "this is the wrong password")
241
242 if __name__ == "__main__":
243 TestEnv.RunTestCases()
244
88 # necessary for the cx_Oracle test suite.
99 #------------------------------------------------------------------------------
1010
11 from __future__ import print_function
12
1311 import cx_Oracle
1412
15 import TestEnv
13 import test_env
1614 import DropTest
1715
18 # connect as SYSDBA
19 conn = cx_Oracle.connect(TestEnv.GetSysdbaConnectString(),
20 mode = cx_Oracle.SYSDBA)
16 # connect as administrative user (usually SYSTEM or ADMIN)
17 conn = cx_Oracle.connect(test_env.get_admin_connect_string())
2118
2219 # drop existing users and editions, if applicable
23 DropTest.DropTests(conn)
20 DropTest.drop_tests(conn)
2421
2522 # create test schemas
2623 print("Creating test schemas...")
27 TestEnv.RunSqlScript(conn, "SetupTest",
28 main_user = TestEnv.GetMainUser(),
29 main_password = TestEnv.GetMainPassword(),
30 proxy_user = TestEnv.GetProxyUser(),
31 proxy_password = TestEnv.GetProxyPassword())
24 test_env.run_sql_script(conn, "SetupTest",
25 main_user=test_env.get_main_user(),
26 main_password=test_env.get_main_password(),
27 proxy_user=test_env.get_proxy_user(),
28 proxy_password=test_env.get_proxy_password())
3229 print("Done.")
33
+0
-280
test/SodaCollection.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """Module for testing Simple Oracle Document Access (SODA) Collections"""
5
6 import TestEnv
7
8 import cx_Oracle
9
10 class TestCase(TestEnv.BaseTestCase):
11
12 def __testSkip(self, coll, numToSkip, expectedContent):
13 filterSpec = {'$orderby': [{'path': 'name', 'order': 'desc'}]}
14 doc = coll.find().filter(filterSpec).skip(numToSkip).getOne()
15 content = doc.getContent() if doc is not None else None
16 self.assertEqual(content, expectedContent)
17
18 def testInvalidJson(self):
19 "test inserting invalid JSON value into SODA collection"
20 invalidJson = "{testKey:testValue}"
21 sodaDatabase = self.connection.getSodaDatabase()
22 coll = sodaDatabase.createCollection("cxoInvalidJSON")
23 doc = sodaDatabase.createDocument(invalidJson)
24 self.assertRaises(cx_Oracle.IntegrityError, coll.insertOne, doc)
25 coll.drop()
26
27 def testInsertDocuments(self):
28 "test inserting documents into a SODA collection"
29 sodaDatabase = self.connection.getSodaDatabase()
30 coll = sodaDatabase.createCollection("cxoInsertDocs")
31 coll.find().remove()
32 valuesToInsert = [
33 { "name" : "George", "age" : 47 },
34 { "name" : "Susan", "age" : 39 },
35 { "name" : "John", "age" : 50 },
36 { "name" : "Jill", "age" : 54 }
37 ]
38 insertedKeys = []
39 for value in valuesToInsert:
40 doc = coll.insertOneAndGet(value)
41 insertedKeys.append(doc.key)
42 self.connection.commit()
43 self.assertEqual(coll.find().count(), len(valuesToInsert))
44 for key, value in zip(insertedKeys, valuesToInsert):
45 doc = coll.find().key(key).getOne()
46 self.assertEqual(doc.getContent(), value)
47 coll.drop()
48
49 def testSkipDocuments(self):
50 "test skipping documents in a SODA collection"
51 sodaDatabase = self.connection.getSodaDatabase()
52 coll = sodaDatabase.createCollection("cxoSkipDocs")
53 coll.find().remove()
54 valuesToInsert = [
55 { "name" : "Anna", "age" : 62 },
56 { "name" : "Mark", "age" : 37 },
57 { "name" : "Martha", "age" : 43 },
58 { "name" : "Matthew", "age" : 28 }
59 ]
60 for value in valuesToInsert:
61 coll.insertOne(value)
62 self.connection.commit()
63 self.__testSkip(coll, 0, valuesToInsert[3])
64 self.__testSkip(coll, 1, valuesToInsert[2])
65 self.__testSkip(coll, 3, valuesToInsert[0])
66 self.__testSkip(coll, 4, None)
67 self.__testSkip(coll, 125, None)
68
69 def testReplaceDocument(self):
70 "test replace documents in SODA collection"
71 sodaDatabase = self.connection.getSodaDatabase()
72 coll = sodaDatabase.createCollection("cxoReplaceDoc")
73 coll.find().remove()
74 content = {'name': 'John', 'address': {'city': 'Sydney'}}
75 doc = coll.insertOneAndGet(content)
76 newContent = {'name': 'John', 'address': {'city':'Melbourne'}}
77 coll.find().key(doc.key).replaceOne(newContent)
78 self.connection.commit()
79 self.assertEqual(coll.find().key(doc.key).getOne().getContent(),
80 newContent)
81 coll.drop()
82
83 def testSearchDocumentsWithContent(self):
84 "test search documents with content using $like and $regex"
85 sodaDatabase = self.connection.getSodaDatabase()
86 coll = sodaDatabase.createCollection("cxoSearchDocContent")
87 coll.find().remove()
88 data = [
89 {'name': 'John', 'address': {'city': 'Bangalore'}},
90 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
91 {'name': 'Joseph', 'address': {'city': 'Bangalore'}},
92 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
93 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
94 {'name': 'Matthew', 'address': {'city': 'Mumbai'}}
95 ]
96 for value in data:
97 coll.insertOne(value)
98 self.connection.commit()
99 filterSpecs = [
100 ({'name': {'$like': 'And%'}}, 1),
101 ({'name': {'$like': 'J%n'}}, 3),
102 ({'name': {'$like': '%hn%'}}, 2),
103 ({'address.city': {'$like': 'Ban%'}}, 3),
104 ({'address.city': {'$like': '%bad'}}, 2),
105 ({'address.city': {'$like': 'Hyderabad'}}, 1),
106 ({'address.city': {'$like': 'China%'}}, 0),
107 ({'name': {'$regex': 'Jo.*'}}, 3),
108 ({'name': {'$regex': '.*[ho]n'}}, 2),
109 ({'name': {'$regex': 'J.*h'}}, 1),
110 ({'address.city': {'$regex': 'Ba.*'}}, 3),
111 ({'address.city': {'$regex': '.*bad'}}, 2),
112 ({'address.city': {'$regex': 'Hyderabad'}}, 1),
113 ({'name': {'$regex': 'Js.*n'}}, 0)
114 ]
115 for filterSpec, expectedCount in filterSpecs:
116 self.assertEqual(coll.find().filter(filterSpec).count(),
117 expectedCount, filterSpec)
118 coll.drop()
119
120 def testDocumentRemove(self):
121 "test removing documents"
122 sodaDatabase = self.connection.getSodaDatabase()
123 coll = sodaDatabase.createCollection("cxoRemoveDocs")
124 coll.find().remove()
125 data = [
126 {'name': 'John', 'address': {'city': 'Bangalore'}},
127 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
128 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
129 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
130 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
131 {'name': 'Matthew', 'address': {'city': 'Mumbai'}}
132 ]
133 docs = [coll.insertOneAndGet(v) for v in data]
134 coll.find().key(docs[3].key).remove()
135 self.assertEqual(coll.find().count(), len(data) - 1)
136 searchResults = coll.find().filter({'name': {'$like': 'Jibin'}})
137 self.assertEqual(searchResults.count(), 0)
138 coll.find().filter({'name': {'$like': 'John%'}}).remove()
139 self.assertEqual(coll.find().count(), len(data) - 3)
140 coll.find().filter({'name': {'$regex': 'J.*'}}).remove()
141 self.assertEqual(coll.find().count(), len(data) - 4)
142 self.connection.commit()
143 coll.drop()
144
145 def testCreateAndDropIndex(self):
146 "test create and drop Index"
147 indexName = "cxoTestIndexes_ix_1"
148 indexSpec = {
149 'name': indexName,
150 'fields': [
151 {
152 'path': 'address.city',
153 'datatype': 'string',
154 'order': 'asc'
155 }
156 ]
157 }
158 sodaDatabase = self.connection.getSodaDatabase()
159 coll = sodaDatabase.createCollection("cxoTestIndexes")
160 coll.find().remove()
161 self.connection.commit()
162 coll.dropIndex(indexName)
163 coll.createIndex(indexSpec)
164 self.assertRaises(cx_Oracle.DatabaseError, coll.createIndex, indexSpec)
165 self.assertEqual(coll.dropIndex(indexName), True)
166 self.assertEqual(coll.dropIndex(indexName), False)
167 coll.drop()
168
169 def testGetDocuments(self):
170 "test getting documents from Collection"
171 self.connection.autocommit = True
172 sodaDatabase = self.connection.getSodaDatabase()
173 coll = sodaDatabase.createCollection("cxoTestGetDocs")
174 coll.find().remove()
175 data = [
176 {'name': 'John', 'address': {'city': 'Bangalore'}},
177 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
178 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
179 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
180 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}}
181 ]
182 insertedKeys = list(sorted(coll.insertOneAndGet(v).key for v in data))
183 fetchedKeys = list(sorted(d.key for d in coll.find().getDocuments()))
184 self.assertEqual(fetchedKeys, insertedKeys)
185 coll.drop()
186
187 def testCursor(self):
188 "test fetching documents from a cursor"
189 self.connection.autocommit = True
190 sodaDatabase = self.connection.getSodaDatabase()
191 coll = sodaDatabase.createCollection("cxoFindViaCursor")
192 coll.find().remove()
193 data = [
194 {'name': 'John', 'address': {'city': 'Bangalore'}},
195 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
196 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
197 ]
198 insertedKeys = list(sorted(coll.insertOneAndGet(v).key for v in data))
199 fetchedKeys = list(sorted(d.key for d in coll.find().getCursor()))
200 self.assertEqual(fetchedKeys, insertedKeys)
201 coll.drop()
202
203 def testMultipleDocumentRemove(self):
204 "test removing multiple documents using multiple keys"
205 sodaDatabase = self.connection.getSodaDatabase()
206 coll = sodaDatabase.createCollection("cxoRemoveMultipleDocs")
207 coll.find().remove()
208 data = [
209 {'name': 'John', 'address': {'city': 'Bangalore'}},
210 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
211 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
212 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
213 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
214 {'name': 'Matthew', 'address': {'city': 'Mumbai'}}
215 ]
216 docs = [coll.insertOneAndGet(v) for v in data]
217 keys = [docs[i].key for i in (1, 3, 5)]
218 numRemoved = coll.find().keys(keys).remove()
219 self.assertEqual(numRemoved, len(keys))
220 self.assertEqual(coll.find().count(), len(data) - len(keys))
221 self.connection.commit()
222 coll.drop()
223
224 def testDocumentVersion(self):
225 "test using version to get documents and remove them"
226 sodaDatabase = self.connection.getSodaDatabase()
227 coll = sodaDatabase.createCollection("cxoDocumentVersion")
228 coll.find().remove()
229 content = {'name': 'John', 'address': {'city': 'Bangalore'}}
230 insertedDoc = coll.insertOneAndGet(content)
231 key = insertedDoc.key
232 version = insertedDoc.version
233 doc = coll.find().key(key).version(version).getOne()
234 self.assertEqual(doc.getContent(), content)
235 newContent = {'name': 'James', 'address': {'city': 'Delhi'}}
236 replacedDoc = coll.find().key(key).replaceOneAndGet(newContent)
237 newVersion = replacedDoc.version
238 doc = coll.find().key(key).version(version).getOne()
239 self.assertEqual(doc, None)
240 doc = coll.find().key(key).version(newVersion).getOne()
241 self.assertEqual(doc.getContent(), newContent)
242 self.assertEqual(coll.find().key(key).version(version).remove(), 0)
243 self.assertEqual(coll.find().key(key).version(newVersion).remove(), 1)
244 self.assertEqual(coll.find().count(), 0)
245 self.connection.commit()
246 coll.drop()
247
248 def testGetCursor(self):
249 "test keys with GetCursor"
250 sodaDatabase = self.connection.getSodaDatabase()
251 coll = sodaDatabase.createCollection("cxoKeysWithGetCursor")
252 coll.find().remove()
253 data = [
254 {'name': 'John', 'address': {'city': 'Bangalore'}},
255 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
256 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
257 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
258 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
259 {'name': 'Matthew', 'address': {'city': 'Mumbai'}}
260 ]
261 docs = [coll.insertOneAndGet(v) for v in data]
262 keys = [docs[i].key for i in (2, 4, 5)]
263 fetchedKeys = [d.key for d in coll.find().keys(keys).getCursor()]
264 self.assertEqual(list(sorted(fetchedKeys)), list(sorted(keys)))
265 self.connection.commit()
266 coll.drop()
267
268 def testCreatedOn(self):
269 "test createdOn attribute of Document"
270 sodaDatabase = self.connection.getSodaDatabase()
271 coll = sodaDatabase.createCollection("cxoCreatedOn")
272 coll.find().remove()
273 data = {'name': 'John', 'address': {'city': 'Bangalore'}}
274 doc = coll.insertOneAndGet(data)
275 self.assertEqual(doc.createdOn, doc.lastModified)
276
277 if __name__ == "__main__":
278 TestEnv.RunTestCases()
279
+0
-107
test/SodaDatabase.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """Module for testing Simple Oracle Document Access (SODA) Database"""
5
6 import TestEnv
7
8 import cx_Oracle
9 import json
10
11 class TestCase(TestEnv.BaseTestCase):
12
13 def __dropExistingCollections(self, sodaDatabase):
14 for name in sodaDatabase.getCollectionNames():
15 sodaDatabase.openCollection(name).drop()
16
17 def __verifyDocument(self, doc, rawContent, strContent=None, content=None,
18 key=None, mediaType='application/json'):
19 self.assertEqual(doc.getContentAsBytes(), rawContent)
20 if strContent is not None:
21 self.assertEqual(doc.getContentAsString(), strContent)
22 if content is not None:
23 self.assertEqual(doc.getContent(), content)
24 self.assertEqual(doc.key, key)
25 self.assertEqual(doc.mediaType, mediaType)
26
27 def testCreateDocumentWithJson(self):
28 "test creating documents with JSON data"
29 sodaDatabase = self.connection.getSodaDatabase()
30 val = {"testKey1" : "testValue1", "testKey2" : "testValue2" }
31 strVal = json.dumps(val)
32 bytesVal = strVal.encode("UTF-8")
33 key = "MyKey"
34 mediaType = "text/plain"
35 doc = sodaDatabase.createDocument(val)
36 self.__verifyDocument(doc, bytesVal, strVal, val)
37 doc = sodaDatabase.createDocument(strVal, key)
38 self.__verifyDocument(doc, bytesVal, strVal, val, key)
39 doc = sodaDatabase.createDocument(bytesVal, key, mediaType)
40 self.__verifyDocument(doc, bytesVal, strVal, val, key, mediaType)
41
42 def testCreateDocumentWithRaw(self):
43 "test creating documents with raw data"
44 sodaDatabase = self.connection.getSodaDatabase()
45 val = b"<html/>"
46 key = "MyRawKey"
47 mediaType = "text/html"
48 doc = sodaDatabase.createDocument(val)
49 self.__verifyDocument(doc, val)
50 doc = sodaDatabase.createDocument(val, key)
51 self.__verifyDocument(doc, val, key=key)
52 doc = sodaDatabase.createDocument(val, key, mediaType)
53 self.__verifyDocument(doc, val, key=key, mediaType=mediaType)
54
55 def testGetCollectionNames(self):
56 "test getting collection names from the database"
57 sodaDatabase = self.connection.getSodaDatabase()
58 self.__dropExistingCollections(sodaDatabase)
59 self.assertEqual(sodaDatabase.getCollectionNames(), [])
60 names = ["zCol", "dCol", "sCol", "aCol", "gCol"]
61 sortedNames = list(sorted(names))
62 for name in names:
63 sodaDatabase.createCollection(name)
64 self.assertEqual(sodaDatabase.getCollectionNames(), sortedNames)
65 self.assertEqual(sodaDatabase.getCollectionNames(limit=2),
66 sortedNames[:2])
67 self.assertEqual(sodaDatabase.getCollectionNames("a"), sortedNames)
68 self.assertEqual(sodaDatabase.getCollectionNames("C"), sortedNames)
69 self.assertEqual(sodaDatabase.getCollectionNames("b", limit=3),
70 sortedNames[1:4])
71 self.assertEqual(sodaDatabase.getCollectionNames("z"),
72 sortedNames[-1:])
73
74 def testOpenCollection(self):
75 "test opening a collection"
76 sodaDatabase = self.connection.getSodaDatabase()
77 self.__dropExistingCollections(sodaDatabase)
78 coll = sodaDatabase.openCollection("CollectionThatDoesNotExist")
79 self.assertEqual(coll, None)
80 createdColl = sodaDatabase.createCollection("cxoTestOpenCollection")
81 coll = sodaDatabase.openCollection(createdColl.name)
82 self.assertEqual(coll.name, createdColl.name)
83 coll.drop()
84
85 def testRepr(self):
86 "test SodaDatabase representation"
87 con1 = self.connection
88 con2 = TestEnv.GetConnection()
89 sodaDatabase1 = con1.getSodaDatabase()
90 sodaDatabase2 = con1.getSodaDatabase()
91 sodaDatabase3 = con2.getSodaDatabase()
92 self.assertEqual(str(sodaDatabase1), str(sodaDatabase2))
93 self.assertEqual(str(sodaDatabase2), str(sodaDatabase3))
94
95 def testNegative(self):
96 "test negative cases for SODA database methods"
97 sodaDatabase = self.connection.getSodaDatabase()
98 self.assertRaises(TypeError, sodaDatabase.createCollection)
99 self.assertRaises(TypeError, sodaDatabase.createCollection, 1)
100 self.assertRaises(cx_Oracle.DatabaseError,
101 sodaDatabase.createCollection, None)
102 self.assertRaises(TypeError, sodaDatabase.getCollectionNames, 1)
103
104 if __name__ == "__main__":
105 TestEnv.RunTestCases()
106
+0
-437
test/StringVar.py less more
0 # -*- coding: utf-8 -*-
1 #------------------------------------------------------------------------------
2 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
3 #
4 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
5 #
6 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
7 # Canada. All rights reserved.
8 #------------------------------------------------------------------------------
9
10 """Module for testing string variables."""
11
12 import TestEnv
13
14 import cx_Oracle
15 import datetime
16 import string
17 import random
18
19 class TestCase(TestEnv.BaseTestCase):
20
21 def setUp(self):
22 TestEnv.BaseTestCase.setUp(self)
23 self.rawData = []
24 self.dataByKey = {}
25 for i in range(1, 11):
26 stringCol = "String %d" % i
27 fixedCharCol = ("Fixed Char %d" % i).ljust(40)
28 rawCol = ("Raw %d" % i).encode("ascii")
29 if i % 2:
30 nullableCol = "Nullable %d" % i
31 else:
32 nullableCol = None
33 dataTuple = (i, stringCol, rawCol, fixedCharCol, nullableCol)
34 self.rawData.append(dataTuple)
35 self.dataByKey[i] = dataTuple
36
37 def testArrayWithIncreasedSize(self):
38 "test creating an array var and then increasing the internal size"
39 val = ["12345678901234567890"] * 3
40 arrayVar = self.cursor.arrayvar(str, len(val), 4)
41 arrayVar.setvalue(0, val)
42 self.assertEqual(arrayVar.getvalue(), val)
43
44 def testBindString(self):
45 "test binding in a string"
46 self.cursor.execute("""
47 select * from TestStrings
48 where StringCol = :value""",
49 value = "String 5")
50 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[5]])
51
52 def testBindDifferentVar(self):
53 "test binding a different variable on second execution"
54 retval_1 = self.cursor.var(cx_Oracle.STRING, 30)
55 retval_2 = self.cursor.var(cx_Oracle.STRING, 30)
56 self.cursor.execute("begin :retval := 'Called'; end;",
57 retval = retval_1)
58 self.assertEqual(retval_1.getvalue(), "Called")
59 self.cursor.execute("begin :retval := 'Called'; end;",
60 retval = retval_2)
61 self.assertEqual(retval_2.getvalue(), "Called")
62
63 def testExceedsNumElements(self):
64 "test exceeding the number of elements returns IndexError"
65 var = self.cursor.var(str)
66 self.assertRaises(IndexError, var.getvalue, 1)
67
68 def testBindStringAfterNumber(self):
69 "test binding in a string after setting input sizes to a number"
70 self.cursor.setinputsizes(value = cx_Oracle.NUMBER)
71 self.cursor.execute("""
72 select * from TestStrings
73 where StringCol = :value""",
74 value = "String 6")
75 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[6]])
76
77 def testBindStringArrayDirect(self):
78 "test binding in a string array"
79 returnValue = self.cursor.var(cx_Oracle.NUMBER)
80 array = [r[1] for r in self.rawData]
81 statement = """
82 begin
83 :returnValue := pkg_TestStringArrays.TestInArrays(
84 :integerValue, :array);
85 end;"""
86 self.cursor.execute(statement,
87 returnValue = returnValue,
88 integerValue = 5,
89 array = array)
90 self.assertEqual(returnValue.getvalue(), 86)
91 array = [ "String - %d" % i for i in range(15) ]
92 self.cursor.execute(statement,
93 integerValue = 8,
94 array = array)
95 self.assertEqual(returnValue.getvalue(), 163)
96
97 def testBindStringArrayBySizes(self):
98 "test binding in a string array (with setinputsizes)"
99 returnValue = self.cursor.var(cx_Oracle.NUMBER)
100 self.cursor.setinputsizes(array = [cx_Oracle.STRING, 10])
101 array = [r[1] for r in self.rawData]
102 self.cursor.execute("""
103 begin
104 :returnValue := pkg_TestStringArrays.TestInArrays(
105 :integerValue, :array);
106 end;""",
107 returnValue = returnValue,
108 integerValue = 6,
109 array = array)
110 self.assertEqual(returnValue.getvalue(), 87)
111
112 def testBindStringArrayByVar(self):
113 "test binding in a string array (with arrayvar)"
114 returnValue = self.cursor.var(cx_Oracle.NUMBER)
115 array = self.cursor.arrayvar(cx_Oracle.STRING, 10, 20)
116 array.setvalue(0, [r[1] for r in self.rawData])
117 self.cursor.execute("""
118 begin
119 :returnValue := pkg_TestStringArrays.TestInArrays(
120 :integerValue, :array);
121 end;""",
122 returnValue = returnValue,
123 integerValue = 7,
124 array = array)
125 self.assertEqual(returnValue.getvalue(), 88)
126
127 def testBindInOutStringArrayByVar(self):
128 "test binding in/out a string array (with arrayvar)"
129 array = self.cursor.arrayvar(cx_Oracle.STRING, 10, 100)
130 originalData = [r[1] for r in self.rawData]
131 expectedData = ["Converted element # %d originally had length %d" % \
132 (i, len(originalData[i - 1])) for i in range(1, 6)] + \
133 originalData[5:]
134 array.setvalue(0, originalData)
135 self.cursor.execute("""
136 begin
137 pkg_TestStringArrays.TestInOutArrays(:numElems, :array);
138 end;""",
139 numElems = 5,
140 array = array)
141 self.assertEqual(array.getvalue(), expectedData)
142
143 def testBindOutStringArrayByVar(self):
144 "test binding out a string array (with arrayvar)"
145 array = self.cursor.arrayvar(cx_Oracle.STRING, 6, 100)
146 expectedData = ["Test out element # %d" % i for i in range(1, 7)]
147 self.cursor.execute("""
148 begin
149 pkg_TestStringArrays.TestOutArrays(:numElems, :array);
150 end;""",
151 numElems = 6,
152 array = array)
153 self.assertEqual(array.getvalue(), expectedData)
154
155 def testBindRaw(self):
156 "test binding in a raw"
157 self.cursor.setinputsizes(value = cx_Oracle.BINARY)
158 self.cursor.execute("""
159 select * from TestStrings
160 where RawCol = :value""",
161 value = "Raw 4".encode("ascii"))
162 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[4]])
163
164 def testBindAndFetchRowid(self):
165 "test binding (and fetching) a rowid"
166 self.cursor.execute("""
167 select rowid
168 from TestStrings
169 where IntCol = 3""")
170 rowid, = self.cursor.fetchone()
171 self.cursor.execute("""
172 select *
173 from TestStrings
174 where rowid = :value""",
175 value = rowid)
176 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[3]])
177
178 def testBindAndFetchUniversalRowids(self):
179 "test binding (and fetching) universal rowids"
180 self.cursor.execute("truncate table TestUniversalRowids")
181 data = [
182 (1, "ABC" * 75, datetime.datetime(2017, 4, 11)),
183 (2, "DEF" * 80, datetime.datetime(2017, 4, 12))
184 ]
185 for row in data:
186 self.cursor.execute("""
187 insert into TestUniversalRowids
188 values (:1, :2, :3)""", row)
189 self.connection.commit()
190 self.cursor.execute("""
191 select rowid
192 from TestUniversalRowIds
193 order by IntCol""")
194 rowids = [r for r, in self.cursor]
195 fetchedData = []
196 for rowid in rowids:
197 self.cursor.execute("""
198 select *
199 from TestUniversalRowids
200 where rowid = :rid""",
201 rid = rowid)
202 fetchedData.extend(self.cursor.fetchall())
203 self.assertEqual(fetchedData, data)
204
205 def testBindNull(self):
206 "test binding in a null"
207 self.cursor.execute("""
208 select * from TestStrings
209 where StringCol = :value""",
210 value = None)
211 self.assertEqual(self.cursor.fetchall(), [])
212
213 def testBindOutSetInputSizesByType(self):
214 "test binding out with set input sizes defined (by type)"
215 vars = self.cursor.setinputsizes(value = cx_Oracle.STRING)
216 self.cursor.execute("""
217 begin
218 :value := 'TSI';
219 end;""")
220 self.assertEqual(vars["value"].getvalue(), "TSI")
221
222 def testBindOutSetInputSizesByInteger(self):
223 "test binding out with set input sizes defined (by integer)"
224 vars = self.cursor.setinputsizes(value = 30)
225 self.cursor.execute("""
226 begin
227 :value := 'TSI (I)';
228 end;""")
229 self.assertEqual(vars["value"].getvalue(), "TSI (I)")
230
231 def testBindInOutSetInputSizesByType(self):
232 "test binding in/out with set input sizes defined (by type)"
233 vars = self.cursor.setinputsizes(value = cx_Oracle.STRING)
234 self.cursor.execute("""
235 begin
236 :value := :value || ' TSI';
237 end;""",
238 value = "InVal")
239 self.assertEqual(vars["value"].getvalue(), "InVal TSI")
240
241 def testBindInOutSetInputSizesByInteger(self):
242 "test binding in/out with set input sizes defined (by integer)"
243 vars = self.cursor.setinputsizes(value = 30)
244 self.cursor.execute("""
245 begin
246 :value := :value || ' TSI (I)';
247 end;""",
248 value = "InVal")
249 self.assertEqual(vars["value"].getvalue(), "InVal TSI (I)")
250
251 def testBindOutVar(self):
252 "test binding out with cursor.var() method"
253 var = self.cursor.var(cx_Oracle.STRING)
254 self.cursor.execute("""
255 begin
256 :value := 'TSI (VAR)';
257 end;""",
258 value = var)
259 self.assertEqual(var.getvalue(), "TSI (VAR)")
260
261 def testBindInOutVarDirectSet(self):
262 "test binding in/out with cursor.var() method"
263 var = self.cursor.var(cx_Oracle.STRING)
264 var.setvalue(0, "InVal")
265 self.cursor.execute("""
266 begin
267 :value := :value || ' TSI (VAR)';
268 end;""",
269 value = var)
270 self.assertEqual(var.getvalue(), "InVal TSI (VAR)")
271
272 def testBindLongString(self):
273 "test that binding a long string succeeds"
274 self.cursor.setinputsizes(bigString = cx_Oracle.LONG_STRING)
275 self.cursor.execute("""
276 declare
277 t_Temp varchar2(20000);
278 begin
279 t_Temp := :bigString;
280 end;""",
281 bigString = "X" * 10000)
282
283 def testBindLongStringAfterSettingSize(self):
284 "test that setinputsizes() returns a long variable"
285 var = self.cursor.setinputsizes(test = 90000)["test"]
286 inString = "1234567890" * 9000
287 var.setvalue(0, inString)
288 outString = var.getvalue()
289 self.assertEqual(inString, outString,
290 "output does not match: in was %d, out was %d" % \
291 (len(inString), len(outString)))
292
293 def testCursorDescription(self):
294 "test cursor description is accurate"
295 self.cursor.execute("select * from TestStrings")
296 self.assertEqual(self.cursor.description,
297 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
298 ('STRINGCOL', cx_Oracle.STRING, 20,
299 20 * TestEnv.GetCharSetRatio(), None,
300 None, 0),
301 ('RAWCOL', cx_Oracle.BINARY, 30, 30, None, None, 0),
302 ('FIXEDCHARCOL', cx_Oracle.FIXED_CHAR, 40,
303 40 * TestEnv.GetCharSetRatio(),
304 None, None, 0),
305 ('NULLABLECOL', cx_Oracle.STRING, 50,
306 50 * TestEnv.GetCharSetRatio(), None,
307 None, 1) ])
308
309 def testFetchAll(self):
310 "test that fetching all of the data returns the correct results"
311 self.cursor.execute("select * From TestStrings order by IntCol")
312 self.assertEqual(self.cursor.fetchall(), self.rawData)
313 self.assertEqual(self.cursor.fetchall(), [])
314
315 def testFetchMany(self):
316 "test that fetching data in chunks returns the correct results"
317 self.cursor.execute("select * From TestStrings order by IntCol")
318 self.assertEqual(self.cursor.fetchmany(3), self.rawData[0:3])
319 self.assertEqual(self.cursor.fetchmany(2), self.rawData[3:5])
320 self.assertEqual(self.cursor.fetchmany(4), self.rawData[5:9])
321 self.assertEqual(self.cursor.fetchmany(3), self.rawData[9:])
322 self.assertEqual(self.cursor.fetchmany(3), [])
323
324 def testFetchOne(self):
325 "test that fetching a single row returns the correct results"
326 self.cursor.execute("""
327 select *
328 from TestStrings
329 where IntCol in (3, 4)
330 order by IntCol""")
331 self.assertEqual(self.cursor.fetchone(), self.dataByKey[3])
332 self.assertEqual(self.cursor.fetchone(), self.dataByKey[4])
333 self.assertEqual(self.cursor.fetchone(), None)
334
335 def testSupplementalCharacters(self):
336 "test that binding and fetching supplemental charcters works correctly"
337 self.cursor.execute("""
338 select value
339 from nls_database_parameters
340 where parameter = 'NLS_CHARACTERSET'""")
341 charset, = self.cursor.fetchone()
342 if charset != "AL32UTF8":
343 self.skipTest("Database character set must be AL32UTF8")
344 supplementalChars = "𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿 𠸎 𠸏 𠹷 𠺝 " \
345 "𠺢 𠻗 𠻹 𠻺 𠼭 𠼮 𠽌 𠾴 𠾼 𠿪 𡁜 𡁯 𡁵 𡁶 𡁻 𡃁 𡃉 𡇙 𢃇 " \
346 "𢞵 𢫕 𢭃 𢯊 𢱑 𢱕 𢳂 𢴈 𢵌 𢵧 𢺳 𣲷 𤓓 𤶸 𤷪 𥄫 𦉘 𦟌 𦧲 " \
347 "𦧺 𧨾 𨅝 𨈇 𨋢 𨳊 𨳍 𨳒 𩶘"
348 self.cursor.execute("truncate table TestTempTable")
349 self.cursor.execute("""
350 insert into TestTempTable (IntCol, StringCol)
351 values (:1, :2)""",
352 (1, supplementalChars))
353 self.connection.commit()
354 self.cursor.execute("select StringCol from TestTempTable")
355 value, = self.cursor.fetchone()
356 self.assertEqual(value, supplementalChars)
357
358 def testBindTwiceWithLargeStringSecond(self):
359 "test binding twice with a larger string the second time"
360 self.cursor.execute("truncate table TestTempTable")
361 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
362 shortString = "short string"
363 longString = "long string " * 30
364 self.cursor.execute(sql, (1, shortString))
365 self.cursor.execute(sql, (2, longString))
366 self.connection.commit()
367 self.cursor.execute("""
368 select IntCol, StringCol
369 from TestTempTable
370 order by IntCol""")
371 self.assertEqual(self.cursor.fetchall(),
372 [(1, shortString), (2, longString)])
373
374 def testIssue50(self):
375 "test issue 50 - avoid error ORA-24816"
376 cursor = self.connection.cursor()
377 try:
378 cursor.execute("drop table issue_50 purge")
379 except cx_Oracle.DatabaseError:
380 pass
381 cursor.execute("""
382 create table issue_50 (
383 Id number(11) primary key,
384 Str1 nvarchar2(256),
385 Str2 nvarchar2(256),
386 Str3 nvarchar2(256),
387 NClob1 nclob,
388 NClob2 nclob
389 )""")
390 idVar = cursor.var(cx_Oracle.NUMBER)
391 cursor.execute("""
392 insert into issue_50 (Id, Str2, Str3, NClob1, NClob2, Str1)
393 values (:arg0, :arg1, :arg2, :arg3, :arg4, :arg5)
394 returning id into :arg6""",
395 [1, '555a4c78', 'f319ef0e', '23009914', '', '', idVar])
396 cursor = self.connection.cursor()
397 cursor.execute("""
398 insert into issue_50 (Id, Str2, Str3, NClob1, NClob2, Str1)
399 values (:arg0, :arg1, :arg2, :arg3, :arg4, :arg5)
400 returning id into :arg6""",
401 [2, u'd5ff845a', u'94275767', u'bf161ff6', u'', u'', idVar])
402 cursor.execute("drop table issue_50 purge")
403
404 def testSetRowidToString(self):
405 "test assigning a string to rowid"
406 var = self.cursor.var(cx_Oracle.ROWID)
407 self.assertRaises(cx_Oracle.NotSupportedError, var.setvalue, 0,
408 "ABDHRYTHFJGKDKKDH")
409
410 def testShortXMLAsString(self):
411 "test fetching XMLType object as a string"
412 self.cursor.execute("""
413 select XMLElement("string", stringCol)
414 from TestStrings
415 where intCol = 1""")
416 actualValue, = self.cursor.fetchone()
417 expectedValue = "<string>String 1</string>"
418 self.assertEqual(actualValue, expectedValue)
419
420 def testLongXMLAsString(self):
421 "test inserting and fetching an XMLType object (1K) as a string"
422 chars = string.ascii_uppercase + string.ascii_lowercase
423 randomString = ''.join(random.choice(chars) for _ in range(1024))
424 intVal = 200
425 xmlString = '<data>' + randomString + '</data>'
426 self.cursor.execute("""
427 insert into TestXML (IntCol, XMLCol)
428 values (:1, :2)""", (intVal, xmlString))
429 self.cursor.execute("select XMLCol from TestXML where intCol = :1",
430 (intVal,))
431 actualValue, = self.cursor.fetchone()
432 self.assertEqual(actualValue.strip(), xmlString)
433
434 if __name__ == "__main__":
435 TestEnv.RunTestCases()
436
+0
-106
test/Subscription.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """Module for testing subscriptions."""
5
6 import TestEnv
7
8 import cx_Oracle
9 import threading
10
11 class SubscriptionData(object):
12
13 def __init__(self, numMessagesExpected):
14 self.condition = threading.Condition()
15 self.numMessagesExpected = numMessagesExpected
16 self.numMessagesReceived = 0
17 self.tableOperations = []
18 self.rowOperations = []
19 self.rowids = []
20
21 def CallbackHandler(self, message):
22 if message.type != cx_Oracle.EVENT_DEREG:
23 table, = message.tables
24 self.tableOperations.append(table.operation)
25 for row in table.rows:
26 self.rowOperations.append(row.operation)
27 self.rowids.append(row.rowid)
28 self.numMessagesReceived += 1
29 if message.type == cx_Oracle.EVENT_DEREG or \
30 self.numMessagesReceived == self.numMessagesExpected:
31 self.condition.acquire()
32 self.condition.notify()
33 self.condition.release()
34
35
36 class TestCase(TestEnv.BaseTestCase):
37
38 def testSubscription(self):
39 "test Subscription for insert, update, delete and truncate"
40 self.cursor.execute("truncate table TestTempTable")
41
42 # expected values
43 tableOperations = [ cx_Oracle.OPCODE_INSERT, cx_Oracle.OPCODE_UPDATE,
44 cx_Oracle.OPCODE_INSERT, cx_Oracle.OPCODE_DELETE,
45 cx_Oracle.OPCODE_ALTER | cx_Oracle.OPCODE_ALLROWS ]
46 rowOperations = [ cx_Oracle.OPCODE_INSERT, cx_Oracle.OPCODE_UPDATE,
47 cx_Oracle.OPCODE_INSERT, cx_Oracle.OPCODE_DELETE ]
48 rowids = []
49
50 # set up subscription
51 data = SubscriptionData(5)
52 connection = TestEnv.GetConnection(threaded=True, events=True)
53 sub = connection.subscribe(callback = data.CallbackHandler,
54 timeout = 10, qos = cx_Oracle.SUBSCR_QOS_ROWIDS)
55 sub.registerquery("select * from TestTempTable")
56 connection.autocommit = True
57 cursor = connection.cursor()
58
59 # insert statement
60 cursor.execute("""
61 insert into TestTempTable (IntCol, StringCol)
62 values (1, 'test')""")
63 cursor.execute("select rowid from TestTempTable where IntCol = 1")
64 rowids.extend(r for r, in cursor)
65
66 # update statement
67 cursor.execute("""
68 update TestTempTable set
69 StringCol = 'update'
70 where IntCol = 1""")
71 cursor.execute("select rowid from TestTempTable where IntCol = 1")
72 rowids.extend(r for r, in cursor)
73
74 # second insert statement
75 cursor.execute("""
76 insert into TestTempTable (IntCol, StringCol)
77 values (2, 'test2')""")
78 cursor.execute("select rowid from TestTempTable where IntCol = 2")
79 rowids.extend(r for r, in cursor)
80
81 # delete statement
82 cursor.execute("delete TestTempTable where IntCol = 2")
83 rowids.append(rowids[-1])
84
85 # truncate table
86 cursor.execute("truncate table TestTempTable")
87
88 # wait for all messages to be sent
89 data.condition.acquire()
90 data.condition.wait(10)
91
92 # verify the correct messages were sent
93 self.assertEqual(data.tableOperations, tableOperations)
94 self.assertEqual(data.rowOperations, rowOperations)
95 self.assertEqual(data.rowids, rowids)
96
97 # test string format of subscription object is as expected
98 fmt = "<cx_Oracle.Subscription on <cx_Oracle.Connection to %s@%s>>"
99 expectedValue = fmt % \
100 (TestEnv.GetMainUser(), TestEnv.GetConnectString())
101 self.assertEqual(str(sub), expectedValue)
102
103 if __name__ == "__main__":
104 TestEnv.RunTestCases()
105
+0
-172
test/TestEnv.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 #------------------------------------------------------------------------------
10 # Sets the environment used by the cx_Oracle test suite. Production
11 # applications should consider using External Authentication to
12 # avoid hard coded credentials.
13 #
14 # You can set values in environment variables to bypass having the test suite
15 # request the information it requires.
16 #
17 # CX_ORACLE_TEST_MAIN_USER: user used for most samples
18 # CX_ORACLE_TEST_MAIN_PASSWORD: password of user used for most samples
19 # CX_ORACLE_TEST_PROXY_USER: user for testing proxy connections
20 # CX_ORACLE_TEST_PROXY_PASSWORD: password of user for proxying
21 # CX_ORACLE_TEST_CONNECT_STRING: connect string
22 # CX_ORACLE_TEST_SYSDBA_USER: SYSDBA user for setting up test suite
23 # CX_ORACLE_TEST_SYSDBA_PASSWORD: SYSDBA password for setting up test suite
24 #
25 # CX_ORACLE_TEST_CONNECT_STRING can be set to an Easy Connect string, or a
26 # Net Service Name from a tnsnames.ora file or external naming service,
27 # or it can be the name of a local Oracle database instance.
28 #
29 # If cx_Oracle is using Instant Client, then an Easy Connect string is
30 # generally appropriate. The syntax is:
31 #
32 # [//]host_name[:port][/service_name][:server_type][/instance_name]
33 #
34 # Commonly just the host_name and service_name are needed
35 # e.g. "localhost/orclpdb" or "localhost/XE"
36 #
37 # If using a tnsnames.ora file, the file can be in a default
38 # location such as $ORACLE_HOME/network/admin/tnsnames.ora or
39 # /etc/tnsnames.ora. Alternatively set the TNS_ADMIN environment
40 # variable and put the file in $TNS_ADMIN/tnsnames.ora.
41 #------------------------------------------------------------------------------
42
43 from __future__ import print_function
44
45 import cx_Oracle
46 import getpass
47 import os
48 import sys
49 import unittest
50
51 # default values
52 DEFAULT_MAIN_USER = "pythontest"
53 DEFAULT_PROXY_USER = "pythontestproxy"
54 DEFAULT_CONNECT_STRING = "localhost/orclpdb"
55
56 # dictionary containing all parameters; these are acquired as needed by the
57 # methods below (which should be used instead of consulting this dictionary
58 # directly) and then stored so that a value is not requested more than once
59 PARAMETERS = {}
60
61 def GetValue(name, label, defaultValue=""):
62 value = PARAMETERS.get(name)
63 if value is not None:
64 return value
65 envName = "CX_ORACLE_TEST_" + name
66 value = os.environ.get(envName)
67 if value is None:
68 if defaultValue:
69 label += " [%s]" % defaultValue
70 label += ": "
71 if defaultValue:
72 value = input(label).strip()
73 else:
74 value = getpass.getpass(label)
75 if not value:
76 value = defaultValue
77 PARAMETERS[name] = value
78 return value
79
80 def GetMainUser():
81 return GetValue("MAIN_USER", "Main User Name", DEFAULT_MAIN_USER)
82
83 def GetMainPassword():
84 return GetValue("MAIN_PASSWORD", "Password for %s" % GetMainUser())
85
86 def GetProxyUser():
87 return GetValue("PROXY_USER", "Proxy User Name", DEFAULT_PROXY_USER)
88
89 def GetProxyPassword():
90 return GetValue("PROXY_PASSWORD", "Password for %s" % GetProxyUser())
91
92 def GetConnectString():
93 return GetValue("CONNECT_STRING", "Connect String", DEFAULT_CONNECT_STRING)
94
95 def GetCharSetRatio():
96 value = PARAMETERS.get("CS_RATIO")
97 if value is None:
98 connection = GetConnection()
99 cursor = connection.cursor()
100 cursor.execute("select 'X' from dual")
101 col, = cursor.description
102 value = col[3]
103 PARAMETERS["CS_RATIO"] = value
104 return value
105
106 def GetSysdbaConnectString():
107 sysdbaUser = GetValue("SYSDBA_USER", "SYSDBA user", "sys")
108 sysdbaPassword = GetValue("SYSDBA_PASSWORD",
109 "Password for %s" % sysdbaUser)
110 return "%s/%s@%s" % (sysdbaUser, sysdbaPassword, GetConnectString())
111
112 def RunSqlScript(conn, scriptName, **kwargs):
113 statementParts = []
114 cursor = conn.cursor()
115 replaceValues = [("&" + k + ".", v) for k, v in kwargs.items()] + \
116 [("&" + k, v) for k, v in kwargs.items()]
117 scriptDir = os.path.dirname(os.path.abspath(sys.argv[0]))
118 fileName = os.path.join(scriptDir, "sql", scriptName + "Exec.sql")
119 for line in open(fileName):
120 if line.strip() == "/":
121 statement = "".join(statementParts).strip()
122 if statement:
123 for searchValue, replaceValue in replaceValues:
124 statement = statement.replace(searchValue, replaceValue)
125 cursor.execute(statement)
126 statementParts = []
127 else:
128 statementParts.append(line)
129 cursor.execute("""
130 select name, type, line, position, text
131 from dba_errors
132 where owner = upper(:owner)
133 order by name, type, line, position""",
134 owner = GetMainUser())
135 prevName = prevObjType = None
136 for name, objType, lineNum, position, text in cursor:
137 if name != prevName or objType != prevObjType:
138 print("%s (%s)" % (name, objType))
139 prevName = name
140 prevObjType = objType
141 print(" %s/%s %s" % (lineNum, position, text))
142
143 def RunTestCases():
144 unittest.main(testRunner=unittest.TextTestRunner(verbosity=2))
145
146 def GetConnection(**kwargs):
147 return cx_Oracle.connect(GetMainUser(), GetMainPassword(),
148 GetConnectString(), encoding="UTF-8", nencoding="UTF-8", **kwargs)
149
150 def GetPool(user=None, password=None, **kwargs):
151 if user is None:
152 user = GetMainUser()
153 if password is None:
154 password = GetMainPassword()
155 return cx_Oracle.SessionPool(user, password, GetConnectString(),
156 encoding="UTF-8", nencoding="UTF-8", **kwargs)
157
158 def GetClientVersion():
159 return cx_Oracle.clientversion()
160
161 class BaseTestCase(unittest.TestCase):
162
163 def setUp(self):
164 self.connection = GetConnection()
165 self.cursor = self.connection.cursor()
166
167 def tearDown(self):
168 self.connection.close()
169 del self.cursor
170 del self.connection
171
+0
-145
test/TimestampVar.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Module for testing timestamp variables."""
10
11 import TestEnv
12
13 import cx_Oracle
14 import time
15
16 class TestCase(TestEnv.BaseTestCase):
17
18 def setUp(self):
19 TestEnv.BaseTestCase.setUp(self)
20 self.rawData = []
21 self.dataByKey = {}
22 for i in range(1, 11):
23 timeTuple = (2002, 12, 9, 0, 0, 0, 0, 0, -1)
24 timeInTicks = time.mktime(timeTuple) + i * 86400
25 dateValue = cx_Oracle.TimestampFromTicks(int(timeInTicks))
26 strValue = str(i * 50)
27 fsecond = int(strValue + "0" * (6 - len(strValue)))
28 dateCol = cx_Oracle.Timestamp(dateValue.year, dateValue.month,
29 dateValue.day, dateValue.hour, dateValue.minute,
30 i * 2, fsecond)
31 if i % 2:
32 timeInTicks = time.mktime(timeTuple) + i * 86400 + 86400
33 dateValue = cx_Oracle.TimestampFromTicks(int(timeInTicks))
34 strValue = str(i * 125)
35 fsecond = int(strValue + "0" * (6 - len(strValue)))
36 nullableCol = cx_Oracle.Timestamp(dateValue.year,
37 dateValue.month, dateValue.day, dateValue.hour,
38 dateValue.minute, i * 3, fsecond)
39 else:
40 nullableCol = None
41 tuple = (i, dateCol, nullableCol)
42 self.rawData.append(tuple)
43 self.dataByKey[i] = tuple
44
45 def testBindTimestamp(self):
46 "test binding in a timestamp"
47 self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP)
48 self.cursor.execute("""
49 select * from TestTimestamps
50 where TimestampCol = :value""",
51 value = cx_Oracle.Timestamp(2002, 12, 14, 0, 0, 10, 250000))
52 self.assertEqual(self.cursor.fetchall(), [self.dataByKey[5]])
53
54 def testBindNull(self):
55 "test binding in a null"
56 self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP)
57 self.cursor.execute("""
58 select * from TestTimestamps
59 where TimestampCol = :value""",
60 value = None)
61 self.assertEqual(self.cursor.fetchall(), [])
62
63 def testBindOutSetInputSizes(self):
64 "test binding out with set input sizes defined"
65 vars = self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP)
66 self.cursor.execute("""
67 begin
68 :value := to_timestamp('20021209', 'YYYYMMDD');
69 end;""")
70 self.assertEqual(vars["value"].getvalue(),
71 cx_Oracle.Timestamp(2002, 12, 9))
72
73 def testBindInOutSetInputSizes(self):
74 "test binding in/out with set input sizes defined"
75 vars = self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP)
76 self.cursor.execute("""
77 begin
78 :value := :value + 5.25;
79 end;""",
80 value = cx_Oracle.Timestamp(2002, 12, 12, 10, 0, 0))
81 self.assertEqual(vars["value"].getvalue(),
82 cx_Oracle.Timestamp(2002, 12, 17, 16, 0, 0))
83
84 def testBindOutVar(self):
85 "test binding out with cursor.var() method"
86 var = self.cursor.var(cx_Oracle.TIMESTAMP)
87 self.cursor.execute("""
88 begin
89 :value := to_date('20021231 12:31:00',
90 'YYYYMMDD HH24:MI:SS');
91 end;""",
92 value = var)
93 self.assertEqual(var.getvalue(),
94 cx_Oracle.Timestamp(2002, 12, 31, 12, 31, 0))
95
96 def testBindInOutVarDirectSet(self):
97 "test binding in/out with cursor.var() method"
98 var = self.cursor.var(cx_Oracle.TIMESTAMP)
99 var.setvalue(0, cx_Oracle.Timestamp(2002, 12, 9, 6, 0, 0))
100 self.cursor.execute("""
101 begin
102 :value := :value + 5.25;
103 end;""",
104 value = var)
105 self.assertEqual(var.getvalue(),
106 cx_Oracle.Timestamp(2002, 12, 14, 12, 0, 0))
107
108 def testCursorDescription(self):
109 "test cursor description is accurate"
110 self.cursor.execute("select * from TestTimestamps")
111 self.assertEqual(self.cursor.description,
112 [ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
113 ('TIMESTAMPCOL', cx_Oracle.TIMESTAMP, 23, None, 0, 6, 0),
114 ('NULLABLECOL', cx_Oracle.TIMESTAMP, 23, None, 0, 6, 1) ])
115
116 def testFetchAll(self):
117 "test that fetching all of the data returns the correct results"
118 self.cursor.execute("select * From TestTimestamps order by IntCol")
119 self.assertEqual(self.cursor.fetchall(), self.rawData)
120 self.assertEqual(self.cursor.fetchall(), [])
121
122 def testFetchMany(self):
123 "test that fetching data in chunks returns the correct results"
124 self.cursor.execute("select * From TestTimestamps order by IntCol")
125 self.assertEqual(self.cursor.fetchmany(3), self.rawData[0:3])
126 self.assertEqual(self.cursor.fetchmany(2), self.rawData[3:5])
127 self.assertEqual(self.cursor.fetchmany(4), self.rawData[5:9])
128 self.assertEqual(self.cursor.fetchmany(3), self.rawData[9:])
129 self.assertEqual(self.cursor.fetchmany(3), [])
130
131 def testFetchOne(self):
132 "test that fetching a single row returns the correct results"
133 self.cursor.execute("""
134 select *
135 from TestTimestamps
136 where IntCol in (3, 4)
137 order by IntCol""")
138 self.assertEqual(self.cursor.fetchone(), self.dataByKey[3])
139 self.assertEqual(self.cursor.fetchone(), self.dataByKey[4])
140 self.assertEqual(self.cursor.fetchone(), None)
141
142 if __name__ == "__main__":
143 TestEnv.RunTestCases()
144
2020 /
2121
2222 create user &main_user identified by &main_password
23 quota unlimited on users
24 default tablespace users
2523 /
2624
2725 create user &proxy_user identified by &proxy_password
3836 create procedure,
3937 create type,
4038 select any dictionary,
41 change notification
39 change notification,
40 unlimited tablespace
4241 to &main_user
4342 /
4443
178177 create table &main_user..TestLongs (
179178 IntCol number(9) not null,
180179 LongCol long not null
181 )
180 ) nocompress
182181 /
183182
184183 create table &main_user..TestLongRaws (
185184 IntCol number(9) not null,
186185 LongRawCol long raw not null
187 )
186 ) nocompress
188187 /
189188
190189 create table &main_user..TestTempTable (
245244 )
246245 /
247246
247 create table &main_user..PlsqlSessionCallbacks (
248 RequestedTag varchar2(250),
249 ActualTag varchar2(250),
250 FixupTimestamp timestamp
251 )
252 /
253
254 declare
255 t_Version number;
256 begin
257
258 select to_number(substr(version, 1, instr(version, '.') - 1))
259 into t_Version
260 from product_component_version
261 where product like 'Oracle Database%';
262
263 if t_Version >= 21 then
264 execute immediate 'create table &main_user..TestJson (' ||
265 ' IntCol number(9) not null,' ||
266 ' JsonCol json not null' ||
267 ')';
268 end if;
269
270 end;
271 /
272
248273 -- create queue table and queues for testing advanced queuing
249274 begin
250 dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE',
275 dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE_TAB',
251276 '&main_user..UDT_BOOK');
252 dbms_aqadm.create_queue('&main_user..BOOKS', '&main_user..BOOK_QUEUE');
253 dbms_aqadm.start_queue('&main_user..BOOKS');
277 dbms_aqadm.create_queue('&main_user..TEST_BOOK_QUEUE',
278 '&main_user..BOOK_QUEUE_TAB');
279 dbms_aqadm.start_queue('&main_user..TEST_BOOK_QUEUE');
280
281 dbms_aqadm.create_queue_table('&main_user..RAW_QUEUE_TAB', 'RAW');
282 dbms_aqadm.create_queue('&main_user..TEST_RAW_QUEUE',
283 '&main_user..RAW_QUEUE_TAB');
284 dbms_aqadm.start_queue('&main_user..TEST_RAW_QUEUE');
254285 end;
255286 /
256287
427458 create procedure &main_user..proc_TestNoArgs as
428459 begin
429460 null;
461 end;
462 /
463
464 -- create procedure for testing refcursor
465 create procedure &main_user..myrefcursorproc (
466 a_RefCursor out sys_refcursor
467 ) as
468 begin
469 open a_RefCursor for
470 select *
471 from TestTempTable;
430472 end;
431473 /
432474
917959 StringValue varchar2(30),
918960 DateValue date,
919961 TimestampValue timestamp,
920 BooleanValue boolean
962 BooleanValue boolean,
963 PlsIntegerValue pls_integer,
964 BinaryIntegerValue binary_integer
921965 );
922966
923967 type udt_RecordArray is table of udt_Record index by binary_integer;
9571001 ''', ''YYYY-MM-DD HH24:MI:SS'')' end || ', ' ||
9581002 case when a_Value.BooleanValue is null then 'null'
9591003 when a_Value.BooleanValue then 'true'
960 else 'false' end || ')';
1004 else 'false' end || ', ' ||
1005 nvl(to_char(a_Value.PlsIntegerValue), 'null') || ', ' ||
1006 nvl(to_char(a_Value.BinaryIntegerValue), 'null') || ')';
9611007 end;
9621008
9631009 procedure TestOut (
9701016 a_Value.TimestampValue := to_timestamp('20160216 18:23:55',
9711017 'YYYYMMDD HH24:MI:SS');
9721018 a_Value.BooleanValue := true;
1019 a_Value.PlsIntegerValue := 45;
1020 a_Value.BinaryIntegerValue := 10;
9731021 end;
9741022
9751023 function TestInArrays (
9891037 end;
9901038 /
9911039
1040 create or replace package &main_user..pkg_SessionCallback as
1041
1042 procedure TheCallback (
1043 a_RequestedTag varchar2,
1044 a_ActualTag varchar2
1045 );
1046
1047 end;
1048 /
1049
1050 create or replace package body &main_user..pkg_SessionCallback as
1051
1052 type udt_Properties is table of varchar2(64) index by varchar2(64);
1053
1054 procedure LogCall (
1055 a_RequestedTag varchar2,
1056 a_ActualTag varchar2
1057 ) is
1058 pragma autonomous_transaction;
1059 begin
1060 insert into PlsqlSessionCallbacks
1061 values (a_RequestedTag, a_ActualTag, systimestamp);
1062 commit;
1063 end;
1064
1065 procedure ParseProperty (
1066 a_Property varchar2,
1067 a_Name out nocopy varchar2,
1068 a_Value out nocopy varchar2
1069 ) is
1070 t_Pos number;
1071 begin
1072 t_Pos := instr(a_Property, '=');
1073 if t_Pos = 0 then
1074 raise_application_error(-20000, 'Tag must contain key=value pairs');
1075 end if;
1076 a_Name := substr(a_Property, 1, t_Pos - 1);
1077 a_Value := substr(a_Property, t_Pos + 1);
1078 end;
1079
1080 procedure SetProperty (
1081 a_Name varchar2,
1082 a_Value varchar2
1083 ) is
1084 t_ValidValues udt_Properties;
1085 begin
1086 if a_Name = 'TIME_ZONE' then
1087 t_ValidValues('UTC') := 'UTC';
1088 t_ValidValues('MST') := '-07:00';
1089 elsif a_Name = 'NLS_DATE_FORMAT' then
1090 t_ValidValues('SIMPLE') := 'YYYY-MM-DD HH24:MI';
1091 t_ValidValues('FULL') := 'YYYY-MM-DD HH24:MI:SS';
1092 else
1093 raise_application_error(-20000, 'Unsupported session setting');
1094 end if;
1095 if not t_ValidValues.exists(a_Value) then
1096 raise_application_error(-20000, 'Unsupported session setting');
1097 end if;
1098 execute immediate
1099 'ALTER SESSION SET ' || a_Name || '=''' ||
1100 t_ValidValues(a_Value) || '''';
1101 end;
1102
1103 procedure ParseTag (
1104 a_Tag varchar2,
1105 a_Properties out nocopy udt_Properties
1106 ) is
1107 t_PropertyName varchar2(64);
1108 t_PropertyValue varchar2(64);
1109 t_StartPos number;
1110 t_EndPos number;
1111 begin
1112 t_StartPos := 1;
1113 while t_StartPos < length(a_Tag) loop
1114 t_EndPos := instr(a_Tag, ';', t_StartPos);
1115 if t_EndPos = 0 then
1116 t_EndPos := length(a_Tag) + 1;
1117 end if;
1118 ParseProperty(substr(a_Tag, t_StartPos, t_EndPos - t_StartPos),
1119 t_PropertyName, t_PropertyValue);
1120 a_Properties(t_PropertyName) := t_PropertyValue;
1121 t_StartPos := t_EndPos + 1;
1122 end loop;
1123 end;
1124
1125 procedure TheCallback (
1126 a_RequestedTag varchar2,
1127 a_ActualTag varchar2
1128 ) is
1129 t_RequestedProps udt_Properties;
1130 t_ActualProps udt_Properties;
1131 t_PropertyName varchar2(64);
1132 begin
1133 LogCall(a_RequestedTag, a_ActualTag);
1134 ParseTag(a_RequestedTag, t_RequestedProps);
1135 ParseTag(a_ActualTag, t_ActualProps);
1136 t_PropertyName := t_RequestedProps.first;
1137 while t_PropertyName is not null loop
1138 if not t_ActualProps.exists(t_PropertyName) or
1139 t_ActualProps(t_PropertyName) !=
1140 t_RequestedProps(t_PropertyName) then
1141 SetProperty(t_PropertyName, t_RequestedProps(t_PropertyName));
1142 end if;
1143 t_PropertyName := t_RequestedProps.next(t_PropertyName);
1144 end loop;
1145 end;
1146
1147 end;
1148 /
1149
+0
-78
test/test.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Runs all defined unit tests."""
10
11 from __future__ import print_function
12
13 import cx_Oracle
14 import os
15 import sys
16 import TestEnv
17 import unittest
18
19 # display version of cx_Oracle and Oracle client for which tests are being run
20 print("Running tests for cx_Oracle version", cx_Oracle.version,
21 "built at", cx_Oracle.buildtime)
22 print("File:", cx_Oracle.__file__)
23 print("Client Version:", ".".join(str(i) for i in cx_Oracle.clientversion()))
24 sys.stdout.flush()
25
26 # verify that we can connect to the database and display database version
27 connection = TestEnv.GetConnection()
28 print("Server Version:", connection.version)
29 sys.stdout.flush()
30
31 # define test cases to run
32 moduleNames = [
33 "Module",
34 "Connection",
35 "Cursor",
36 "CursorVar",
37 "DateTimeVar",
38 "DMLReturning",
39 "Error",
40 "IntervalVar",
41 "LobVar",
42 "LongVar",
43 "NCharVar",
44 "NumberVar",
45 "ObjectVar",
46 "SessionPool",
47 "StringVar",
48 "TimestampVar",
49 "AQ",
50 "Rowid",
51 "Subscription"
52 ]
53 clientVersion = cx_Oracle.clientversion()
54 if clientVersion[:2] >= (12, 1):
55 moduleNames.append("BooleanVar")
56 moduleNames.append("Features12_1")
57 if clientVersion[:2] >= (18, 3):
58 moduleNames.append("SodaDatabase")
59 moduleNames.append("SodaCollection")
60
61 # run all test cases
62 failures = []
63 loader = unittest.TestLoader()
64 runner = unittest.TextTestRunner(verbosity = 2)
65 for name in moduleNames:
66 print()
67 print("Running tests in", name)
68 tests = loader.loadTestsFromName(name + ".TestCase")
69 result = runner.run(tests)
70 if not result.wasSuccessful():
71 failures.append(name)
72 if failures:
73 print("***** Some tests in the following modules failed. *****")
74 for name in failures:
75 print(" %s" % name)
76 sys.exit(1)
77
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 1000 - Module for testing top-level module methods
6 """
7
8 import test_env
9
10 import cx_Oracle as oracledb
11 import datetime
12 import time
13
14 class TestCase(test_env.BaseTestCase):
15 requires_connection = False
16
17 def test_1000_date_from_ticks(self):
18 "1000 - test DateFromTicks()"
19 today = datetime.datetime.today()
20 timestamp = time.mktime(today.timetuple())
21 date = oracledb.DateFromTicks(timestamp)
22 self.assertEqual(date, today.date())
23
24 def test_1001_future_obj(self):
25 "1001 - test management of __future__ object"
26 self.assertEqual(oracledb.__future__.dummy, None)
27 oracledb.__future__.dummy = "Unimportant"
28 self.assertEqual(oracledb.__future__.dummy, None)
29
30 def test_1002_timestamp_from_ticks(self):
31 "1002 - test TimestampFromTicks()"
32 timestamp = time.mktime(datetime.datetime.today().timetuple())
33 today = datetime.datetime.fromtimestamp(timestamp)
34 date = oracledb.TimestampFromTicks(timestamp)
35 self.assertEqual(date, today)
36
37 def test_1003_unsupported_functions(self):
38 "1003 - test unsupported time functions"
39 self.assertRaises(oracledb.NotSupportedError, oracledb.Time, 12, 0, 0)
40 self.assertRaises(oracledb.NotSupportedError, oracledb.TimeFromTicks,
41 100)
42
43 if __name__ == "__main__":
44 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 1100 - Module for testing connections
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import random
17 import string
18 import threading
19
20 class TestCase(test_env.BaseTestCase):
21 requires_connection = False
22
23 def __connect_and_drop(self):
24 """Connect to the database, perform a query and drop the connection."""
25 connection = test_env.get_connection(threaded=True)
26 cursor = connection.cursor()
27 cursor.execute("select count(*) from TestNumbers")
28 count, = cursor.fetchone()
29 self.assertEqual(count, 10)
30
31 def __verify_args(self, connection):
32 self.assertEqual(connection.username, test_env.get_main_user(),
33 "user name differs")
34 self.assertEqual(connection.tnsentry, test_env.get_connect_string(),
35 "tnsentry differs")
36 self.assertEqual(connection.dsn, test_env.get_connect_string(),
37 "dsn differs")
38
39 def __verify_attributes(self, connection, attrName, value, sql):
40 setattr(connection, attrName, value)
41 cursor = connection.cursor()
42 cursor.execute(sql)
43 result, = cursor.fetchone()
44 self.assertEqual(result, value, "%s value mismatch" % attrName)
45
46 def test_1100_all_args(self):
47 "1100 - connection to database with user, password, TNS separate"
48 connection = test_env.get_connection()
49 self.__verify_args(connection)
50
51 def test_1101_app_context(self):
52 "1101 - test use of application context"
53 namespace = "CLIENTCONTEXT"
54 app_context_entries = [
55 ( namespace, "ATTR1", "VALUE1" ),
56 ( namespace, "ATTR2", "VALUE2" ),
57 ( namespace, "ATTR3", "VALUE3" )
58 ]
59 connection = test_env.get_connection(appcontext=app_context_entries)
60 cursor = connection.cursor()
61 for namespace, name, value in app_context_entries:
62 cursor.execute("select sys_context(:1, :2) from dual",
63 (namespace, name))
64 actual_value, = cursor.fetchone()
65 self.assertEqual(actual_value, value)
66
67 def test_1102_app_context_negative(self):
68 "1102 - test invalid use of application context"
69 self.assertRaises(TypeError, oracledb.connect,
70 test_env.get_main_user(),
71 test_env.get_main_password(),
72 test_env.get_connect_string(),
73 appcontext=[('userenv', 'action')])
74
75 def test_1103_attributes(self):
76 "1103 - test connection end-to-end tracing attributes"
77 connection = test_env.get_connection()
78 if test_env.get_client_version() >= (12, 1) \
79 and not self.is_on_oracle_cloud(connection):
80 sql = "select dbop_name from v$sql_monitor " \
81 "where sid = sys_context('userenv', 'sid')" \
82 "and status = 'EXECUTING'"
83 self.__verify_attributes(connection, "dbop", "oracledb_dbop", sql)
84 sql = "select sys_context('userenv', 'action') from dual"
85 self.__verify_attributes(connection, "action", "oracledb_Action", sql)
86 sql = "select sys_context('userenv', 'module') from dual"
87 self.__verify_attributes(connection, "module", "oracledb_Module", sql)
88 sql = "select sys_context('userenv', 'client_info') from dual"
89 self.__verify_attributes(connection, "clientinfo", "oracledb_cinfo",
90 sql)
91 sql = "select sys_context('userenv', 'client_identifier') from dual"
92 self.__verify_attributes(connection, "client_identifier",
93 "oracledb_cid", sql)
94
95 def test_1104_autocommit(self):
96 "1104 - test use of autocommit"
97 connection = test_env.get_connection()
98 cursor = connection.cursor()
99 other_connection = test_env.get_connection()
100 other_cursor = other_connection.cursor()
101 cursor.execute("truncate table TestTempTable")
102 cursor.execute("insert into TestTempTable (IntCol) values (1)")
103 other_cursor.execute("select IntCol from TestTempTable")
104 rows = other_cursor.fetchall()
105 self.assertEqual(rows, [])
106 connection.autocommit = True
107 cursor.execute("insert into TestTempTable (IntCol) values (2)")
108 other_cursor.execute("select IntCol from TestTempTable order by IntCol")
109 rows = other_cursor.fetchall()
110 self.assertEqual(rows, [(1,), (2,)])
111
112 def test_1105_bad_connect_string(self):
113 "1105 - connection to database with bad connect string"
114 self.assertRaises(oracledb.DatabaseError, oracledb.connect,
115 test_env.get_main_user())
116 self.assertRaises(oracledb.DatabaseError, oracledb.connect,
117 test_env.get_main_user() + "@" + \
118 test_env.get_connect_string())
119 self.assertRaises(oracledb.DatabaseError, oracledb.connect,
120 test_env.get_main_user() + "@" + \
121 test_env.get_connect_string() + "/" + \
122 test_env.get_main_password())
123
124 def test_1106_bad_password(self):
125 "1106 - connection to database with bad password"
126 self.assertRaises(oracledb.DatabaseError, oracledb.connect,
127 test_env.get_main_user(),
128 test_env.get_main_password() + "X",
129 test_env.get_connect_string())
130
131 def test_1107_change_password(self):
132 "1107 - test changing password"
133 connection = test_env.get_connection()
134 if self.is_on_oracle_cloud(connection):
135 self.skipTest("passwords on Oracle Cloud are strictly controlled")
136 sys_random = random.SystemRandom()
137 new_password = "".join(sys_random.choice(string.ascii_letters) \
138 for i in range(20))
139 connection.changepassword(test_env.get_main_password(), new_password)
140 cconnection = oracledb.connect(test_env.get_main_user(), new_password,
141 test_env.get_connect_string())
142 connection.changepassword(new_password, test_env.get_main_password())
143
144 def test_1108_change_password_negative(self):
145 "1108 - test changing password to an invalid value"
146 connection = test_env.get_connection()
147 if self.is_on_oracle_cloud(connection):
148 self.skipTest("passwords on Oracle Cloud are strictly controlled")
149 new_password = "1" * 150
150 self.assertRaises(oracledb.DatabaseError, connection.changepassword,
151 test_env.get_main_password(), new_password)
152
153 def test_1109_parse_password(self):
154 "1109 - test connecting with password containing / and @ symbols"
155 connection = test_env.get_connection()
156 if self.is_on_oracle_cloud(connection):
157 self.skipTest("passwords on Oracle Cloud are strictly controlled")
158 sys_random = random.SystemRandom()
159 chars = list(sys_random.choice(string.ascii_letters) for i in range(20))
160 chars[4] = "/"
161 chars[8] = "@"
162 new_password = "".join(chars)
163 connection.changepassword(test_env.get_main_password(), new_password)
164 try:
165 arg = "%s/%s@%s" % (test_env.get_main_user(), new_password,
166 test_env.get_connect_string())
167 oracledb.connect(arg)
168 finally:
169 connection.changepassword(new_password,
170 test_env.get_main_password())
171
172 def test_1110_encodings(self):
173 "1110 - connection with only encoding/nencoding specified should work"
174 connection = oracledb.connect(test_env.get_main_user(),
175 test_env.get_main_password(),
176 test_env.get_connect_string())
177 encoding = connection.encoding
178 nencoding = connection.nencoding
179 alt_encoding = "ISO-8859-1"
180 connection = oracledb.connect(test_env.get_main_user(),
181 test_env.get_main_password(),
182 test_env.get_connect_string(),
183 encoding=alt_encoding)
184 self.assertEqual(connection.encoding, alt_encoding)
185 self.assertEqual(connection.nencoding, nencoding)
186 connection = oracledb.connect(test_env.get_main_user(),
187 test_env.get_main_password(),
188 test_env.get_connect_string(),
189 nencoding=alt_encoding)
190 self.assertEqual(connection.encoding, encoding)
191 self.assertEqual(connection.nencoding, alt_encoding)
192
193 def test_1111_different_encodings(self):
194 "1111 - different encodings can be specified for encoding/nencoding"
195 connection = oracledb.connect(test_env.get_main_user(),
196 test_env.get_main_password(),
197 test_env.get_connect_string(),
198 encoding="UTF-8", nencoding="UTF-16")
199 value = "\u03b4\u4e2a"
200 cursor = connection.cursor()
201 nchar_var = cursor.var(oracledb.DB_TYPE_NVARCHAR, 100)
202 nchar_var.setvalue(0, value)
203 cursor.execute("select :value from dual", value=nchar_var)
204 result, = cursor.fetchone()
205 self.assertEqual(result, value)
206
207 def test_1112_exception_on_close(self):
208 "1112 - confirm an exception is raised after closing a connection"
209 connection = test_env.get_connection()
210 connection.close()
211 self.assertRaises(oracledb.InterfaceError, connection.rollback)
212
213 def test_1113_connect_with_handle(self):
214 "1113 - test creating a connection using a handle"
215 connection = test_env.get_connection()
216 cursor = connection.cursor()
217 cursor.execute("truncate table TestTempTable")
218 int_value = random.randint(1, 32768)
219 cursor.execute("""
220 insert into TestTempTable (IntCol, StringCol)
221 values (:val, null)""", val=int_value)
222 connection2 = oracledb.connect(handle = connection.handle)
223 cursor = connection2.cursor()
224 cursor.execute("select IntCol from TestTempTable")
225 fetched_int_value, = cursor.fetchone()
226 self.assertEqual(fetched_int_value, int_value)
227 cursor.close()
228 self.assertRaises(oracledb.DatabaseError, connection2.close)
229 connection.close()
230
231 def test_1114_make_dsn(self):
232 "1114 - test making a data source name from host, port and sid"
233 format_string = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" + \
234 "(HOST=%s)(PORT=%d))(CONNECT_DATA=(SID=%s)))"
235 args = ("hostname", 1521, "TEST")
236 result = oracledb.makedsn(*args)
237 self.assertEqual(result, format_string % args)
238
239 def test_1115_single_arg(self):
240 "1115 - connection to database with user, password, DSN together"
241 arg = "%s/%s@%s" % (test_env.get_main_user(),
242 test_env.get_main_password(),
243 test_env.get_connect_string())
244 connection = oracledb.connect(arg)
245 self.__verify_args(connection)
246
247 def test_1116_version(self):
248 "1116 - connection version is a string"
249 connection = test_env.get_connection()
250 self.assertTrue(isinstance(connection.version, str))
251
252 def test_1117_rollback_on_close(self):
253 "1117 - connection rolls back before close"
254 connection = test_env.get_connection()
255 cursor = connection.cursor()
256 cursor.execute("truncate table TestTempTable")
257 other_connection = test_env.get_connection()
258 other_cursor = other_connection.cursor()
259 other_cursor.execute("insert into TestTempTable (IntCol) values (1)")
260 other_cursor.close()
261 other_connection.close()
262 cursor.execute("select count(*) from TestTempTable")
263 count, = cursor.fetchone()
264 self.assertEqual(count, 0)
265
266 def test_1118_rollback_on_del(self):
267 "1118 - connection rolls back before destruction"
268 connection = test_env.get_connection()
269 cursor = connection.cursor()
270 cursor.execute("truncate table TestTempTable")
271 other_connection = test_env.get_connection()
272 other_cursor = other_connection.cursor()
273 other_cursor.execute("insert into TestTempTable (IntCol) values (1)")
274 del other_cursor
275 del other_connection
276 cursor.execute("select count(*) from TestTempTable")
277 count, = cursor.fetchone()
278 self.assertEqual(count, 0)
279
280 def test_1119_threading(self):
281 "1119 - connection to database with multiple threads"
282 threads = []
283 for i in range(20):
284 thread = threading.Thread(None, self.__connect_and_drop)
285 threads.append(thread)
286 thread.start()
287 for thread in threads:
288 thread.join()
289
290 def test_1120_string_format(self):
291 "1120 - test string format of connection"
292 connection = test_env.get_connection()
293 expected_value = "<cx_Oracle.Connection to %s@%s>" % \
294 (test_env.get_main_user(), test_env.get_connect_string())
295 self.assertEqual(str(connection), expected_value)
296
297 def test_1121_ctx_mgr_close(self):
298 "1121 - test context manager - close"
299 connection = test_env.get_connection()
300 with connection:
301 cursor = connection.cursor()
302 cursor.execute("truncate table TestTempTable")
303 cursor.execute("insert into TestTempTable (IntCol) values (1)")
304 connection.commit()
305 cursor.execute("insert into TestTempTable (IntCol) values (2)")
306 self.assertRaises(oracledb.InterfaceError, connection.ping)
307 connection = test_env.get_connection()
308 cursor = connection.cursor()
309 cursor.execute("select count(*) from TestTempTable")
310 count, = cursor.fetchone()
311 self.assertEqual(count, 1)
312
313 def test_1122_connection_attributes(self):
314 "1122 - test connection attribute values"
315 connection = oracledb.connect(test_env.get_main_user(),
316 test_env.get_main_password(),
317 test_env.get_connect_string(),
318 encoding="ASCII")
319 self.assertEqual(connection.maxBytesPerCharacter, 1)
320 connection = oracledb.connect(test_env.get_main_user(),
321 test_env.get_main_password(),
322 test_env.get_connect_string(),
323 encoding="UTF-8")
324 self.assertEqual(connection.maxBytesPerCharacter, 4)
325 if test_env.get_client_version() >= (12, 1):
326 self.assertEqual(connection.ltxid, b'')
327 self.assertEqual(connection.current_schema, None)
328 connection.current_schema = "test_schema"
329 self.assertEqual(connection.current_schema, "test_schema")
330 self.assertEqual(connection.edition, None)
331 connection.external_name = "test_external"
332 self.assertEqual(connection.external_name, "test_external")
333 connection.internal_name = "test_internal"
334 self.assertEqual(connection.internal_name, "test_internal")
335 connection.stmtcachesize = 30
336 self.assertEqual(connection.stmtcachesize, 30)
337 self.assertRaises(TypeError, connection.stmtcachesize, 20.5)
338 self.assertRaises(TypeError, connection.stmtcachesize, "value")
339
340 def test_1123_closed_connection_attributes(self):
341 "1123 - test closed connection attribute values"
342 connection = test_env.get_connection()
343 connection.close()
344 attr_names = ["current_schema", "edition", "external_name",
345 "internal_name", "stmtcachesize"]
346 if test_env.get_client_version() >= (12, 1):
347 attr_names.append("ltxid")
348 for name in attr_names:
349 self.assertRaises(oracledb.InterfaceError, getattr, connection,
350 name)
351
352 def test_1124_ping(self):
353 "1124 - test connection ping"
354 connection = test_env.get_connection()
355 connection.ping()
356
357 def test_1125_transaction_begin(self):
358 "1125 - test begin, prepare, cancel transaction"
359 connection = test_env.get_connection()
360 cursor = connection.cursor()
361 cursor.execute("truncate table TestTempTable")
362 connection.begin(10, 'trxnId', 'branchId')
363 self.assertEqual(connection.prepare(), False)
364 connection.begin(10, 'trxnId', 'branchId')
365 cursor.execute("""
366 insert into TestTempTable (IntCol, StringCol)
367 values (1, 'tesName')""")
368 self.assertEqual(connection.prepare(), True)
369 connection.cancel()
370 connection.rollback()
371 cursor.execute("select count(*) from TestTempTable")
372 count, = cursor.fetchone()
373 self.assertEqual(count, 0)
374
375 if __name__ == "__main__":
376 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 1200 - Module for testing cursors
11 """
12
13 import test_env
14 import cx_Oracle as oracledb
15 import decimal
16
17 class TestCase(test_env.BaseTestCase):
18
19 def test_1200_create_scrollable_cursor(self):
20 "1200 - test creating a scrollable cursor"
21 cursor = self.connection.cursor()
22 self.assertEqual(cursor.scrollable, False)
23 cursor = self.connection.cursor(True)
24 self.assertEqual(cursor.scrollable, True)
25 cursor = self.connection.cursor(scrollable=True)
26 self.assertEqual(cursor.scrollable, True)
27 cursor.scrollable = False
28 self.assertEqual(cursor.scrollable, False)
29
30 def test_1201_execute_no_args(self):
31 "1201 - test executing a statement without any arguments"
32 result = self.cursor.execute("begin null; end;")
33 self.assertEqual(result, None)
34
35 def test_1202_execute_no_statement_with_args(self):
36 "1202 - test executing a None statement with bind variables"
37 self.assertRaises(oracledb.ProgrammingError, self.cursor.execute, None,
38 x=5)
39
40 def test_1203_execute_empty_keyword_args(self):
41 "1203 - test executing a statement with args and empty keyword args"
42 simple_var = self.cursor.var(oracledb.NUMBER)
43 args = [simple_var]
44 kwargs = {}
45 result = self.cursor.execute("begin :1 := 25; end;", args, **kwargs)
46 self.assertEqual(result, None)
47 self.assertEqual(simple_var.getvalue(), 25)
48
49 def test_1204_execute_keyword_args(self):
50 "1204 - test executing a statement with keyword arguments"
51 simple_var = self.cursor.var(oracledb.NUMBER)
52 result = self.cursor.execute("begin :value := 5; end;",
53 value=simple_var)
54 self.assertEqual(result, None)
55 self.assertEqual(simple_var.getvalue(), 5)
56
57 def test_1205_execute_dictionary_arg(self):
58 "1205 - test executing a statement with a dictionary argument"
59 simple_var = self.cursor.var(oracledb.NUMBER)
60 dict_arg = dict(value=simple_var)
61 result = self.cursor.execute("begin :value := 10; end;", dict_arg)
62 self.assertEqual(result, None)
63 self.assertEqual(simple_var.getvalue(), 10)
64
65 def test_1206_execute_multiple_arg_types(self):
66 "1206 - test executing a statement with both a dict and keyword args"
67 simple_var = self.cursor.var(oracledb.NUMBER)
68 dict_arg = dict(value=simple_var)
69 self.assertRaises(oracledb.InterfaceError, self.cursor.execute,
70 "begin :value := 15; end;", dict_arg,
71 value=simple_var)
72
73 def test_1207_execute_and_modify_array_size(self):
74 "1207 - test executing a statement and then changing the array size"
75 self.cursor.execute("select IntCol from TestNumbers")
76 self.cursor.arraysize = 20
77 self.assertEqual(len(self.cursor.fetchall()), 10)
78
79 def test_1208_callproc(self):
80 "1208 - test executing a stored procedure"
81 var = self.cursor.var(oracledb.NUMBER)
82 results = self.cursor.callproc("proc_Test", ("hi", 5, var))
83 self.assertEqual(results, ["hi", 10, 2.0])
84
85 def test_1209_callproc_all_keywords(self):
86 "1209 - test executing a stored procedure with all args keyword args"
87 inout_value = self.cursor.var(oracledb.NUMBER)
88 inout_value.setvalue(0, 5)
89 out_value = self.cursor.var(oracledb.NUMBER)
90 kwargs = dict(a_InOutValue=inout_value, a_InValue="hi",
91 a_OutValue=out_value)
92 results = self.cursor.callproc("proc_Test", [], kwargs)
93 self.assertEqual(results, [])
94 self.assertEqual(inout_value.getvalue(), 10)
95 self.assertEqual(out_value.getvalue(), 2.0)
96
97 def test_1210_callproc_only_last_keyword(self):
98 "1210 - test executing a stored procedure with last arg as keyword arg"
99 out_value = self.cursor.var(oracledb.NUMBER)
100 kwargs = dict(a_OutValue=out_value)
101 results = self.cursor.callproc("proc_Test", ("hi", 5), kwargs)
102 self.assertEqual(results, ["hi", 10])
103 self.assertEqual(out_value.getvalue(), 2.0)
104
105 def test_1211_callproc_repeated_keyword_parameters(self):
106 "1211 - test executing a stored procedure, repeated keyword arg"
107 kwargs = dict(a_InValue="hi",
108 a_OutValue=self.cursor.var(oracledb.NUMBER))
109 self.assertRaises(oracledb.DatabaseError, self.cursor.callproc,
110 "proc_Test", ("hi", 5), kwargs)
111
112 def test_1212_callproc_no_args(self):
113 "1212 - test executing a stored procedure without any arguments"
114 results = self.cursor.callproc("proc_TestNoArgs")
115 self.assertEqual(results, [])
116
117 def test_1213_callfunc(self):
118 "1213 - test executing a stored function"
119 results = self.cursor.callfunc("func_Test", oracledb.NUMBER, ("hi", 5))
120 self.assertEqual(results, 7)
121
122 def test_1214_callfunc_no_args(self):
123 "1214 - test executing a stored function without any arguments"
124 results = self.cursor.callfunc("func_TestNoArgs", oracledb.NUMBER)
125 self.assertEqual(results, 712)
126
127 def test_1215_callfunc_negative(self):
128 "1215 - test executing a stored function with wrong parameters"
129 func_name = "func_Test"
130 self.assertRaises(TypeError, self.cursor.callfunc, oracledb.NUMBER,
131 func_name, ("hi", 5))
132 self.assertRaises(oracledb.DatabaseError, self.cursor.callfunc,
133 func_name, oracledb.NUMBER, ("hi", 5, 7))
134 self.assertRaises(TypeError, self.cursor.callfunc, func_name,
135 oracledb.NUMBER, "hi", 7)
136 self.assertRaises(oracledb.DatabaseError, self.cursor.callfunc,
137 func_name, oracledb.NUMBER, [5, "hi"])
138 self.assertRaises(oracledb.DatabaseError, self.cursor.callfunc,
139 func_name, oracledb.NUMBER)
140 self.assertRaises(TypeError, self.cursor.callfunc, func_name,
141 oracledb.NUMBER, 5)
142
143 def test_1216_executemany_by_name(self):
144 "1216 - test executing a statement multiple times (named args)"
145 self.cursor.execute("truncate table TestTempTable")
146 rows = [{"value": n} for n in range(250)]
147 self.cursor.arraysize = 100
148 statement = "insert into TestTempTable (IntCol) values (:value)"
149 self.cursor.executemany(statement, rows)
150 self.connection.commit()
151 self.cursor.execute("select count(*) from TestTempTable")
152 count, = self.cursor.fetchone()
153 self.assertEqual(count, len(rows))
154
155 def test_1217_executemany_by_position(self):
156 "1217 - test executing a statement multiple times (positional args)"
157 self.cursor.execute("truncate table TestTempTable")
158 rows = [[n] for n in range(230)]
159 self.cursor.arraysize = 100
160 statement = "insert into TestTempTable (IntCol) values (:1)"
161 self.cursor.executemany(statement, rows)
162 self.connection.commit()
163 self.cursor.execute("select count(*) from TestTempTable")
164 count, = self.cursor.fetchone()
165 self.assertEqual(count, len(rows))
166
167 def test_1218_executemany_with_prepare(self):
168 "1218 - test executing a statement multiple times (with prepare)"
169 self.cursor.execute("truncate table TestTempTable")
170 rows = [[n] for n in range(225)]
171 self.cursor.arraysize = 100
172 statement = "insert into TestTempTable (IntCol) values (:1)"
173 self.cursor.prepare(statement)
174 self.cursor.executemany(None, rows)
175 self.connection.commit()
176 self.cursor.execute("select count(*) from TestTempTable")
177 count, = self.cursor.fetchone()
178 self.assertEqual(count, len(rows))
179
180 def test_1219_executemany_with_rebind(self):
181 "1219 - test executing a statement multiple times (with rebind)"
182 self.cursor.execute("truncate table TestTempTable")
183 rows = [[n] for n in range(235)]
184 self.cursor.arraysize = 100
185 statement = "insert into TestTempTable (IntCol) values (:1)"
186 self.cursor.executemany(statement, rows[:50])
187 self.cursor.executemany(statement, rows[50:])
188 self.connection.commit()
189 self.cursor.execute("select count(*) from TestTempTable")
190 count, = self.cursor.fetchone()
191 self.assertEqual(count, len(rows))
192
193 def test_1220_executemany_with_input_sizes_wrong(self):
194 "1220 - test executing multiple times (with input sizes wrong)"
195 cursor = self.connection.cursor()
196 cursor.setinputsizes(oracledb.NUMBER)
197 data = [[decimal.Decimal("25.8")], [decimal.Decimal("30.0")]]
198 cursor.executemany("declare t number; begin t := :1; end;", data)
199
200 def test_1221_executemany_with_multiple_batches(self):
201 "1221 - test executing multiple times (with multiple batches)"
202 self.cursor.execute("truncate table TestTempTable")
203 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
204 self.cursor.executemany(sql, [(1, None), (2, None)])
205 self.cursor.executemany(sql, [(3, None), (4, "Testing")])
206
207 def test_1222_executemany_numeric(self):
208 "1222 - test executemany() with various numeric types"
209 self.cursor.execute("truncate table TestTempTable")
210 data = [
211 (1, 5),
212 (2, 7.0),
213 (3, 6.5),
214 (4, 2 ** 65),
215 (5, decimal.Decimal("24.5"))
216 ]
217 sql = "insert into TestTempTable (IntCol, NumberCol) values (:1, :2)"
218 self.cursor.executemany(sql, data)
219 self.cursor.execute("""
220 select IntCol, NumberCol
221 from TestTempTable
222 order by IntCol""")
223 self.assertEqual(self.cursor.fetchall(), data)
224
225 def test_1223_executemany_with_resize(self):
226 "1223 - test executing a statement multiple times (with resize)"
227 self.cursor.execute("truncate table TestTempTable")
228 rows = [
229 (1, "First"),
230 (2, "Second"),
231 (3, "Third"),
232 (4, "Fourth"),
233 (5, "Fifth"),
234 (6, "Sixth"),
235 (7, "Seventh and the longest one")
236 ]
237 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
238 self.cursor.executemany(sql, rows)
239 self.cursor.execute("""
240 select IntCol, StringCol
241 from TestTempTable
242 order by IntCol""")
243 fetched_rows = self.cursor.fetchall()
244 self.assertEqual(fetched_rows, rows)
245
246 def test_1224_executemany_with_exception(self):
247 "1224 - test executing a statement multiple times (with exception)"
248 self.cursor.execute("truncate table TestTempTable")
249 rows = [{"value": n} for n in (1, 2, 3, 2, 5)]
250 statement = "insert into TestTempTable (IntCol) values (:value)"
251 self.assertRaises(oracledb.DatabaseError, self.cursor.executemany,
252 statement, rows)
253 self.assertEqual(self.cursor.rowcount, 3)
254
255 def test_1225_executemany_with_invalid_parameters(self):
256 "1225 - test calling executemany() with invalid parameters"
257 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
258 self.assertRaises(TypeError, self.cursor.executemany, sql,
259 "These are not valid parameters")
260
261 def test_1226_executemany_no_parameters(self):
262 "1226 - test calling executemany() without any bind parameters"
263 num_rows = 5
264 self.cursor.execute("truncate table TestTempTable")
265 self.cursor.executemany("""
266 declare
267 t_Id number;
268 begin
269 select nvl(count(*), 0) + 1 into t_Id
270 from TestTempTable;
271
272 insert into TestTempTable (IntCol, StringCol)
273 values (t_Id, 'Test String ' || t_Id);
274 end;""", num_rows)
275 self.assertEqual(self.cursor.rowcount, num_rows)
276 self.cursor.execute("select count(*) from TestTempTable")
277 count, = self.cursor.fetchone()
278 self.assertEqual(count, num_rows)
279
280 def test_1227_executemany_bound_earlier(self):
281 "1227 - test calling executemany() with binds performed earlier"
282 num_rows = 9
283 self.cursor.execute("truncate table TestTempTable")
284 var = self.cursor.var(int, arraysize=num_rows)
285 self.cursor.setinputsizes(var)
286 self.cursor.executemany("""
287 declare
288 t_Id number;
289 begin
290 select nvl(count(*), 0) + 1 into t_Id
291 from TestTempTable;
292
293 insert into TestTempTable (IntCol, StringCol)
294 values (t_Id, 'Test String ' || t_Id);
295
296 select sum(IntCol) into :1
297 from TestTempTable;
298 end;""", num_rows)
299 self.assertEqual(self.cursor.rowcount, num_rows)
300 expected_data = [1, 3, 6, 10, 15, 21, 28, 36, 45]
301 self.assertEqual(var.values, expected_data)
302
303 def test_1228_prepare(self):
304 "1228 - test preparing a statement and executing it multiple times"
305 self.assertEqual(self.cursor.statement, None)
306 statement = "begin :value := :value + 5; end;"
307 self.cursor.prepare(statement)
308 var = self.cursor.var(oracledb.NUMBER)
309 self.assertEqual(self.cursor.statement, statement)
310 var.setvalue(0, 2)
311 self.cursor.execute(None, value = var)
312 self.assertEqual(var.getvalue(), 7)
313 self.cursor.execute(None, value = var)
314 self.assertEqual(var.getvalue(), 12)
315 self.cursor.execute("begin :value2 := 3; end;", value2 = var)
316 self.assertEqual(var.getvalue(), 3)
317
318 def test_1229_exception_on_close(self):
319 "1229 - confirm an exception is raised after closing a cursor"
320 self.cursor.close()
321 self.assertRaises(oracledb.InterfaceError, self.cursor.execute,
322 "select 1 from dual")
323
324 def test_1230_iterators(self):
325 "1230 - test iterators"
326 self.cursor.execute("""
327 select IntCol
328 from TestNumbers
329 where IntCol between 1 and 3
330 order by IntCol""")
331 rows = [v for v, in self.cursor]
332 self.assertEqual(rows, [1, 2, 3])
333
334 def test_1231_iterators_interrupted(self):
335 "1231 - test iterators (with intermediate execute)"
336 self.cursor.execute("truncate table TestTempTable")
337 self.cursor.execute("""
338 select IntCol
339 from TestNumbers
340 where IntCol between 1 and 3
341 order by IntCol""")
342 test_iter = iter(self.cursor)
343 value, = next(test_iter)
344 self.cursor.execute("insert into TestTempTable (IntCol) values (1)")
345 self.assertRaises(oracledb.InterfaceError, next, test_iter)
346
347 def test_1232_bind_names(self):
348 "1232 - test that bindnames() works correctly."
349 self.assertRaises(oracledb.ProgrammingError, self.cursor.bindnames)
350 self.cursor.prepare("begin null; end;")
351 self.assertEqual(self.cursor.bindnames(), [])
352 self.cursor.prepare("begin :retval := :inval + 5; end;")
353 self.assertEqual(self.cursor.bindnames(), ["RETVAL", "INVAL"])
354 self.cursor.prepare("begin :retval := :a * :a + :b * :b; end;")
355 self.assertEqual(self.cursor.bindnames(), ["RETVAL", "A", "B"])
356 self.cursor.prepare("begin :a := :b + :c + :d + :e + :f + :g + " + \
357 ":h + :i + :j + :k + :l; end;")
358 names = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
359 self.assertEqual(self.cursor.bindnames(), names)
360 self.cursor.prepare("select :a * :a + :b * :b from dual")
361 self.assertEqual(self.cursor.bindnames(), ["A", "B"])
362
363 def test_1233_bad_execute(self):
364 "1233 - test that subsequent executes succeed after bad execute"
365 self.assertRaises(oracledb.DatabaseError,
366 self.cursor.execute,
367 "begin raise_application_error(-20000, 'this); end;")
368 self.cursor.execute("begin null; end;")
369
370 def test_1234_fetch_after_bad_execute(self):
371 "1234 - test that subsequent fetches fail after bad execute"
372 self.assertRaises(oracledb.DatabaseError,
373 self.cursor.execute, "select y from dual")
374 self.assertRaises(oracledb.InterfaceError, self.cursor.fetchall)
375
376 def test_1235_scroll_absolute_exception_after(self):
377 "1235 - test scrolling absolute yields an exception (after result set)"
378 cursor = self.connection.cursor(scrollable=True)
379 cursor.arraysize = self.cursor.arraysize
380 cursor.execute("""
381 select NumberCol
382 from TestNumbers
383 order by IntCol""")
384 self.assertRaises(oracledb.DatabaseError, cursor.scroll, 12,
385 "absolute")
386
387 def test_1236_scroll_absolute_in_buffer(self):
388 "1236 - test scrolling absolute (when in buffers)"
389 cursor = self.connection.cursor(scrollable=True)
390 cursor.arraysize = self.cursor.arraysize
391 cursor.execute("""
392 select NumberCol
393 from TestNumbers
394 order by IntCol""")
395 cursor.fetchmany()
396 self.assertTrue(cursor.arraysize > 1,
397 "array size must exceed 1 for this test to work correctly")
398 cursor.scroll(1, mode = "absolute")
399 row = cursor.fetchone()
400 self.assertEqual(row[0], 1.25)
401 self.assertEqual(cursor.rowcount, 1)
402
403 def test_1237_scroll_absolute_not_in_buffer(self):
404 "1237 - test scrolling absolute (when not in buffers)"
405 cursor = self.connection.cursor(scrollable=True)
406 cursor.arraysize = self.cursor.arraysize
407 cursor.execute("""
408 select NumberCol
409 from TestNumbers
410 order by IntCol""")
411 cursor.scroll(6, mode = "absolute")
412 row = cursor.fetchone()
413 self.assertEqual(row[0], 7.5)
414 self.assertEqual(cursor.rowcount, 6)
415
416 def test_1238_scroll_first_in_buffer(self):
417 "1238 - test scrolling to first row in result set (in buffers)"
418 cursor = self.connection.cursor(scrollable=True)
419 cursor.arraysize = self.cursor.arraysize
420 cursor.execute("""
421 select NumberCol
422 from TestNumbers
423 order by IntCol""")
424 cursor.fetchmany()
425 cursor.scroll(mode="first")
426 row = cursor.fetchone()
427 self.assertEqual(row[0], 1.25)
428 self.assertEqual(cursor.rowcount, 1)
429
430 def test_1239_scroll_first_not_in_buffer(self):
431 "1239 - test scrolling to first row in result set (not in buffers)"
432 cursor = self.connection.cursor(scrollable=True)
433 cursor.arraysize = self.cursor.arraysize
434 cursor.execute("""
435 select NumberCol
436 from TestNumbers
437 order by IntCol""")
438 cursor.fetchmany()
439 cursor.fetchmany()
440 cursor.scroll(mode="first")
441 row = cursor.fetchone()
442 self.assertEqual(row[0], 1.25)
443 self.assertEqual(cursor.rowcount, 1)
444
445 def test_1240_scroll_last(self):
446 "1240 - test scrolling to last row in result set"
447 cursor = self.connection.cursor(scrollable=True)
448 cursor.arraysize = self.cursor.arraysize
449 cursor.execute("""
450 select NumberCol
451 from TestNumbers
452 order by IntCol""")
453 cursor.scroll(mode="last")
454 row = cursor.fetchone()
455 self.assertEqual(row[0], 12.5)
456 self.assertEqual(cursor.rowcount, 10)
457
458 def test_1241_scroll_relative_exception_after(self):
459 "1241 - test scrolling relative yields an exception (after result set)"
460 cursor = self.connection.cursor(scrollable=True)
461 cursor.arraysize = self.cursor.arraysize
462 cursor.execute("""
463 select NumberCol
464 from TestNumbers
465 order by IntCol""")
466 self.assertRaises(oracledb.DatabaseError, cursor.scroll, 15)
467
468 def test_1242_scroll_relative_exception_before(self):
469 "1242 - test scrolling relative yields exception (before result set)"
470 cursor = self.connection.cursor(scrollable=True)
471 cursor.arraysize = self.cursor.arraysize
472 cursor.execute("""
473 select NumberCol
474 from TestNumbers
475 order by IntCol""")
476 self.assertRaises(oracledb.DatabaseError, cursor.scroll, -5)
477
478 def test_1243_scroll_relative_in_buffer(self):
479 "1243 - test scrolling relative (when in buffers)"
480 cursor = self.connection.cursor(scrollable=True)
481 cursor.arraysize = self.cursor.arraysize
482 cursor.execute("""
483 select NumberCol
484 from TestNumbers
485 order by IntCol""")
486 cursor.fetchmany()
487 message = "array size must exceed 1 for this test to work correctly"
488 self.assertTrue(cursor.arraysize > 1, message)
489 cursor.scroll(2 - cursor.rowcount)
490 row = cursor.fetchone()
491 self.assertEqual(row[0], 2.5)
492 self.assertEqual(cursor.rowcount, 2)
493
494 def test_1244_scroll_relative_not_in_buffer(self):
495 "1244 - test scrolling relative (when not in buffers)"
496 cursor = self.connection.cursor(scrollable=True)
497 cursor.arraysize = self.cursor.arraysize
498 cursor.execute("""
499 select NumberCol
500 from TestNumbers
501 order by IntCol""")
502 cursor.fetchmany()
503 cursor.fetchmany()
504 message = "array size must exceed 1 for this test to work correctly"
505 self.assertTrue(cursor.arraysize > 1, message)
506 cursor.scroll(3 - cursor.rowcount)
507 row = cursor.fetchone()
508 self.assertEqual(row[0], 3.75)
509 self.assertEqual(cursor.rowcount, 3)
510
511 def test_1245_scroll_no_rows(self):
512 "1245 - test scrolling when there are no rows"
513 self.cursor.execute("truncate table TestTempTable")
514 cursor = self.connection.cursor(scrollable=True)
515 cursor.execute("select * from TestTempTable")
516 cursor.scroll(mode = "last")
517 self.assertEqual(cursor.fetchall(), [])
518 cursor.scroll(mode = "first")
519 self.assertEqual(cursor.fetchall(), [])
520 self.assertRaises(oracledb.DatabaseError, cursor.scroll, 1,
521 mode="absolute")
522
523 def test_1246_scroll_differing_array_and_fetch_sizes(self):
524 "1246 - test scrolling with differing array and fetch array sizes"
525 self.cursor.execute("truncate table TestTempTable")
526 for i in range(30):
527 self.cursor.execute("""
528 insert into TestTempTable (IntCol, StringCol)
529 values (:1, null)""",
530 (i + 1,))
531 for arraysize in range(1, 6):
532 cursor = self.connection.cursor(scrollable = True)
533 cursor.arraysize = arraysize
534 cursor.execute("select IntCol from TestTempTable order by IntCol")
535 for num_rows in range(1, arraysize + 1):
536 cursor.scroll(15, "absolute")
537 rows = cursor.fetchmany(num_rows)
538 self.assertEqual(rows[0][0], 15)
539 self.assertEqual(cursor.rowcount, 15 + num_rows - 1)
540 cursor.scroll(9)
541 rows = cursor.fetchmany(num_rows)
542 num_rows_fetched = len(rows)
543 self.assertEqual(rows[0][0], 15 + num_rows + 8)
544 self.assertEqual(cursor.rowcount,
545 15 + num_rows + num_rows_fetched + 7)
546 cursor.scroll(-12)
547 rows = cursor.fetchmany(num_rows)
548 count = 15 + num_rows + num_rows_fetched - 5
549 self.assertEqual(rows[0][0], count)
550 count = 15 + num_rows + num_rows_fetched + num_rows - 6
551 self.assertEqual(cursor.rowcount, count)
552
553 def test_1247_set_input_sizes_negative(self):
554 "1247 - test cursor.setinputsizes() with invalid parameters"
555 val = decimal.Decimal(5)
556 self.assertRaises(oracledb.InterfaceError,
557 self.cursor.setinputsizes, val, x=val)
558 self.assertRaises(TypeError, self.cursor.setinputsizes, val)
559
560 def test_1248_set_input_sizes_no_parameters(self):
561 "1248 - test setting input sizes without any parameters"
562 self.cursor.setinputsizes()
563 self.cursor.execute("select :val from dual", val="Test Value")
564 self.assertEqual(self.cursor.fetchall(), [("Test Value",)])
565
566 def test_1249_set_input_sizes_empty_dict(self):
567 "1249 - test setting input sizes with an empty dictionary"
568 empty_dict = {}
569 self.cursor.prepare("select 236 from dual")
570 self.cursor.setinputsizes(**empty_dict)
571 self.cursor.execute(None, empty_dict)
572 self.assertEqual(self.cursor.fetchall(), [(236,)])
573
574 def test_1250_set_input_sizes_empty_list(self):
575 "1250 - test setting input sizes with an empty list"
576 empty_list = {}
577 self.cursor.prepare("select 239 from dual")
578 self.cursor.setinputsizes(*empty_list)
579 self.cursor.execute(None, empty_list)
580 self.assertEqual(self.cursor.fetchall(), [(239,)])
581
582 def test_1251_set_input_sizes_by_position(self):
583 "1251 - test setting input sizes with positional args"
584 var = self.cursor.var(oracledb.STRING, 100)
585 self.cursor.setinputsizes(None, 5, None, 10, None, oracledb.NUMBER)
586 self.cursor.execute("""
587 begin
588 :1 := :2 || to_char(:3) || :4 || to_char(:5) || to_char(:6);
589 end;""", [var, 'test_', 5, '_second_', 3, 7])
590 self.assertEqual(var.getvalue(), "test_5_second_37")
591
592 def test_1252_string_format(self):
593 "1252 - test string format of cursor"
594 format_string = "<cx_Oracle.Cursor on <cx_Oracle.Connection to %s@%s>>"
595 expected_value = format_string % \
596 (test_env.get_main_user(), test_env.get_connect_string())
597 self.assertEqual(str(self.cursor), expected_value)
598
599 def test_1253_cursor_fetch_raw(self):
600 "1253 - test cursor.fetchraw()"
601 cursor = self.connection.cursor()
602 cursor.arraysize = 25
603 cursor.execute("select LongIntCol from TestNumbers order by IntCol")
604 self.assertEqual(cursor.fetchraw(), 10)
605 self.assertEqual(cursor.fetchvars[0].getvalue(), 38)
606
607 def test_1254_parse(self):
608 "1254 - test parsing statements"
609 sql = "select LongIntCol from TestNumbers where IntCol = :val"
610 self.cursor.parse(sql)
611 self.assertEqual(self.cursor.statement, sql)
612 self.assertEqual(self.cursor.description,
613 [('LONGINTCOL', oracledb.DB_TYPE_NUMBER, 17, None,
614 16, 0, 0)])
615
616 def test_1255_set_output_size(self):
617 "1255 - test cursor.setoutputsize() does not fail (but does nothing)"
618 self.cursor.setoutputsize(100, 2)
619
620 def test_1256_var_negative(self):
621 "1256 - test cursor.var() with invalid parameters"
622 self.assertRaises(TypeError, self.cursor.var, 5)
623
624 def test_1257_arrayvar_negative(self):
625 "1257 - test cursor.arrayvar() with invalid parameters"
626 self.assertRaises(TypeError, self.cursor.arrayvar, 5, 1)
627
628 def test_1258_boolean_without_plsql(self):
629 "1258 - test binding boolean data without the use of PL/SQL"
630 self.cursor.execute("truncate table TestTempTable")
631 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
632 self.cursor.execute(sql, (False, "Value should be 0"))
633 self.cursor.execute(sql, (True, "Value should be 1"))
634 self.cursor.execute("""
635 select IntCol, StringCol
636 from TestTempTable
637 order by IntCol""")
638 self.assertEqual(self.cursor.fetchall(),
639 [(0, "Value should be 0"), (1, "Value should be 1")])
640
641 def test_1259_as_context_manager(self):
642 "1259 - test using a cursor as a context manager"
643 with self.cursor as cursor:
644 cursor.execute("truncate table TestTempTable")
645 cursor.execute("select count(*) from TestTempTable")
646 count, = cursor.fetchone()
647 self.assertEqual(count, 0)
648 self.assertRaises(oracledb.InterfaceError, self.cursor.close)
649
650 def test_1260_query_row_count(self):
651 "1260 - test that rowcount attribute is reset to zero on query execute"
652 sql = "select * from dual where 1 = :s"
653 self.cursor.execute(sql, [0])
654 self.cursor.fetchone()
655 self.assertEqual(self.cursor.rowcount, 0)
656 self.cursor.execute(sql, [1])
657 self.cursor.fetchone()
658 self.assertEqual(self.cursor.rowcount, 1)
659 self.cursor.execute(sql, [1])
660 self.cursor.fetchone()
661 self.assertEqual(self.cursor.rowcount, 1)
662 self.cursor.execute(sql, [0])
663 self.cursor.fetchone()
664 self.assertEqual(self.cursor.rowcount, 0)
665
666 def test_1261_var_type_name_none(self):
667 "1261 - test that the typename attribute can be passed a value of None"
668 value_to_set = 5
669 var = self.cursor.var(int, typename=None)
670 var.setvalue(0, value_to_set)
671 self.assertEqual(var.getvalue(), value_to_set)
672
673 def test_1262_var_type_with_object_type(self):
674 "1262 - test that an object type can be used as type in cursor.var()"
675 obj_type = self.connection.gettype("UDT_OBJECT")
676 var = self.cursor.var(obj_type)
677 self.cursor.callproc("pkg_TestBindObject.BindObjectOut",
678 (28, "Bind obj out", var))
679 obj = var.getvalue()
680 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
681 (obj,))
682 exp = "udt_Object(28, 'Bind obj out', null, null, null, null, null)"
683 self.assertEqual(result, exp)
684
685 def test_1263_fetch_xmltype(self):
686 "1263 - test that fetching an XMLType returns a string"
687 int_val = 5
688 label = "IntCol"
689 expected_result = "<%s>%s</%s>" % (label, int_val, label)
690 self.cursor.execute("""
691 select XMLElement("%s", IntCol)
692 from TestStrings
693 where IntCol = :int_val""" % label,
694 int_val=int_val)
695 result, = self.cursor.fetchone()
696 self.assertEqual(result, expected_result)
697
698 def test_1264_lastrowid(self):
699 "1264 - test last rowid"
700
701 # no statement executed: no rowid
702 self.assertEqual(None, self.cursor.lastrowid)
703
704 # DDL statement executed: no rowid
705 self.cursor.execute("truncate table TestTempTable")
706 self.assertEqual(None, self.cursor.lastrowid)
707
708 # statement prepared: no rowid
709 self.cursor.prepare("insert into TestTempTable (IntCol) values (:1)")
710 self.assertEqual(None, self.cursor.lastrowid)
711
712 # multiple rows inserted: rowid of last row inserted
713 rows = [(n,) for n in range(225)]
714 self.cursor.executemany(None, rows)
715 rowid = self.cursor.lastrowid
716 self.cursor.execute("""
717 select rowid
718 from TestTempTable
719 where IntCol = :1""", rows[-1])
720 self.assertEqual(rowid, self.cursor.fetchone()[0])
721
722 # statement executed but no rows updated: no rowid
723 self.cursor.execute("delete from TestTempTable where 1 = 0")
724 self.assertEqual(None, self.cursor.lastrowid)
725
726 # stetement executed with one row updated: rowid of updated row
727 self.cursor.execute("""
728 update TestTempTable set
729 StringCol = 'Modified'
730 where IntCol = :1""", rows[-2])
731 rowid = self.cursor.lastrowid
732 self.cursor.execute("""
733 select rowid
734 from TestTempTable
735 where IntCol = :1""", rows[-2])
736 self.assertEqual(rowid, self.cursor.fetchone()[0])
737
738 # statement executed with many rows updated: rowid of last updated row
739 self.cursor.execute("""
740 update TestTempTable set
741 StringCol = 'Row ' || to_char(IntCol)
742 where IntCol = :1""", rows[-3])
743 rowid = self.cursor.lastrowid
744 self.cursor.execute("""
745 select StringCol
746 from TestTempTable
747 where rowid = :1""", [rowid])
748 self.assertEqual("Row %s" % rows[-3], self.cursor.fetchone()[0])
749
750 def test_1265_prefetchrows(self):
751 "1265 - test prefetch rows"
752 self.setup_round_trip_checker()
753
754 # perform simple query and verify only one round trip is needed
755 with self.connection.cursor() as cursor:
756 cursor.execute("select sysdate from dual").fetchall()
757 self.assertRoundTrips(1)
758
759 # set prefetchrows to 1 and verify that two round trips are now needed
760 with self.connection.cursor() as cursor:
761 cursor.prefetchrows = 1
762 cursor.execute("select sysdate from dual").fetchall()
763 self.assertRoundTrips(2)
764
765 # simple DDL only requires a single round trip
766 with self.connection.cursor() as cursor:
767 cursor.execute("truncate table TestTempTable")
768 self.assertRoundTrips(1)
769
770 # array execution only requires a single round trip
771 num_rows = 590
772 with self.connection.cursor() as cursor:
773 sql = "insert into TestTempTable (IntCol) values (:1)"
774 data = [(n + 1,) for n in range(num_rows)]
775 cursor.executemany(sql, data)
776 self.assertRoundTrips(1)
777
778 # setting prefetch and array size to 1 requires a round-trip for each
779 # row
780 with self.connection.cursor() as cursor:
781 cursor.prefetchrows = 1
782 cursor.arraysize = 1
783 cursor.execute("select IntCol from TestTempTable").fetchall()
784 self.assertRoundTrips(num_rows + 1)
785
786 # setting prefetch and array size to 300 requires 2 round-trips
787 with self.connection.cursor() as cursor:
788 cursor.prefetchrows = 300
789 cursor.arraysize = 300
790 cursor.execute("select IntCol from TestTempTable").fetchall()
791 self.assertRoundTrips(2)
792
793 def test_1266_refcursor_prefetchrows(self):
794 "1266 - test prefetch rows and arraysize using a refcursor"
795 self.setup_round_trip_checker()
796
797 # simple DDL only requires a single round trip
798 with self.connection.cursor() as cursor:
799 cursor.execute("truncate table TestTempTable")
800 self.assertRoundTrips(1)
801
802 # array execution only requires a single round trip
803 num_rows = 590
804 with self.connection.cursor() as cursor:
805 sql = "insert into TestTempTable (IntCol) values (:1)"
806 data = [(n + 1,) for n in range(num_rows)]
807 cursor.executemany(sql, data)
808 self.assertRoundTrips(1)
809
810 # create refcursor and execute stored procedure
811 with self.connection.cursor() as cursor:
812 refcursor = self.connection.cursor()
813 refcursor.prefetchrows = 300
814 refcursor.arraysize = 300
815 cursor.callproc("myrefcursorproc", [refcursor])
816 refcursor.fetchall()
817 self.assertRoundTrips(2)
818
819 def test_1267_existing_cursor_prefetchrows(self):
820 "1267 - test prefetch rows using existing cursor"
821 self.setup_round_trip_checker()
822
823 # Set prefetch rows on an existing cursor
824 num_rows = 590
825 with self.connection.cursor() as cursor:
826 cursor.execute("truncate table TestTempTable")
827 sql = "insert into TestTempTable (IntCol) values (:1)"
828 data = [(n + 1,) for n in range(num_rows)]
829 cursor.executemany(sql, data)
830 cursor.prefetchrows = 300
831 cursor.arraysize = 300
832 cursor.execute("select IntCol from TestTempTable").fetchall()
833 self.assertRoundTrips(4)
834
835 def test_1268_bind_names_with_single_line_comments(self):
836 "1268 - test bindnames() with single line comments"
837 self.cursor.prepare("""--begin :value2 := :a + :b + :c +:a +3; end;
838 begin :value2 := :a + :c +3; end;
839 """)
840 self.assertEqual(self.cursor.bindnames(), ["VALUE2", "A", "C"])
841
842 def test_1269_bind_names_with_multi_line_comments(self):
843 "1269 - test bindnames() with multi line comments"
844 self.cursor.prepare("""/*--select * from :a where :a = 1
845 select * from table_names where :a = 1*/
846 select * from :table_name where :value = 1
847 """)
848 self.assertEqual(self.cursor.bindnames(), ["TABLE_NAME", "VALUE"])
849
850 def test_1270_execute_bind_names_with_incorrect_bind(self):
851 "1270 - test executing a statement with an incorrect named bind"
852 statement = "select * from TestStrings where IntCol = :value"
853 self.assertRaises(oracledb.DatabaseError, self.cursor.execute,
854 statement, value2=3)
855
856 def test_1271_execute_with_named_binds(self):
857 "1271 - test executing a statement with named binds"
858 statement = "select * from TestNumbers where IntCol = :value1 " + \
859 "and LongIntCol = :value2"
860 result = self.cursor.execute(statement, value1=1, value2=38)
861 self.assertEqual(len(result.fetchall()), 1)
862
863 def test_1272_execute_bind_position_with_incorrect_bind(self):
864 "1272 - test executing a statement with an incorrect positional bind"
865 statement = "select * from TestNumbers where IntCol = :value " + \
866 "and LongIntCol = :value2"
867 self.assertRaises(oracledb.DatabaseError, self.cursor.execute,
868 statement, [3])
869
870 def test_1273_execute_with_positional_binds(self):
871 "1273 - test executing a statement with positional binds"
872 statement = "select * from TestNumbers where IntCol = :value " + \
873 "and LongIntCol = :value2"
874 result = self.cursor.execute(statement, [1,38])
875 self.assertEqual(len(result.fetchall()), 1)
876
877 def test_1274_execute_with_rebinding_bind_name(self):
878 "1274 - test executing a statement after rebinding a named bind"
879 statement = "begin :value := :value2 + 5; end;"
880 simple_var = self.cursor.var(oracledb.NUMBER)
881 simple_var2 = self.cursor.var(oracledb.NUMBER)
882 simple_var2.setvalue(0, 5)
883 result = self.cursor.execute(statement, value=simple_var,
884 value2=simple_var2)
885 self.assertEqual(result, None)
886 self.assertEqual(simple_var.getvalue(), 10)
887
888 simple_var = self.cursor.var(oracledb.NATIVE_FLOAT)
889 simple_var2 = self.cursor.var(oracledb.NATIVE_FLOAT)
890 simple_var2.setvalue(0, 10)
891 result = self.cursor.execute(statement, value=simple_var,
892 value2=simple_var2)
893 self.assertEqual(result, None)
894 self.assertEqual(simple_var.getvalue(), 15)
895
896 def test_1275_bind_names_with_strings(self):
897 "1275 - test bindnames() with strings in the statement"
898 statement = """
899 begin
900 :value := to_date('20021231 12:31:00',
901 'YYYYMMDD HH24:MI:SS');
902 end;"""
903 self.cursor.prepare(statement)
904 self.assertEqual(self.cursor.bindnames(), ["VALUE"])
905
906 def test_1276_bind_by_name_with_duplicates(self):
907 "1276 - test executing a PL/SQL statement with duplicate binds"
908 statement = "begin :value := :value + 5; end;"
909 simple_var = self.cursor.var(oracledb.NUMBER)
910 simple_var.setvalue(0, 5)
911 result = self.cursor.execute(statement, value=simple_var)
912 self.assertEqual(result, None)
913 self.assertEqual(simple_var.getvalue(), 10)
914
915 def test_1277_positional_bind_with_duplicates(self):
916 "1277 - test executing a PL/SQL statement with duplicate binds"
917 statement = "begin :value := :value + 5; end;"
918 simple_var = self.cursor.var(oracledb.NUMBER)
919 simple_var.setvalue(0, 5)
920 self.cursor.execute(statement, [simple_var])
921 self.assertEqual(simple_var.getvalue(), 10)
922
923 def test_1278_execute_with_incorrect_bind_values(self):
924 "1278 - test executing a statement with an incorrect number of binds"
925 statement = "begin :value := :value2 + 5; end;"
926 var = self.cursor.var(oracledb.NUMBER)
927 var.setvalue(0, 5)
928 self.assertRaises(oracledb.DatabaseError, self.cursor.execute,
929 statement)
930 self.assertRaises(oracledb.DatabaseError, self.cursor.execute,
931 statement, value=var)
932 self.assertRaises(oracledb.DatabaseError, self.cursor.execute,
933 statement, value=var, value2=var, value3=var)
934
935 if __name__ == "__main__":
936 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 1300 - Module for testing cursor variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import sys
17
18 class TestCase(test_env.BaseTestCase):
19
20 def test_1300_bind_cursor(self):
21 "1300 - test binding in a cursor"
22 cursor = self.connection.cursor()
23 self.assertEqual(cursor.description, None)
24 self.cursor.execute("""
25 begin
26 open :cursor for select 'X' StringValue from dual;
27 end;""",
28 cursor=cursor)
29 expected_value = [
30 ('STRINGVALUE', oracledb.DB_TYPE_CHAR, 1,
31 test_env.get_charset_ratio(), None, None, 1)
32 ]
33 self.assertEqual(cursor.description, expected_value)
34 self.assertEqual(cursor.fetchall(), [('X',)])
35
36 def test_1301_bind_cursor_in_package(self):
37 "1301 - test binding in a cursor from a package"
38 cursor = self.connection.cursor()
39 self.assertEqual(cursor.description, None)
40 self.cursor.callproc("pkg_TestRefCursors.TestOutCursor", (2, cursor))
41 expected_value = [
42 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
43 ('STRINGCOL', oracledb.DB_TYPE_VARCHAR, 20,
44 20 * test_env.get_charset_ratio(), None, None, 0)
45 ]
46 self.assertEqual(cursor.description, expected_value)
47 self.assertEqual(cursor.fetchall(), [(1, 'String 1'), (2, 'String 2')])
48
49 def test_1302_bind_self(self):
50 "1302 - test that binding the cursor itself is not supported"
51 cursor = self.connection.cursor()
52 sql = """
53 begin
54 open :pcursor for
55 select 1 from dual;
56 end;"""
57 self.assertRaises(oracledb.DatabaseError, cursor.execute, sql,
58 pcursor=cursor)
59
60 def test_1303_execute_after_close(self):
61 "1303 - test returning a ref cursor after closing it"
62 out_cursor = self.connection.cursor()
63 sql = """
64 begin
65 open :pcursor for
66 select IntCol
67 from TestNumbers
68 order by IntCol;
69 end;"""
70 self.cursor.execute(sql, pcursor=out_cursor)
71 rows = out_cursor.fetchall()
72 out_cursor.close()
73 out_cursor = self.connection.cursor()
74 self.cursor.execute(sql, pcursor=out_cursor)
75 rows2 = out_cursor.fetchall()
76 self.assertEqual(rows, rows2)
77
78 def test_1304_fetch_cursor(self):
79 "1304 - test fetching a cursor"
80 self.cursor.execute("""
81 select
82 IntCol,
83 cursor(select IntCol + 1 from dual) CursorValue
84 from TestNumbers
85 order by IntCol""")
86 expected_value = [
87 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
88 ('CURSORVALUE', oracledb.DB_TYPE_CURSOR, None, None, None, None, 1)
89 ]
90 self.assertEqual(self.cursor.description, expected_value)
91 for i in range(1, 11):
92 number, cursor = self.cursor.fetchone()
93 self.assertEqual(number, i)
94 self.assertEqual(cursor.fetchall(), [(i + 1,)])
95
96 if __name__ == "__main__":
97 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 1400 - Module for testing date/time variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import datetime
17 import time
18
19 class TestCase(test_env.BaseTestCase):
20
21 def setUp(self):
22 super().setUp()
23 self.raw_data = []
24 self.data_by_key = {}
25 for i in range(1, 11):
26 time_tuple = (2002, 12, 9, 0, 0, 0, 0, 0, -1)
27 time_in_ticks = time.mktime(time_tuple) + i * 86400 + i * 8640
28 date_col = oracledb.TimestampFromTicks(int(time_in_ticks))
29 if i % 2:
30 time_in_ticks = time.mktime(time_tuple) + i * 86400 * 2 + \
31 i * 12960
32 nullable_col = oracledb.TimestampFromTicks(int(time_in_ticks))
33 else:
34 nullable_col = None
35 tuple = (i, date_col, nullable_col)
36 self.raw_data.append(tuple)
37 self.data_by_key[i] = tuple
38
39 def test_1400_bind_date(self):
40 "1400 - test binding in a date"
41 self.cursor.execute("""
42 select * from TestDates
43 where DateCol = :value""",
44 value = oracledb.Timestamp(2002, 12, 13, 9, 36, 0))
45 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[4]])
46
47 def test_1401_bind_datetime(self):
48 "1401 - test binding in a datetime.datetime value"
49 self.cursor.execute("""
50 select * from TestDates
51 where DateCol = :value""",
52 value=datetime.datetime(2002, 12, 13, 9, 36, 0))
53 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[4]])
54
55 def test_1402_bind_date_in_datetime_var(self):
56 "1402 - test binding date in a datetime variable"
57 var = self.cursor.var(oracledb.DATETIME)
58 dateVal = datetime.date.today()
59 var.setvalue(0, dateVal)
60 self.assertEqual(var.getvalue().date(), dateVal)
61
62 def test_1403_bind_date_after_string(self):
63 "1403 - test binding in a date after setting input sizes to a string"
64 self.cursor.setinputsizes(value=15)
65 self.cursor.execute("""
66 select * from TestDates
67 where DateCol = :value""",
68 value = oracledb.Timestamp(2002, 12, 14, 12, 0, 0))
69 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]])
70
71 def test_1404_bind_null(self):
72 "1404 - test binding in a null"
73 self.cursor.setinputsizes(value=oracledb.DATETIME)
74 self.cursor.execute("""
75 select * from TestDates
76 where DateCol = :value""",
77 value = None)
78 self.assertEqual(self.cursor.fetchall(), [])
79
80 def test_1405_bind_date_array_direct(self):
81 "1405 - test binding in a date array"
82 return_value = self.cursor.var(oracledb.NUMBER)
83 array = [r[1] for r in self.raw_data]
84 statement = """
85 begin
86 :return_value := pkg_TestDateArrays.TestInArrays(
87 :start_value, :base_date, :array);
88 end;"""
89 self.cursor.execute(statement, return_value=return_value,
90 start_value=5,
91 base_date=oracledb.Date(2002, 12, 12), array=array)
92 self.assertEqual(return_value.getvalue(), 35.5)
93 array = array + array[:5]
94 self.cursor.execute(statement, start_value=7,
95 base_date=oracledb.Date(2002, 12, 13), array=array)
96 self.assertEqual(return_value.getvalue(), 24.0)
97
98 def test_1406_bind_date_array_by_sizes(self):
99 "1406 - test binding in a date array (with setinputsizes)"
100 return_value = self.cursor.var(oracledb.NUMBER)
101 self.cursor.setinputsizes(array=[oracledb.DATETIME, 10])
102 array = [r[1] for r in self.raw_data]
103 self.cursor.execute("""
104 begin
105 :return_value := pkg_TestDateArrays.TestInArrays(
106 :start_value, :base_date, :array);
107 end;""",
108 return_value=return_value,
109 start_value=6,
110 base_date=oracledb.Date(2002, 12, 13),
111 array=array)
112 self.assertEqual(return_value.getvalue(), 26.5)
113
114 def test_1407_bind_date_array_by_var(self):
115 "1407 - test binding in a date array (with arrayvar)"
116 return_value = self.cursor.var(oracledb.NUMBER)
117 array = self.cursor.arrayvar(oracledb.DATETIME, 10, 20)
118 array.setvalue(0, [r[1] for r in self.raw_data])
119 self.cursor.execute("""
120 begin
121 :return_value := pkg_TestDateArrays.TestInArrays(
122 :start_value, :base_date, :array);
123 end;""",
124 return_value=return_value,
125 start_value=7,
126 base_date=oracledb.Date(2002, 12, 14),
127 array=array)
128 self.assertEqual(return_value.getvalue(), 17.5)
129
130 def test_1408_bind_in_out_date_array_by_var(self):
131 "1408 - test binding in/out a date array (with arrayvar)"
132 array = self.cursor.arrayvar(oracledb.DATETIME, 10, 100)
133 original_data = [r[1] for r in self.raw_data]
134 array.setvalue(0, original_data)
135 self.cursor.execute("""
136 begin
137 pkg_TestDateArrays.TestInOutArrays(:num_elems, :array);
138 end;""",
139 num_elems=5,
140 array=array)
141 self.assertEqual(array.getvalue(),
142 [ oracledb.Timestamp(2002, 12, 17, 2, 24, 0),
143 oracledb.Timestamp(2002, 12, 18, 4, 48, 0),
144 oracledb.Timestamp(2002, 12, 19, 7, 12, 0),
145 oracledb.Timestamp(2002, 12, 20, 9, 36, 0),
146 oracledb.Timestamp(2002, 12, 21, 12, 0, 0) ] + \
147 original_data[5:])
148
149 def test_1409_bind_out_date_array_by_var(self):
150 "1409 - test binding out a date array (with arrayvar)"
151 array = self.cursor.arrayvar(oracledb.DATETIME, 6, 100)
152 self.cursor.execute("""
153 begin
154 pkg_TestDateArrays.TestOutArrays(:num_elems, :array);
155 end;""",
156 num_elems=6,
157 array=array)
158 self.assertEqual(array.getvalue(),
159 [ oracledb.Timestamp(2002, 12, 13, 4, 48, 0),
160 oracledb.Timestamp(2002, 12, 14, 9, 36, 0),
161 oracledb.Timestamp(2002, 12, 15, 14, 24, 0),
162 oracledb.Timestamp(2002, 12, 16, 19, 12, 0),
163 oracledb.Timestamp(2002, 12, 18, 0, 0, 0),
164 oracledb.Timestamp(2002, 12, 19, 4, 48, 0) ])
165
166 def test_1410_bind_out_set_input_sizes(self):
167 "1410 - test binding out with set input sizes defined"
168 bind_vars = self.cursor.setinputsizes(value=oracledb.DATETIME)
169 self.cursor.execute("""
170 begin
171 :value := to_date(20021209, 'YYYYMMDD');
172 end;""")
173 self.assertEqual(bind_vars["value"].getvalue(),
174 oracledb.Timestamp(2002, 12, 9))
175
176 def test_1411_bind_in_out_set_input_sizes(self):
177 "1411 - test binding in/out with set input sizes defined"
178 bind_vars = self.cursor.setinputsizes(value=oracledb.DATETIME)
179 self.cursor.execute("""
180 begin
181 :value := :value + 5.25;
182 end;""",
183 value=oracledb.Timestamp(2002, 12, 12, 10, 0, 0))
184 self.assertEqual(bind_vars["value"].getvalue(),
185 oracledb.Timestamp(2002, 12, 17, 16, 0, 0))
186
187 def test_1412_bind_out_var(self):
188 "1412 - test binding out with cursor.var() method"
189 var = self.cursor.var(oracledb.DATETIME)
190 self.cursor.execute("""
191 begin
192 :value := to_date('20021231 12:31:00',
193 'YYYYMMDD HH24:MI:SS');
194 end;""",
195 value=var)
196 self.assertEqual(var.getvalue(),
197 oracledb.Timestamp(2002, 12, 31, 12, 31, 0))
198
199 def test_1413_bind_in_out_var_direct_set(self):
200 "1413 - test binding in/out with cursor.var() method"
201 var = self.cursor.var(oracledb.DATETIME)
202 var.setvalue(0, oracledb.Timestamp(2002, 12, 9, 6, 0, 0))
203 self.cursor.execute("""
204 begin
205 :value := :value + 5.25;
206 end;""",
207 value=var)
208 self.assertEqual(var.getvalue(),
209 oracledb.Timestamp(2002, 12, 14, 12, 0, 0))
210
211 def test_1414_cursor_description(self):
212 "1414 - test cursor description is accurate"
213 self.cursor.execute("select * from TestDates")
214 expected_value = [
215 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
216 ('DATECOL', oracledb.DB_TYPE_DATE, 23, None, None, None, 0),
217 ('NULLABLECOL', oracledb.DB_TYPE_DATE, 23, None, None, None, 1)
218 ]
219 self.assertEqual(self.cursor.description, expected_value)
220
221 def test_1415_fetchall(self):
222 "1415 - test that fetching all of the data returns the correct results"
223 self.cursor.execute("select * From TestDates order by IntCol")
224 self.assertEqual(self.cursor.fetchall(), self.raw_data)
225 self.assertEqual(self.cursor.fetchall(), [])
226
227 def test_1416_fetchmany(self):
228 "1416 - test that fetching data in chunks returns the correct results"
229 self.cursor.execute("select * From TestDates order by IntCol")
230 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3])
231 self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5])
232 self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9])
233 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:])
234 self.assertEqual(self.cursor.fetchmany(3), [])
235
236 def test_1417_fetchone(self):
237 "1417 - test that fetching a single row returns the correct results"
238 self.cursor.execute("""
239 select *
240 from TestDates
241 where IntCol in (3, 4)
242 order by IntCol""")
243 self.assertEqual(self.cursor.fetchone(), self.data_by_key[3])
244 self.assertEqual(self.cursor.fetchone(), self.data_by_key[4])
245 self.assertEqual(self.cursor.fetchone(), None)
246
247 if __name__ == "__main__":
248 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 1500 - Module for testing comparisons with database types and API types,
6 including the synonyms retained for backwards compatibility. This module also
7 tests for pickling/unpickling of database types and API types.
8 """
9
10 import test_env
11
12 import cx_Oracle as oracledb
13 import pickle
14
15 class TestCase(test_env.BaseTestCase):
16 requires_connection = False
17
18 def __test_compare(self, db_type, api_type):
19 self.assertEqual(db_type, db_type)
20 self.assertEqual(db_type, api_type)
21 self.assertEqual(api_type, db_type)
22 self.assertNotEqual(db_type, 5)
23 self.assertNotEqual(db_type, oracledb.DB_TYPE_OBJECT)
24
25 def __test_pickle(self, typ):
26 self.assertIs(typ, pickle.loads(pickle.dumps(typ)))
27
28 def test_1500_DB_TYPE_BFILE(self):
29 "1500 - test oracledb.DB_TYPE_BFILE comparisons and pickling"
30 self.assertEqual(oracledb.DB_TYPE_BFILE, oracledb.BFILE)
31 self.__test_pickle(oracledb.DB_TYPE_BFILE)
32
33 def test_1501_DB_TYPE_BINARY_DOUBLE(self):
34 "1501 - test oracledb.DB_TYPE_BINARY_DOUBLE comparisons and pickling"
35 self.__test_compare(oracledb.DB_TYPE_BINARY_DOUBLE, oracledb.NUMBER)
36 self.assertEqual(oracledb.DB_TYPE_BINARY_DOUBLE,
37 oracledb.NATIVE_FLOAT)
38 self.__test_pickle(oracledb.DB_TYPE_BINARY_DOUBLE)
39
40 def test_1502_DB_TYPE_BINARY_FLOAT(self):
41 "1502 - test oracledb.DB_TYPE_BINARY_FLOAT comparisons and pickling"
42 self.__test_compare(oracledb.DB_TYPE_BINARY_FLOAT, oracledb.NUMBER)
43 self.__test_pickle(oracledb.DB_TYPE_BINARY_FLOAT)
44
45 def test_1503_DB_TYPE_BINARY_INTEGER(self):
46 "1503 - test oracledb.DB_TYPE_BINARY_INTEGER comparisons and pickling"
47 self.__test_compare(oracledb.DB_TYPE_BINARY_INTEGER, oracledb.NUMBER)
48 self.assertEqual(oracledb.DB_TYPE_BINARY_INTEGER,
49 oracledb.NATIVE_INT)
50 self.__test_pickle(oracledb.DB_TYPE_BINARY_INTEGER)
51
52 def test_1504_DB_TYPE_BLOB(self):
53 "1504 - test oracledb.DB_TYPE_BLOB comparisons and pickling"
54 self.assertEqual(oracledb.DB_TYPE_BLOB, oracledb.BLOB)
55 self.__test_pickle(oracledb.DB_TYPE_BLOB)
56
57 def test_1505_DB_TYPE_BOOLEAN(self):
58 "1505 - test oracledb.DB_TYPE_BOOLEAN comparisons and pickling"
59 self.assertEqual(oracledb.DB_TYPE_BOOLEAN, oracledb.BOOLEAN)
60 self.__test_pickle(oracledb.DB_TYPE_BOOLEAN)
61
62 def test_1506_DB_TYPE_CHAR(self):
63 "1506 - test oracledb.DB_TYPE_CHAR comparisons and pickling"
64 self.__test_compare(oracledb.DB_TYPE_CHAR, oracledb.STRING)
65 self.assertEqual(oracledb.DB_TYPE_CHAR, oracledb.FIXED_CHAR)
66 self.__test_pickle(oracledb.DB_TYPE_CHAR)
67
68 def test_1507_DB_TYPE_CLOB(self):
69 "1507 - test oracledb.DB_TYPE_CLOB comparisons and pickling"
70 self.assertEqual(oracledb.DB_TYPE_CLOB, oracledb.CLOB)
71 self.__test_pickle(oracledb.DB_TYPE_CLOB)
72
73 def test_1508_DB_TYPE_CURSOR(self):
74 "1508 - test oracledb.DB_TYPE_CURSOR comparisons and pickling"
75 self.assertEqual(oracledb.DB_TYPE_CURSOR, oracledb.CURSOR)
76 self.__test_pickle(oracledb.DB_TYPE_CURSOR)
77
78 def test_1509_DB_TYPE_DATE(self):
79 "1509 - test oracledb.DB_TYPE_DATE comparisons and pickling"
80 self.__test_compare(oracledb.DB_TYPE_DATE, oracledb.DATETIME)
81 self.__test_pickle(oracledb.DB_TYPE_DATE)
82
83 def test_1510_DB_TYPE_INTERVAL_DS(self):
84 "1510 - test oracledb.DB_TYPE_INTERVAL_DS comparisons and pickling"
85 self.assertEqual(oracledb.DB_TYPE_INTERVAL_DS, oracledb.INTERVAL)
86 self.__test_pickle(oracledb.DB_TYPE_INTERVAL_DS)
87
88 def test_1511_DB_TYPE_LONG(self):
89 "1511 - test oracledb.DB_TYPE_LONG comparisons and pickling"
90 self.__test_compare(oracledb.DB_TYPE_LONG, oracledb.STRING)
91 self.assertEqual(oracledb.DB_TYPE_LONG, oracledb.LONG_STRING)
92 self.__test_pickle(oracledb.DB_TYPE_LONG)
93
94 def test_1512_DB_TYPE_LONG_RAW(self):
95 "1512 - test oracledb.DB_TYPE_LONG_RAW comparisons and pickling"
96 self.__test_compare(oracledb.DB_TYPE_LONG_RAW, oracledb.BINARY)
97 self.assertEqual(oracledb.DB_TYPE_LONG_RAW, oracledb.LONG_BINARY)
98 self.__test_pickle(oracledb.DB_TYPE_LONG_RAW)
99
100 def test_1513_DB_TYPE_NCHAR(self):
101 "1513 - test oracledb.DB_TYPE_NCHAR comparisons and pickling"
102 self.__test_compare(oracledb.DB_TYPE_NCHAR, oracledb.STRING)
103 self.assertEqual(oracledb.DB_TYPE_NCHAR, oracledb.FIXED_NCHAR)
104 self.__test_pickle(oracledb.DB_TYPE_NCHAR)
105
106 def test_1514_DB_TYPE_NCLOB(self):
107 "1514 - test oracledb.DB_TYPE_NCLOB comparisons and pickling"
108 self.assertEqual(oracledb.DB_TYPE_NCLOB, oracledb.NCLOB)
109 self.__test_pickle(oracledb.DB_TYPE_NCLOB)
110
111 def test_1515_DB_TYPE_NUMBER(self):
112 "1515 - test oracledb.DB_TYPE_NUMBER comparisons and pickling"
113 self.__test_compare(oracledb.DB_TYPE_NUMBER, oracledb.NUMBER)
114 self.__test_pickle(oracledb.DB_TYPE_NUMBER)
115
116 def test_1516_DB_TYPE_NVARCHAR(self):
117 "1516 - test oracledb.DB_TYPE_NVARCHAR comparisons and pickling"
118 self.__test_compare(oracledb.DB_TYPE_NVARCHAR, oracledb.STRING)
119 self.assertEqual(oracledb.DB_TYPE_NVARCHAR, oracledb.NCHAR)
120 self.__test_pickle(oracledb.DB_TYPE_NVARCHAR)
121
122 def test_1517_DB_TYPE_OBJECT(self):
123 "1517 - test oracledb.DB_TYPE_OBJECT comparisons and pickling"
124 self.assertEqual(oracledb.DB_TYPE_OBJECT, oracledb.OBJECT)
125 self.__test_pickle(oracledb.DB_TYPE_OBJECT)
126
127 def test_1518_DB_TYPE_RAW(self):
128 "1518 - test oracledb.DB_TYPE_RAW comparisons and pickling"
129 self.__test_compare(oracledb.DB_TYPE_RAW, oracledb.BINARY)
130 self.__test_pickle(oracledb.DB_TYPE_RAW)
131
132 def test_1519_DB_TYPE_ROWID(self):
133 "1519 - test oracledb.DB_TYPE_ROWID comparisons and pickling"
134 self.__test_compare(oracledb.DB_TYPE_ROWID, oracledb.ROWID)
135 self.__test_pickle(oracledb.DB_TYPE_ROWID)
136
137 def test_1520_DB_TYPE_TIMESTAMP(self):
138 "1520 - test oracledb.DB_TYPE_TIMESTAMP comparisons and pickling"
139 self.__test_compare(oracledb.DB_TYPE_TIMESTAMP, oracledb.DATETIME)
140 self.assertEqual(oracledb.DB_TYPE_TIMESTAMP, oracledb.TIMESTAMP)
141 self.__test_pickle(oracledb.DB_TYPE_TIMESTAMP)
142
143 def test_1521_DB_TYPE_TIMESTAMP_LTZ(self):
144 "1521 - test oracledb.DB_TYPE_TIMESTAMP_LTZ comparisons and pickling"
145 self.__test_compare(oracledb.DB_TYPE_TIMESTAMP_LTZ, oracledb.DATETIME)
146 self.__test_pickle(oracledb.DB_TYPE_TIMESTAMP_LTZ)
147
148 def test_1522_DB_TYPE_TIMESTAMP_TZ(self):
149 "1522 - test oracledb.DB_TYPE_TIMESTAMP_TZ comparisons and pickling"
150 self.__test_compare(oracledb.DB_TYPE_TIMESTAMP_TZ, oracledb.DATETIME)
151 self.__test_pickle(oracledb.DB_TYPE_TIMESTAMP_TZ)
152
153 def test_1523_DB_TYPE_VARCHAR(self):
154 "1523 - test oracledb.DB_TYPE_VARCHAR comparisons and pickling"
155 self.__test_compare(oracledb.DB_TYPE_VARCHAR, oracledb.STRING)
156 self.__test_pickle(oracledb.DB_TYPE_VARCHAR)
157
158 def test_1524_NUMBER(self):
159 "1524 - test oracledb.NUMBER pickling"
160 self.__test_pickle(oracledb.NUMBER)
161
162 def test_1525_STRING(self):
163 "1525 - test oracledb.STRING pickling"
164 self.__test_pickle(oracledb.STRING)
165
166 def test_1526_DATETIME(self):
167 "1526 - test oracledb.DATETIME pickling"
168 self.__test_pickle(oracledb.DATETIME)
169
170 def test_1527_BINARY(self):
171 "1527 - test oracledb.BINARY pickling"
172 self.__test_pickle(oracledb.BINARY)
173
174 def test_1528_ROWID(self):
175 "1528 - test oracledb.ROWID pickling"
176 self.__test_pickle(oracledb.ROWID)
177
178 if __name__ == "__main__":
179 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 1600 - Module for testing DML returning clauses
6 """
7
8 import test_env
9
10 import cx_Oracle as oracledb
11
12 class TestCase(test_env.BaseTestCase):
13
14 def test_1600_insert(self):
15 "1600 - test insert (single row) with DML returning"
16 self.cursor.execute("truncate table TestTempTable")
17 int_val = 5
18 str_val = "A test string"
19 int_var = self.cursor.var(oracledb.NUMBER)
20 str_var = self.cursor.var(str)
21 self.cursor.execute("""
22 insert into TestTempTable (IntCol, StringCol)
23 values (:int_val, :str_val)
24 returning IntCol, StringCol into :int_var, :str_var""",
25 int_val=int_val,
26 str_val=str_val,
27 int_var=int_var,
28 str_var=str_var)
29 self.assertEqual(int_var.values, [[int_val]])
30 self.assertEqual(str_var.values, [[str_val]])
31
32 def test_1601_insert_many(self):
33 "1601 - test insert (multiple rows) with DML returning"
34 self.cursor.execute("truncate table TestTempTable")
35 int_values = [5, 8, 17, 24, 6]
36 str_values = ["Test 5", "Test 8", "Test 17", "Test 24", "Test 6"]
37 int_var = self.cursor.var(oracledb.NUMBER, arraysize=len(int_values))
38 str_var = self.cursor.var(str, arraysize=len(int_values))
39 self.cursor.setinputsizes(None, None, int_var, str_var)
40 data = list(zip(int_values, str_values))
41 self.cursor.executemany("""
42 insert into TestTempTable (IntCol, StringCol)
43 values (:int_val, :str_val)
44 returning IntCol, StringCol into :int_var, :str_var""", data)
45 self.assertEqual(int_var.values, [[v] for v in int_values])
46 self.assertEqual(str_var.values, [[v] for v in str_values])
47
48 def test_1602_insert_with_small_size(self):
49 "1602 - test insert with DML returning into too small a variable"
50 self.cursor.execute("truncate table TestTempTable")
51 int_val = 6
52 str_val = "A different test string"
53 int_var = self.cursor.var(oracledb.NUMBER)
54 str_var = self.cursor.var(str, 2)
55 parameters = dict(int_val=int_val, str_val=str_val, int_var=int_var,
56 str_var=str_var)
57 self.assertRaises(oracledb.DatabaseError, self.cursor.execute, """
58 insert into TestTempTable (IntCol, StringCol)
59 values (:int_val, :str_val)
60 returning IntCol, StringCol into :int_var, :str_var""",
61 parameters)
62
63 def test_1603_update_single_row(self):
64 "1603 - test update single row with DML returning"
65 int_val = 7
66 str_val = "The updated value of the string"
67 self.cursor.execute("truncate table TestTempTable")
68 self.cursor.execute("""
69 insert into TestTempTable (IntCol, StringCol)
70 values (:1, :2)""",
71 (int_val, "The initial value of the string"))
72 int_var = self.cursor.var(oracledb.NUMBER)
73 str_var = self.cursor.var(str)
74 self.cursor.execute("""
75 update TestTempTable set
76 StringCol = :str_val
77 where IntCol = :int_val
78 returning IntCol, StringCol into :int_var, :str_var""",
79 int_val=int_val,
80 str_val=str_val,
81 int_var=int_var,
82 str_var=str_var)
83 self.assertEqual(int_var.values, [[int_val]])
84 self.assertEqual(str_var.values, [[str_val]])
85
86 def test_1604_update_no_rows(self):
87 "1604 - test update no rows with DML returning"
88 int_val = 8
89 str_val = "The updated value of the string"
90 self.cursor.execute("truncate table TestTempTable")
91 self.cursor.execute("""
92 insert into TestTempTable (IntCol, StringCol)
93 values (:1, :2)""",
94 (int_val, "The initial value of the string"))
95 int_var = self.cursor.var(oracledb.NUMBER)
96 str_var = self.cursor.var(str)
97 self.cursor.execute("""
98 update TestTempTable set
99 StringCol = :str_val
100 where IntCol = :int_val
101 returning IntCol, StringCol into :int_var, :str_var""",
102 int_val=int_val + 1,
103 str_val=str_val,
104 int_var=int_var,
105 str_var=str_var)
106 self.assertEqual(int_var.values, [[]])
107 self.assertEqual(str_var.values, [[]])
108 self.assertEqual(int_var.getvalue(), [])
109 self.assertEqual(str_var.getvalue(), [])
110
111 def test_1605_update_multiple_rows(self):
112 "1605 - test update multiple rows with DML returning"
113 self.cursor.execute("truncate table TestTempTable")
114 for i in (8, 9, 10):
115 self.cursor.execute("""
116 insert into TestTempTable (IntCol, StringCol)
117 values (:1, :2)""",
118 (i, "The initial value of string %d" % i))
119 int_var = self.cursor.var(oracledb.NUMBER)
120 str_var = self.cursor.var(str)
121 self.cursor.execute("""
122 update TestTempTable set
123 IntCol = IntCol + 15,
124 StringCol = 'The final value of string ' || to_char(IntCol)
125 returning IntCol, StringCol into :int_var, :str_var""",
126 int_var=int_var,
127 str_var=str_var)
128 self.assertEqual(self.cursor.rowcount, 3)
129 self.assertEqual(int_var.values, [[23, 24, 25]])
130 expected_values = [[
131 "The final value of string 8",
132 "The final value of string 9",
133 "The final value of string 10"
134 ]]
135 self.assertEqual(str_var.values, expected_values)
136
137 def test_1606_update_multiple_rows_executemany(self):
138 "1606 - test update multiple rows with DML returning (executeMany)"
139 data = [(i, "The initial value of string %d" % i) \
140 for i in range(1, 11)]
141 self.cursor.execute("truncate table TestTempTable")
142 self.cursor.executemany("""
143 insert into TestTempTable (IntCol, StringCol)
144 values (:1, :2)""", data)
145 int_var = self.cursor.var(oracledb.NUMBER, arraysize=3)
146 str_var = self.cursor.var(str, arraysize=3)
147 self.cursor.setinputsizes(None, int_var, str_var)
148 self.cursor.executemany("""
149 update TestTempTable set
150 IntCol = IntCol + 25,
151 StringCol = 'Updated value of string ' || to_char(IntCol)
152 where IntCol < :inVal
153 returning IntCol, StringCol into :int_var, :str_var""",
154 [[3], [8], [11]])
155 expected_values = [
156 [26, 27],
157 [28, 29, 30, 31, 32],
158 [33, 34, 35]
159 ]
160 self.assertEqual(int_var.values, expected_values)
161 expected_values = [
162 [
163 "Updated value of string 1",
164 "Updated value of string 2"
165 ],
166 [
167 "Updated value of string 3",
168 "Updated value of string 4",
169 "Updated value of string 5",
170 "Updated value of string 6",
171 "Updated value of string 7"
172 ],
173 [
174 "Updated value of string 8",
175 "Updated value of string 9",
176 "Updated value of string 10"
177 ]
178 ]
179 self.assertEqual(str_var.values, expected_values)
180
181 def test_1607_insert_and_return_object(self):
182 "1607 - test inserting an object with DML returning"
183 type_obj = self.connection.gettype("UDT_OBJECT")
184 string_value = "The string that will be verified"
185 obj = type_obj.newobject()
186 obj.STRINGVALUE = string_value
187 out_var = self.cursor.var(oracledb.DB_TYPE_OBJECT,
188 typename="UDT_OBJECT")
189 self.cursor.execute("""
190 insert into TestObjects (IntCol, ObjectCol)
191 values (4, :obj)
192 returning ObjectCol into :outObj""",
193 obj=obj, outObj=out_var)
194 result, = out_var.getvalue()
195 self.assertEqual(result.STRINGVALUE, string_value)
196 self.connection.rollback()
197
198 def test_1608_insert_and_return_rowid(self):
199 "1608 - test inserting a row and returning a rowid"
200 self.cursor.execute("truncate table TestTempTable")
201 var = self.cursor.var(oracledb.ROWID)
202 self.cursor.execute("""
203 insert into TestTempTable (IntCol, StringCol)
204 values (278, 'String 278')
205 returning rowid into :1""", (var,))
206 rowid, = var.getvalue()
207 self.cursor.execute("""
208 select IntCol, StringCol
209 from TestTempTable
210 where rowid = :1""",
211 (rowid,))
212 self.assertEqual(self.cursor.fetchall(), [(278, 'String 278')])
213
214 def test_1609_insert_with_ref_cursor(self):
215 "1609 - test inserting with a REF cursor and returning a rowid"
216 self.cursor.execute("truncate table TestTempTable")
217 var = self.cursor.var(oracledb.ROWID)
218 in_cursor = self.connection.cursor()
219 in_cursor.execute("""
220 select StringCol
221 from TestStrings
222 where IntCol >= 5
223 order by IntCol""")
224 self.cursor.execute("""
225 insert into TestTempTable (IntCol, StringCol)
226 values (187, pkg_TestRefCursors.TestInCursor(:1))
227 returning rowid into :2""", (in_cursor, var))
228 rowid, = var.getvalue()
229 self.cursor.execute("""
230 select IntCol, StringCol
231 from TestTempTable
232 where rowid = :1""",
233 (rowid,))
234 self.assertEqual(self.cursor.fetchall(),
235 [(187, 'String 7 (Modified)')])
236
237 def test_1610_delete_returning_decreasing_rows_returned(self):
238 "1610 - test delete returning decreasing number of rows"
239 data = [(i, "Test String %d" % i) for i in range(1, 11)]
240 self.cursor.execute("truncate table TestTempTable")
241 self.cursor.executemany("""
242 insert into TestTempTable (IntCol, StringCol)
243 values (:1, :2)""", data)
244 results = []
245 int_var = self.cursor.var(int)
246 self.cursor.setinputsizes(None, int_var)
247 for int_val in (5, 8, 10):
248 self.cursor.execute("""
249 delete from TestTempTable
250 where IntCol < :1
251 returning IntCol into :2""", [int_val])
252 results.append(int_var.getvalue())
253 self.assertEqual(results, [[1, 2, 3, 4], [5, 6, 7], [8, 9]])
254
255 def test_1611_delete_returning_no_rows_after_many_rows(self):
256 "1611 - test delete returning no rows after returning many rows"
257 data = [(i, "Test String %d" % i) for i in range(1, 11)]
258 self.cursor.execute("truncate table TestTempTable")
259 self.cursor.executemany("""
260 insert into TestTempTable (IntCol, StringCol)
261 values (:1, :2)""", data)
262 int_var = self.cursor.var(int)
263 self.cursor.execute("""
264 delete from TestTempTable
265 where IntCol < :1
266 returning IntCol into :2""", [5, int_var])
267 self.assertEqual(int_var.getvalue(), [1, 2, 3, 4])
268 self.cursor.execute(None, [4, int_var])
269 self.assertEqual(int_var.getvalue(), [])
270
271 if __name__ == "__main__":
272 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 1700 - Module for testing error objects
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import pickle
17
18 class TestCase(test_env.BaseTestCase):
19
20 def test_1700_parse_error(self):
21 "1700 - test parse error returns offset correctly"
22 with self.assertRaises(oracledb.Error) as cm:
23 self.cursor.execute("begin t_Missing := 5; end;")
24 error_obj, = cm.exception.args
25 self.assertEqual(error_obj.offset, 6)
26
27 def test_1701_pickle_error(self):
28 "1701 - test picking/unpickling an error object"
29 with self.assertRaises(oracledb.Error) as cm:
30 self.cursor.execute("""
31 begin
32 raise_application_error(-20101, 'Test!');
33 end;""")
34 error_obj, = cm.exception.args
35 self.assertEqual(type(error_obj), oracledb._Error)
36 self.assertTrue("Test!" in error_obj.message)
37 self.assertEqual(error_obj.code, 20101)
38 self.assertEqual(error_obj.offset, 0)
39 self.assertTrue(isinstance(error_obj.isrecoverable, bool))
40 new_error_obj = pickle.loads(pickle.dumps(error_obj))
41 self.assertEqual(type(new_error_obj), oracledb._Error)
42 self.assertTrue(new_error_obj.message == error_obj.message)
43 self.assertTrue(new_error_obj.code == error_obj.code)
44 self.assertTrue(new_error_obj.offset == error_obj.offset)
45 self.assertTrue(new_error_obj.context == error_obj.context)
46 self.assertTrue(new_error_obj.isrecoverable == error_obj.isrecoverable)
47
48 if __name__ == "__main__":
49 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 1800 - Module for testing interval variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import datetime
17
18 class TestCase(test_env.BaseTestCase):
19
20 def setUp(self):
21 super().setUp()
22 self.raw_data = []
23 self.data_by_key = {}
24 for i in range(1, 11):
25 delta = datetime.timedelta(days=i, hours=i, minutes=i * 2,
26 seconds=i * 3)
27 if i % 2 == 0:
28 nullable_delta = None
29 else:
30 nullable_delta = datetime.timedelta(days=i + 5, hours=i + 2,
31 minutes=i * 2 + 5,
32 seconds=i * 3 + 5)
33 data_tuple = (i, delta, nullable_delta)
34 self.raw_data.append(data_tuple)
35 self.data_by_key[i] = data_tuple
36
37 def test_1800_bind_interval(self):
38 "1800 - test binding in an interval"
39 self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS)
40 value = datetime.timedelta(days=5, hours=5, minutes=10, seconds=15)
41 self.cursor.execute("""
42 select * from TestIntervals
43 where IntervalCol = :value""",
44 value=value)
45 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]])
46
47 def test_1801_bind_null(self):
48 "1801 - test binding in a null"
49 self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS)
50 self.cursor.execute("""
51 select * from TestIntervals
52 where IntervalCol = :value""",
53 value=None)
54 self.assertEqual(self.cursor.fetchall(), [])
55
56 def test_1802_bind_out_set_input_sizes(self):
57 "1802 - test binding out with set input sizes defined"
58 bind_vars = \
59 self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS)
60 self.cursor.execute("""
61 begin
62 :value := to_dsinterval('8 09:24:18.123789');
63 end;""")
64 expected_value = datetime.timedelta(days=8, hours=9, minutes=24,
65 seconds=18, microseconds=123789)
66 self.assertEqual(bind_vars["value"].getvalue(), expected_value)
67
68 def test_1803_bind_in_out_set_input_sizes(self):
69 "1803 - test binding in/out with set input sizes defined"
70 bind_vars = \
71 self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS)
72 self.cursor.execute("""
73 begin
74 :value := :value + to_dsinterval('5 08:30:00');
75 end;""",
76 value=datetime.timedelta(days=5, hours=2, minutes=15))
77 expected_value = datetime.timedelta(days=10, hours=10, minutes=45)
78 self.assertEqual(bind_vars["value"].getvalue(), expected_value)
79
80 def test_1804_bind_in_out_fractional_second(self):
81 "1804 - test binding in/out with set input sizes defined"
82 bind_vars = \
83 self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS)
84 self.cursor.execute("""
85 begin
86 :value := :value + to_dsinterval('5 08:30:00');
87 end;""",
88 value=datetime.timedelta(days=5, seconds=12.123789))
89 expected_value = datetime.timedelta(days=10, hours=8, minutes=30,
90 seconds=12, microseconds=123789)
91 self.assertEqual(bind_vars["value"].getvalue(), expected_value)
92
93 def test_1805_bind_out_var(self):
94 "1805 - test binding out with cursor.var() method"
95 var = self.cursor.var(oracledb.DB_TYPE_INTERVAL_DS)
96 self.cursor.execute("""
97 begin
98 :value := to_dsinterval('15 18:35:45.586');
99 end;""",
100 value=var)
101 expected_value = datetime.timedelta(days=15, hours=18, minutes=35,
102 seconds=45, milliseconds=586)
103 self.assertEqual(var.getvalue(), expected_value)
104
105 def test_1806_bind_in_out_var_direct_set(self):
106 "1806 - test binding in/out with cursor.var() method"
107 var = self.cursor.var(oracledb.DB_TYPE_INTERVAL_DS)
108 var.setvalue(0, datetime.timedelta(days=1, minutes=50))
109 self.cursor.execute("""
110 begin
111 :value := :value + to_dsinterval('8 05:15:00');
112 end;""",
113 value=var)
114 expected_value = datetime.timedelta(days=9, hours=6, minutes=5)
115 self.assertEqual(var.getvalue(), expected_value)
116
117 def test_1807_cursor_description(self):
118 "1807 - test cursor description is accurate"
119 self.cursor.execute("select * from TestIntervals")
120 expected_value = [
121 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
122 ('INTERVALCOL', oracledb.DB_TYPE_INTERVAL_DS, None, None, 2, 6, 0),
123 ('NULLABLECOL', oracledb.DB_TYPE_INTERVAL_DS, None, None, 2, 6, 1)
124 ]
125 self.assertEqual(self.cursor.description, expected_value)
126
127 def test_1808_fetchall(self):
128 "1808 - test that fetching all of the data returns the correct results"
129 self.cursor.execute("select * From TestIntervals order by IntCol")
130 self.assertEqual(self.cursor.fetchall(), self.raw_data)
131 self.assertEqual(self.cursor.fetchall(), [])
132
133 def test_1809_fetchmany(self):
134 "1809 - test that fetching data in chunks returns the correct results"
135 self.cursor.execute("select * From TestIntervals order by IntCol")
136 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3])
137 self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5])
138 self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9])
139 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:])
140 self.assertEqual(self.cursor.fetchmany(3), [])
141
142 def test_1810_fetchone(self):
143 "1810 - test that fetching a single row returns the correct results"
144 self.cursor.execute("""
145 select *
146 from TestIntervals
147 where IntCol in (3, 4)
148 order by IntCol""")
149 self.assertEqual(self.cursor.fetchone(), self.data_by_key[3])
150 self.assertEqual(self.cursor.fetchone(), self.data_by_key[4])
151 self.assertEqual(self.cursor.fetchone(), None)
152
153 if __name__ == "__main__":
154 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 1900 - Module for testing LOB (CLOB and BLOB) variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16
17 class TestCase(test_env.BaseTestCase):
18
19 def __get_temp_lobs(self, sid):
20 cursor = self.connection.cursor()
21 cursor.execute("""
22 select abstract_lobs
23 from v$temporary_lobs
24 where sid = :sid""", sid = sid)
25 row = cursor.fetchone()
26 if row is None:
27 return 0
28 return int(row[0])
29
30 def __perform_test(self, lob_type, input_type):
31 long_string = ""
32 db_type = getattr(oracledb, "DB_TYPE_" + lob_type)
33 self.cursor.execute("truncate table Test%ss" % lob_type)
34 for i in range(0, 11):
35 if i > 0:
36 char = chr(ord('A') + i - 1)
37 long_string += char * 25000
38 elif input_type is not db_type:
39 continue
40 self.cursor.setinputsizes(long_string = input_type)
41 if lob_type == "BLOB":
42 bind_value = long_string.encode()
43 else:
44 bind_value = long_string
45 self.cursor.execute("""
46 insert into Test%ss (
47 IntCol,
48 %sCol
49 ) values (
50 :integer_value,
51 :long_string
52 )""" % (lob_type, lob_type),
53 integer_value=i,
54 long_string=bind_value)
55 self.connection.commit()
56 self.cursor.execute("""
57 select *
58 from Test%ss
59 order by IntCol""" % lob_type)
60 self.__validate_query(self.cursor, lob_type)
61
62 def __test_lob_operations(self, lob_type):
63 self.cursor.execute("truncate table Test%ss" % lob_type)
64 self.cursor.setinputsizes(long_string=getattr(oracledb, lob_type))
65 long_string = "X" * 75000
66 write_value = "TEST"
67 if lob_type == "BLOB":
68 long_string = long_string.encode("ascii")
69 write_value = write_value.encode("ascii")
70 self.cursor.execute("""
71 insert into Test%ss (
72 IntCol,
73 %sCol
74 ) values (
75 :integer_value,
76 :long_string
77 )""" % (lob_type, lob_type),
78 integer_value=1,
79 long_string=long_string)
80 self.cursor.execute("""
81 select %sCol
82 from Test%ss
83 where IntCol = 1""" % (lob_type, lob_type))
84 lob, = self.cursor.fetchone()
85 self.assertEqual(lob.isopen(), False)
86 lob.open()
87 self.assertEqual(lob.isopen(), True)
88 lob.close()
89 self.assertEqual(lob.isopen(), False)
90 self.assertEqual(lob.size(), 75000)
91 lob.write(write_value, 75001)
92 self.assertEqual(lob.size(), 75000 + len(write_value))
93 self.assertEqual(lob.read(), long_string + write_value)
94 lob.write(write_value, 1)
95 self.assertEqual(lob.read(),
96 write_value + long_string[4:] + write_value)
97 lob.trim(25000)
98 self.assertEqual(lob.size(), 25000)
99 lob.trim()
100 self.assertEqual(lob.size(), 0)
101
102 def __test_temporary_lob(self, lob_type):
103 self.cursor.execute("truncate table Test%ss" % lob_type)
104 value = "A test string value"
105 if lob_type == "BLOB":
106 value = value.encode("ascii")
107 db_type = getattr(oracledb, "DB_TYPE_" + lob_type)
108 lob = self.connection.createlob(db_type)
109 lob.write(value)
110 self.cursor.execute("""
111 insert into Test%ss (IntCol, %sCol)
112 values (:int_val, :lob_val)""" % (lob_type, lob_type),
113 int_val=1,
114 lob_val=lob)
115 self.cursor.execute("select %sCol from Test%ss" % (lob_type, lob_type))
116 lob, = self.cursor.fetchone()
117 self.assertEqual(lob.read(), value)
118
119 def __validate_query(self, rows, lob_type):
120 long_string = ""
121 db_type = getattr(oracledb, "DB_TYPE_" + lob_type)
122 for row in rows:
123 integer_value, lob = row
124 self.assertEqual(lob.type, db_type)
125 if integer_value == 0:
126 self.assertEqual(lob.size(), 0)
127 expected_value = ""
128 if lob_type == "BLOB":
129 expected_value = expected_value.encode()
130 self.assertEqual(lob.read(), expected_value)
131 else:
132 char = chr(ord('A') + integer_value - 1)
133 prev_char = chr(ord('A') + integer_value - 2)
134 long_string += char * 25000
135 if lob_type == "BLOB":
136 actualValue = long_string.encode("ascii")
137 char = char.encode("ascii")
138 prev_char = prev_char.encode("ascii")
139 else:
140 actualValue = long_string
141 self.assertEqual(lob.size(), len(actualValue))
142 self.assertEqual(lob.read(), actualValue)
143 if lob_type == "CLOB":
144 self.assertEqual(str(lob), actualValue)
145 self.assertEqual(lob.read(len(actualValue)), char)
146 if integer_value > 1:
147 offset = (integer_value - 1) * 25000 - 4
148 string = prev_char * 5 + char * 5
149 self.assertEqual(lob.read(offset, 10), string)
150
151 def test_1900_bind_lob_value(self):
152 "1900 - test binding a LOB value directly"
153 self.cursor.execute("truncate table TestCLOBs")
154 self.cursor.execute("insert into TestCLOBs values (1, 'Short value')")
155 self.cursor.execute("select ClobCol from TestCLOBs")
156 lob, = self.cursor.fetchone()
157 self.cursor.execute("insert into TestCLOBs values (2, :value)",
158 value=lob)
159
160 def test_1901_blob_cursor_description(self):
161 "1901 - test cursor description is accurate for BLOBs"
162 self.cursor.execute("select * from TestBLOBs")
163 expected_value = [
164 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
165 ('BLOBCOL', oracledb.DB_TYPE_BLOB, None, None, None, None, 0)
166 ]
167 self.assertEqual(self.cursor.description, expected_value)
168
169 def test_1902_blob_direct(self):
170 "1902 - test binding and fetching BLOB data (directly)"
171 self.__perform_test("BLOB", oracledb.DB_TYPE_BLOB)
172
173 def test_1903_blob_indirect(self):
174 "1903 - test binding and fetching BLOB data (indirectly)"
175 self.__perform_test("BLOB", oracledb.DB_TYPE_LONG_RAW)
176
177 def test_1904_blob_operations(self):
178 "1904 - test operations on BLOBs"
179 self.__test_lob_operations("BLOB")
180
181 def test_1905_clob_cursor_description(self):
182 "1905 - test cursor description is accurate for CLOBs"
183 self.cursor.execute("select * from TestCLOBs")
184 expected_value = [
185 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
186 ('CLOBCOL', oracledb.DB_TYPE_CLOB, None, None, None, None, 0)
187 ]
188 self.assertEqual(self.cursor.description, expected_value)
189
190 def test_1906_clob_direct(self):
191 "1906 - test binding and fetching CLOB data (directly)"
192 self.__perform_test("CLOB", oracledb.DB_TYPE_CLOB)
193
194 def test_1907_clob_indirect(self):
195 "1907 - test binding and fetching CLOB data (indirectly)"
196 self.__perform_test("CLOB", oracledb.DB_TYPE_LONG)
197
198 def test_1908_clob_operations(self):
199 "1908 - test operations on CLOBs"
200 self.__test_lob_operations("CLOB")
201
202 def test_1909_create_temp_blob(self):
203 "1909 - test creating a temporary BLOB"
204 self.__test_temporary_lob("BLOB")
205
206 def test_1910_create_temp_clob(self):
207 "1910 - test creating a temporary CLOB"
208 self.__test_temporary_lob("CLOB")
209
210 def test_1911_create_temp_nclob(self):
211 "1911 - test creating a temporary NCLOB"
212 self.__test_temporary_lob("NCLOB")
213
214 def test_1912_multiple_fetch(self):
215 "1912 - test retrieving data from a CLOB after multiple fetches"
216 self.cursor.arraysize = 1
217 self.cursor.execute("select * from TestCLOBS")
218 rows = self.cursor.fetchall()
219 self.__validate_query(rows, "CLOB")
220
221 def test_1913_nclob_cursor_description(self):
222 "1913 - test cursor description is accurate for NCLOBs"
223 self.cursor.execute("select * from TestNCLOBs")
224 expected_value = [
225 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
226 ('NCLOBCOL', oracledb.DB_TYPE_NCLOB, None, None, None, None, 0)
227 ]
228 self.assertEqual(self.cursor.description, expected_value)
229
230 def test_1914_nclob_direct(self):
231 "1914 - test binding and fetching NCLOB data (directly)"
232 self.__perform_test("NCLOB", oracledb.DB_TYPE_NCLOB)
233
234 def test_1915_nclob_different_encodings(self):
235 "1915 - test binding and fetching NCLOB data (different encodings)"
236 connection = oracledb.connect(test_env.get_main_user(),
237 test_env.get_main_password(),
238 test_env.get_connect_string(),
239 encoding="UTF-8", nencoding="UTF-16")
240 value = "\u03b4\u4e2a"
241 cursor = connection.cursor()
242 cursor.execute("truncate table TestNCLOBs")
243 cursor.setinputsizes(val=oracledb.DB_TYPE_NVARCHAR)
244 cursor.execute("insert into TestNCLOBs values (1, :val)", val=value)
245 cursor.execute("select NCLOBCol from TestNCLOBs")
246 nclob, = cursor.fetchone()
247 cursor.setinputsizes(val=oracledb.DB_TYPE_NVARCHAR)
248 cursor.execute("update TestNCLOBs set NCLOBCol = :val",
249 val=nclob.read() + value)
250 cursor.execute("select NCLOBCol from TestNCLOBs")
251 nclob, = cursor.fetchone()
252 self.assertEqual(nclob.read(), value + value)
253
254 def test_1916_nclob_indirect(self):
255 "1916 - test binding and fetching NCLOB data (indirectly)"
256 self.__perform_test("NCLOB", oracledb.DB_TYPE_LONG)
257
258 def test_1917_nclob_operations(self):
259 "1917 - test operations on NCLOBs"
260 self.__test_lob_operations("NCLOB")
261
262 def test_1918_temporary_lobs(self):
263 "1918 - test temporary LOBs"
264 cursor = self.connection.cursor()
265 cursor.arraysize = self.cursor.arraysize
266 cursor.execute("""
267 select sys_context('USERENV', 'SID')
268 from dual""")
269 sid, = cursor.fetchone()
270 temp_lobs = self.__get_temp_lobs(sid)
271 self.assertEqual(temp_lobs, 0)
272 cursor.execute("""
273 select extract(xmlcol, '/').getclobval()
274 from TestXML""")
275 for lob, in cursor:
276 value = lob.read()
277 del lob
278 cursor.close()
279 temp_lobs = self.__get_temp_lobs(sid)
280 self.assertEqual(temp_lobs, 0)
281
282 def test_1919_AssignStringBeyondArraySize(self):
283 "1919 - test assign string to NCLOB beyond array size"
284 nclobVar = self.cursor.var(oracledb.DB_TYPE_NCLOB)
285 self.assertRaises(IndexError, nclobVar.setvalue, 1, "test char")
286
287 if __name__ == "__main__":
288 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 2000 - Module for testing long and long raw variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16
17 class TestCase(test_env.BaseTestCase):
18
19 def __perform_test(self, typ):
20 name_part = "Long" if typ is oracledb.DB_TYPE_LONG else "LongRaw"
21
22 self.cursor.execute("truncate table Test%ss" % name_part)
23 long_string = ""
24 for i in range(1, 11):
25 char = chr(ord('A') + i - 1)
26 long_string += char * 25000
27 self.cursor.setinputsizes(long_string=typ)
28 if typ is oracledb.DB_TYPE_LONG_RAW:
29 bind_value = long_string.encode()
30 else:
31 bind_value = long_string
32 self.cursor.execute("""
33 insert into Test%ss (
34 IntCol,
35 %sCol
36 ) values (
37 :integer_value,
38 :long_string
39 )""" % (name_part, name_part),
40 integer_value=i,
41 long_string=bind_value)
42 self.connection.commit()
43 self.cursor.execute("""
44 select *
45 from Test%ss
46 order by IntCol""" % name_part)
47 long_string = ""
48 for integer_value, fetched_value in self.cursor:
49 char = chr(ord('A') + integer_value - 1)
50 long_string += char * 25000
51 if typ is oracledb.DB_TYPE_LONG_RAW:
52 actual_value = long_string.encode()
53 else:
54 actual_value = long_string
55 self.assertEqual(len(fetched_value), integer_value * 25000)
56 self.assertEqual(fetched_value, actual_value)
57
58 def test_2000_longs(self):
59 "2000 - test binding and fetching long data"
60 self.__perform_test(oracledb.DB_TYPE_LONG)
61
62 def test_2001_long_with_execute_many(self):
63 "2001 - test binding long data with executemany()"
64 data = []
65 self.cursor.execute("truncate table TestLongs")
66 for i in range(5):
67 char = chr(ord('A') + i)
68 long_str = char * (32768 * (i + 1))
69 data.append((i + 1, long_str))
70 self.cursor.executemany("insert into TestLongs values (:1, :2)", data)
71 self.connection.commit()
72 self.cursor.execute("select * from TestLongs order by IntCol")
73 fetched_data = self.cursor.fetchall()
74 self.assertEqual(fetched_data, data)
75
76 def test_2002_long_raws(self):
77 "2002 - test binding and fetching long raw data"
78 self.__perform_test(oracledb.DB_TYPE_LONG_RAW)
79
80 def test_2003_long_cursor_description(self):
81 "2003 - test cursor description is accurate for longs"
82 self.cursor.execute("select * from TestLongs")
83 expected_value = [
84 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
85 ('LONGCOL', oracledb.DB_TYPE_LONG, None, None, None, None, 0)
86 ]
87 self.assertEqual(self.cursor.description, expected_value)
88
89 def test_2004_long_raw_cursor_description(self):
90 "2004 - test cursor description is accurate for long raws"
91 self.cursor.execute("select * from TestLongRaws")
92 self.assertEqual(self.cursor.description,
93 [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
94 ('LONGRAWCOL', oracledb.DB_TYPE_LONG_RAW, None, None, None,
95 None, 0) ])
96
97 def test_2005_array_size_too_large(self):
98 "2005 - test array size too large generates an exception"
99 self.cursor.arraysize = 268435456
100 self.assertRaises(oracledb.DatabaseError, self.cursor.execute,
101 "select * from TestLongRaws")
102
103 if __name__ == "__main__":
104 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 2100 - Module for testing NCHAR variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16
17 class TestCase(test_env.BaseTestCase):
18
19 def setUp(self):
20 super().setUp()
21 self.raw_data = []
22 self.data_by_key = {}
23 for i in range(1, 11):
24 unicode_col = "Unicode \u3042 %d" % i
25 fixed_char_col = ("Fixed Unicode %d" % i).ljust(40)
26 if i % 2:
27 nullable_col = "Nullable %d" % i
28 else:
29 nullable_col = None
30 data_tuple = (i, unicode_col, fixed_char_col, nullable_col)
31 self.raw_data.append(data_tuple)
32 self.data_by_key[i] = data_tuple
33
34 def test_2100_unicode_length(self):
35 "2100 - test value length"
36 return_value = self.cursor.var(int)
37 self.cursor.execute("""
38 begin
39 :retval := LENGTH(:value);
40 end;""",
41 value="InVal \u3042",
42 retval=return_value)
43 self.assertEqual(return_value.getvalue(), 7)
44
45 def test_2101_bind_unicode(self):
46 "2101 - test binding in a unicode"
47 self.cursor.setinputsizes(value=oracledb.DB_TYPE_NVARCHAR)
48 self.cursor.execute("""
49 select * from TestUnicodes
50 where UnicodeCol = :value""",
51 value="Unicode \u3042 5")
52 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]])
53
54 def test_2102_bind_different_var(self):
55 "2102 - test binding a different variable on second execution"
56 retval_1 = self.cursor.var(oracledb.DB_TYPE_NVARCHAR, 30)
57 retval_2 = self.cursor.var(oracledb.DB_TYPE_NVARCHAR, 30)
58 self.cursor.execute(r"begin :retval := unistr('Called \3042'); end;",
59 retval=retval_1)
60 self.assertEqual(retval_1.getvalue(), "Called \u3042")
61 self.cursor.execute("begin :retval := 'Called'; end;", retval=retval_2)
62 self.assertEqual(retval_2.getvalue(), "Called")
63
64 def test_2103_bind_unicode_after_number(self):
65 "2103 - test binding in a string after setting input sizes to a number"
66 unicode_val = self.cursor.var(oracledb.DB_TYPE_NVARCHAR)
67 unicode_val.setvalue(0, "Unicode \u3042 6")
68 self.cursor.setinputsizes(value=oracledb.NUMBER)
69 self.cursor.execute("""
70 select * from TestUnicodes
71 where UnicodeCol = :value""",
72 value=unicode_val)
73 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[6]])
74
75 def test_2104_bind_unicode_array_direct(self):
76 "2104 - test binding in a unicode array"
77 return_value = self.cursor.var(oracledb.NUMBER)
78 array = [r[1] for r in self.raw_data]
79 array_var = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, array)
80 statement = """
81 begin
82 :retval := pkg_TestUnicodeArrays.TestInArrays(
83 :integer_value, :array);
84 end;"""
85 self.cursor.execute(statement, retval=return_value, integer_value=5,
86 array=array_var)
87 self.assertEqual(return_value.getvalue(), 116)
88 array = ["Unicode - \u3042 %d" % i for i in range(15)]
89 array_var = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, array)
90 self.cursor.execute(statement, integer_value=8, array=array_var)
91 self.assertEqual(return_value.getvalue(), 208)
92
93 def test_2105_bind_unicode_array_by_sizes(self):
94 "2105 - test binding in a unicode array (with setinputsizes)"
95 return_value = self.cursor.var(oracledb.NUMBER)
96 self.cursor.setinputsizes(array = [oracledb.DB_TYPE_NVARCHAR, 10])
97 array = [r[1] for r in self.raw_data]
98 self.cursor.execute("""
99 begin
100 :retval := pkg_TestUnicodeArrays.TestInArrays(:integer_value,
101 :array);
102 end;""",
103 retval=return_value,
104 integer_value=6,
105 array=array)
106 self.assertEqual(return_value.getvalue(), 117)
107
108 def test_2106_bind_unicode_array_by_var(self):
109 "2106 - test binding in a unicode array (with arrayvar)"
110 return_value = self.cursor.var(oracledb.NUMBER)
111 array = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, 10, 20)
112 array.setvalue(0, [r[1] for r in self.raw_data])
113 self.cursor.execute("""
114 begin
115 :retval := pkg_TestUnicodeArrays.TestInArrays(:integer_value,
116 :array);
117 end;""",
118 retval=return_value,
119 integer_value=7,
120 array=array)
121 self.assertEqual(return_value.getvalue(), 118)
122
123 def test_2107_bind_in_out_unicode_array_by_var(self):
124 "2107 - test binding in/out a unicode array (with arrayvar)"
125 array = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, 10, 100)
126 original_data = [r[1] for r in self.raw_data]
127 fmt = "Converted element \u3042 # %d originally had length %d"
128 expected_data = [fmt % (i, len(original_data[i - 1])) \
129 for i in range(1, 6)] + original_data[5:]
130 array.setvalue(0, original_data)
131 self.cursor.execute("""
132 begin
133 pkg_TestUnicodeArrays.TestInOutArrays(:numElems, :array);
134 end;""",
135 numElems = 5,
136 array = array)
137 self.assertEqual(array.getvalue(), expected_data)
138
139 def test_2108_bind_out_unicode_array_by_var(self):
140 "2108 - test binding out a unicode array (with arrayvar)"
141 array = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, 6, 100)
142 fmt = "Test out element \u3042 # %d"
143 expected_data = [fmt % i for i in range(1, 7)]
144 self.cursor.execute("""
145 begin
146 pkg_TestUnicodeArrays.TestOutArrays(:numElems, :array);
147 end;""",
148 numElems = 6,
149 array = array)
150 self.assertEqual(array.getvalue(), expected_data)
151
152 def test_2109_bind_null(self):
153 "2109 - test binding in a null"
154 self.cursor.execute("""
155 select * from TestUnicodes
156 where UnicodeCol = :value""",
157 value = None)
158 self.assertEqual(self.cursor.fetchall(), [])
159
160 def test_2110_bind_out_set_input_sizes_by_type(self):
161 "2110 - test binding out with set input sizes defined (by type)"
162 bind_vars = self.cursor.setinputsizes(value=oracledb.DB_TYPE_NVARCHAR)
163 self.cursor.execute(r"""
164 begin
165 :value := unistr('TSI \3042');
166 end;""")
167 self.assertEqual(bind_vars["value"].getvalue(), "TSI \u3042")
168
169 def test_2111_bind_in_out_set_input_sizes_by_type(self):
170 "2111 - test binding in/out with set input sizes defined (by type)"
171 bind_vars = self.cursor.setinputsizes(value=oracledb.DB_TYPE_NVARCHAR)
172 self.cursor.execute(r"""
173 begin
174 :value := :value || unistr(' TSI \3042');
175 end;""",
176 value = "InVal \u3041")
177 self.assertEqual(bind_vars["value"].getvalue(),
178 "InVal \u3041 TSI \u3042")
179
180 def test_2112_bind_out_var(self):
181 "2112 - test binding out with cursor.var() method"
182 var = self.cursor.var(oracledb.DB_TYPE_NVARCHAR)
183 self.cursor.execute(r"""
184 begin
185 :value := unistr('TSI (VAR) \3042');
186 end;""",
187 value=var)
188 self.assertEqual(var.getvalue(), "TSI (VAR) \u3042")
189
190 def test_2113_bind_in_out_var_direct_set(self):
191 "2113 - test binding in/out with cursor.var() method"
192 var = self.cursor.var(oracledb.DB_TYPE_NVARCHAR)
193 var.setvalue(0, "InVal \u3041")
194 self.cursor.execute(r"""
195 begin
196 :value := :value || unistr(' TSI (VAR) \3042');
197 end;""",
198 value = var)
199 self.assertEqual(var.getvalue(), "InVal \u3041 TSI (VAR) \u3042")
200
201 def test_2114_cursor_description(self):
202 "2114 - test cursor description is accurate"
203 self.cursor.execute("select * from TestUnicodes")
204 expected_value = [
205 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
206 ('UNICODECOL', oracledb.DB_TYPE_NVARCHAR, 20, 80, None, None, 0),
207 ('FIXEDUNICODECOL', oracledb.DB_TYPE_NCHAR, 40, 160, None, None, 0),
208 ('NULLABLECOL', oracledb.DB_TYPE_NVARCHAR, 50, 200, None, None, 1)
209 ]
210 self.assertEqual(self.cursor.description, expected_value)
211
212 def test_2115_fetchall(self):
213 "2115 - test that fetching all of the data returns the correct results"
214 self.cursor.execute("select * From TestUnicodes order by IntCol")
215 self.assertEqual(self.cursor.fetchall(), self.raw_data)
216 self.assertEqual(self.cursor.fetchall(), [])
217
218 def test_2116_fetchmany(self):
219 "2116 - test that fetching data in chunks returns the correct results"
220 self.cursor.execute("select * From TestUnicodes order by IntCol")
221 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3])
222 self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5])
223 self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9])
224 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:])
225 self.assertEqual(self.cursor.fetchmany(3), [])
226
227 def test_2117_fetchone(self):
228 "2117 - test that fetching a single row returns the correct results"
229 self.cursor.execute("""
230 select *
231 from TestUnicodes
232 where IntCol in (3, 4)
233 order by IntCol""")
234 self.assertEqual(self.cursor.fetchone(), self.data_by_key[3])
235 self.assertEqual(self.cursor.fetchone(), self.data_by_key[4])
236 self.assertEqual(self.cursor.fetchone(), None)
237
238 if __name__ == "__main__":
239 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 2200 - Module for testing number variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import decimal
17 import sys
18
19 class TestCase(test_env.BaseTestCase):
20
21 def output_type_handler_binary_int(self, cursor, name, default_type, size,
22 precision, scale):
23 return cursor.var(oracledb.DB_TYPE_BINARY_INTEGER,
24 arraysize=cursor.arraysize)
25
26 def output_type_handler_decimal(self, cursor, name, default_type, size,
27 precision, scale):
28 if default_type == oracledb.NUMBER:
29 return cursor.var(str, 255, outconverter=decimal.Decimal,
30 arraysize=cursor.arraysize)
31
32 def setUp(self):
33 super().setUp()
34 self.raw_data = []
35 self.data_by_key = {}
36 for i in range(1, 11):
37 number_col = i + i * 0.25
38 float_col = i + i * 0.75
39 unconstrained_col = i ** 3 + i * 0.5
40 if i % 2:
41 nullable_col = 143 ** i
42 else:
43 nullable_col = None
44 data_tuple = (i, 38 ** i, number_col, float_col,
45 unconstrained_col, nullable_col)
46 self.raw_data.append(data_tuple)
47 self.data_by_key[i] = data_tuple
48
49 def test_2200_bind_boolean(self):
50 "2200 - test binding in a boolean"
51 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
52 (True,))
53 self.assertEqual(result, "TRUE")
54
55 def test_2201_bind_boolean_as_number(self):
56 "2201 - test binding in a boolean as a number"
57 var = self.cursor.var(oracledb.NUMBER)
58 var.setvalue(0, True)
59 self.cursor.execute("select :1 from dual", [var])
60 result, = self.cursor.fetchone()
61 self.assertEqual(result, 1)
62 var.setvalue(0, False)
63 self.cursor.execute("select :1 from dual", [var])
64 result, = self.cursor.fetchone()
65 self.assertEqual(result, 0)
66
67 def test_2202_bind_decimal(self):
68 "2202 - test binding in a decimal.Decimal"
69 self.cursor.execute("""
70 select * from TestNumbers
71 where NumberCol - :value1 - :value2 = trunc(NumberCol)""",
72 value1=decimal.Decimal("0.20"),
73 value2=decimal.Decimal("0.05"))
74 expected_data = [self.data_by_key[1], self.data_by_key[5],
75 self.data_by_key[9]]
76 self.assertEqual(self.cursor.fetchall(), expected_data)
77
78 def test_2203_bind_float(self):
79 "2203 - test binding in a float"
80 self.cursor.execute("""
81 select * from TestNumbers
82 where NumberCol - :value = trunc(NumberCol)""",
83 value=0.25)
84 expected_data = [self.data_by_key[1], self.data_by_key[5],
85 self.data_by_key[9]]
86 self.assertEqual(self.cursor.fetchall(), expected_data)
87
88 def test_2204_bind_integer(self):
89 "2204 - test binding in an integer"
90 self.cursor.execute("""
91 select * from TestNumbers
92 where IntCol = :value""",
93 value = 2)
94 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[2]])
95
96 def test_2205_bind_large_long_as_oracle_number(self):
97 "2205 - test binding in a large long integer as Oracle number"
98 in_val = 6088343244
99 value_var = self.cursor.var(oracledb.NUMBER)
100 value_var.setvalue(0, in_val)
101 self.cursor.execute("""
102 begin
103 :value := :value + 5;
104 end;""",
105 value=value_var)
106 value = value_var.getvalue()
107 self.assertEqual(value, in_val + 5)
108
109 def test_2206_bind_large_long_as_integer(self):
110 "2206 - test binding in a large long integer as Python integer"
111 long_value = -9999999999999999999
112 self.cursor.execute("select :value from dual", value=long_value)
113 result, = self.cursor.fetchone()
114 self.assertEqual(result, long_value)
115
116 def test_2207_bind_integer_after_string(self):
117 "2207 - test binding in an integer after setting input sizes to string"
118 self.cursor.setinputsizes(value=15)
119 self.cursor.execute("""
120 select * from TestNumbers
121 where IntCol = :value""",
122 value=3)
123 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[3]])
124
125 def test_2208_bind_decimal_after_number(self):
126 "2208 - test binding in a decimal after setting input sizes to number"
127 cursor = self.connection.cursor()
128 value = decimal.Decimal("319438950232418390.273596")
129 cursor.setinputsizes(value=oracledb.NUMBER)
130 cursor.outputtypehandler = self.output_type_handler_decimal
131 cursor.execute("select :value from dual", value=value)
132 out_value, = cursor.fetchone()
133 self.assertEqual(out_value, value)
134
135 def test_2209_bind_null(self):
136 "2209 - test binding in a null"
137 self.cursor.execute("""
138 select * from TestNumbers
139 where IntCol = :value""",
140 value=None)
141 self.assertEqual(self.cursor.fetchall(), [])
142
143 def test_2210_bind_number_array_direct(self):
144 "2210 - test binding in a number array"
145 return_value = self.cursor.var(oracledb.NUMBER)
146 array = [r[2] for r in self.raw_data]
147 statement = """
148 begin
149 :return_value := pkg_TestNumberArrays.TestInArrays(
150 :start_value, :array);
151 end;"""
152 self.cursor.execute(statement, return_value=return_value,
153 start_value=5, array=array)
154 self.assertEqual(return_value.getvalue(), 73.75)
155 array = list(range(15))
156 self.cursor.execute(statement, start_value=10, array=array)
157 self.assertEqual(return_value.getvalue(), 115.0)
158
159 def test_2211_bind_number_array_by_sizes(self):
160 "2211 - test binding in a number array (with setinputsizes)"
161 return_value = self.cursor.var(oracledb.NUMBER)
162 self.cursor.setinputsizes(array = [oracledb.NUMBER, 10])
163 array = [r[2] for r in self.raw_data]
164 self.cursor.execute("""
165 begin
166 :return_value := pkg_TestNumberArrays.TestInArrays(
167 :start_value, :array);
168 end;""",
169 return_value=return_value,
170 start_value=6,
171 array=array)
172 self.assertEqual(return_value.getvalue(), 74.75)
173
174 def test_2212_bind_number_array_by_var(self):
175 "2212 - test binding in a number array (with arrayvar)"
176 return_value = self.cursor.var(oracledb.NUMBER)
177 array = self.cursor.arrayvar(oracledb.NUMBER,
178 [r[2] for r in self.raw_data])
179 self.cursor.execute("""
180 begin
181 :return_value := pkg_TestNumberArrays.TestInArrays(
182 :integer_value, :array);
183 end;""",
184 return_value = return_value,
185 integer_value = 7,
186 array = array)
187 self.assertEqual(return_value.getvalue(), 75.75)
188
189 def test_2213_bind_zero_length_number_array_by_var(self):
190 "2213 - test binding in a zero length number array (with arrayvar)"
191 return_value = self.cursor.var(oracledb.NUMBER)
192 array = self.cursor.arrayvar(oracledb.NUMBER, 0)
193 self.cursor.execute("""
194 begin
195 :return_value := pkg_TestNumberArrays.TestInArrays(
196 :integer_value, :array);
197 end;""",
198 return_value=return_value,
199 integer_value=8,
200 array=array)
201 self.assertEqual(return_value.getvalue(), 8.0)
202 self.assertEqual(array.getvalue(), [])
203
204 def test_2214_bind_in_out_number_array_by_var(self):
205 "2214 - test binding in/out a number array (with arrayvar)"
206 array = self.cursor.arrayvar(oracledb.NUMBER, 10)
207 original_data = [r[2] for r in self.raw_data]
208 expected_data = [original_data[i - 1] * 10 for i in range(1, 6)] + \
209 original_data[5:]
210 array.setvalue(0, original_data)
211 self.cursor.execute("""
212 begin
213 pkg_TestNumberArrays.TestInOutArrays(:num_elems, :array);
214 end;""",
215 num_elems=5,
216 array=array)
217 self.assertEqual(array.getvalue(), expected_data)
218
219 def test_2215_bind_out_number_array_by_var(self):
220 "2215 - test binding out a Number array (with arrayvar)"
221 array = self.cursor.arrayvar(oracledb.NUMBER, 6)
222 expected_data = [i * 100 for i in range(1, 7)]
223 self.cursor.execute("""
224 begin
225 pkg_TestNumberArrays.TestOutArrays(:num_elems, :array);
226 end;""",
227 num_elems=6,
228 array=array)
229 self.assertEqual(array.getvalue(), expected_data)
230
231 def test_2216_bind_out_set_input_sizes(self):
232 "2216 - test binding out with set input sizes defined"
233 bind_vars = self.cursor.setinputsizes(value = oracledb.NUMBER)
234 self.cursor.execute("""
235 begin
236 :value := 5;
237 end;""")
238 self.assertEqual(bind_vars["value"].getvalue(), 5)
239
240 def test_2217_bind_in_out_set_input_sizes(self):
241 "2217 - test binding in/out with set input sizes defined"
242 bind_vars = self.cursor.setinputsizes(value = oracledb.NUMBER)
243 self.cursor.execute("""
244 begin
245 :value := :value + 5;
246 end;""",
247 value = 1.25)
248 self.assertEqual(bind_vars["value"].getvalue(), 6.25)
249
250 def test_2218_bind_out_var(self):
251 "2218 - test binding out with cursor.var() method"
252 var = self.cursor.var(oracledb.NUMBER)
253 self.cursor.execute("""
254 begin
255 :value := 5;
256 end;""",
257 value = var)
258 self.assertEqual(var.getvalue(), 5)
259
260 def test_2219_bind_in_out_var_direct_set(self):
261 "2219 - test binding in/out with cursor.var() method"
262 var = self.cursor.var(oracledb.NUMBER)
263 var.setvalue(0, 2.25)
264 self.cursor.execute("""
265 begin
266 :value := :value + 5;
267 end;""",
268 value = var)
269 self.assertEqual(var.getvalue(), 7.25)
270
271 def test_2220_cursor_description(self):
272 "2220 - test cursor description is accurate"
273 self.cursor.execute("select * from TestNumbers")
274 expected_value = [
275 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
276 ('LONGINTCOL', oracledb.DB_TYPE_NUMBER, 17, None, 16, 0, 0),
277 ('NUMBERCOL', oracledb.DB_TYPE_NUMBER, 13, None, 9, 2, 0),
278 ('FLOATCOL', oracledb.DB_TYPE_NUMBER, 127, None, 126, -127, 0),
279 ('UNCONSTRAINEDCOL', oracledb.DB_TYPE_NUMBER, 127, None, 0, -127,
280 0),
281 ('NULLABLECOL', oracledb.DB_TYPE_NUMBER, 39, None, 38, 0, 1)
282 ]
283 self.assertEqual(self.cursor.description, expected_value)
284
285 def test_2221_fetchall(self):
286 "2221 - test that fetching all of the data returns the correct results"
287 self.cursor.execute("select * From TestNumbers order by IntCol")
288 self.assertEqual(self.cursor.fetchall(), self.raw_data)
289 self.assertEqual(self.cursor.fetchall(), [])
290
291 def test_2222_fetchmany(self):
292 "2222 - test that fetching data in chunks returns the correct results"
293 self.cursor.execute("select * From TestNumbers order by IntCol")
294 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3])
295 self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5])
296 self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9])
297 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:])
298 self.assertEqual(self.cursor.fetchmany(3), [])
299
300 def test_2223_fetchone(self):
301 "2223 - test that fetching a single row returns the correct results"
302 self.cursor.execute("""
303 select *
304 from TestNumbers
305 where IntCol in (3, 4)
306 order by IntCol""")
307 self.assertEqual(self.cursor.fetchone(), self.data_by_key[3])
308 self.assertEqual(self.cursor.fetchone(), self.data_by_key[4])
309 self.assertEqual(self.cursor.fetchone(), None)
310
311 def test_2224_return_as_long(self):
312 "2224 - test that fetching a long integer returns such in Python"
313 self.cursor.execute("""
314 select NullableCol
315 from TestNumbers
316 where IntCol = 9""")
317 col, = self.cursor.fetchone()
318 self.assertEqual(col, 25004854810776297743)
319
320 def test_2225_return_constant_float(self):
321 "2225 - test fetching a floating point number returns such in Python"
322 self.cursor.execute("select 1.25 from dual")
323 result, = self.cursor.fetchone()
324 self.assertEqual(result, 1.25)
325
326 def test_2226_return_constant_integer(self):
327 "2226 - test that fetching an integer returns such in Python"
328 self.cursor.execute("select 148 from dual")
329 result, = self.cursor.fetchone()
330 self.assertEqual(result, 148)
331 self.assertTrue(isinstance(result, int), "integer not returned")
332
333 def test_2227_acceptable_boundary_numbers(self):
334 "2227 - test that acceptable boundary numbers are handled properly"
335 in_values = [decimal.Decimal("9.99999999999999e+125"),
336 decimal.Decimal("-9.99999999999999e+125"), 0.0, 1e-130,
337 -1e-130]
338 out_values = [int("9" * 15 + "0" * 111), -int("9" * 15 + "0" * 111),
339 0, 1e-130, -1e-130]
340 for in_value, out_value in zip(in_values, out_values):
341 self.cursor.execute("select :1 from dual", (in_value,))
342 result, = self.cursor.fetchone()
343 self.assertEqual(result, out_value)
344
345 def test_2228_unacceptable_boundary_numbers(self):
346 "2228 - test that unacceptable boundary numbers are rejected"
347 in_values = [1e126, -1e126, float("inf"), float("-inf"),
348 float("NaN"), decimal.Decimal("1e126"),
349 decimal.Decimal("-1e126"), decimal.Decimal("inf"),
350 decimal.Decimal("-inf"), decimal.Decimal("NaN")]
351 no_rep_err = "value cannot be represented as an Oracle number"
352 invalid_err = "invalid number"
353 expected_errors = [no_rep_err, no_rep_err, invalid_err, invalid_err,
354 invalid_err, no_rep_err, no_rep_err, invalid_err,
355 invalid_err, invalid_err]
356 for in_value, error in zip(in_values, expected_errors):
357 self.assertRaisesRegex(oracledb.DatabaseError, error,
358 self.cursor.execute, "select :1 from dual",
359 (in_value,))
360
361 def test_2229_return_float_from_division(self):
362 "2229 - test that fetching the result of division returns a float"
363 self.cursor.execute("""
364 select IntCol / 7
365 from TestNumbers
366 where IntCol = 1""")
367 result, = self.cursor.fetchone()
368 self.assertEqual(result, 1.0 / 7.0)
369 self.assertTrue(isinstance(result, float), "float not returned")
370
371 def test_2230_string_format(self):
372 "2230 - test that string format is returned properly"
373 var = self.cursor.var(oracledb.NUMBER)
374 self.assertEqual(str(var),
375 "<cx_Oracle.Var of type DB_TYPE_NUMBER with value None>")
376 var.setvalue(0, 4)
377 self.assertEqual(str(var),
378 "<cx_Oracle.Var of type DB_TYPE_NUMBER with value 4.0>")
379
380 def test_2231_bind_binary_double(self):
381 "2231 - test that binding binary double is possible"
382 statement = "select :1 from dual"
383 self.cursor.setinputsizes(oracledb.DB_TYPE_BINARY_DOUBLE)
384 self.cursor.execute(statement, (5,))
385 self.assertEqual(self.cursor.bindvars[0].type,
386 oracledb.DB_TYPE_BINARY_DOUBLE)
387 value, = self.cursor.fetchone()
388 self.assertEqual(value, 5)
389 self.cursor.execute(statement, (1.5,))
390 self.assertEqual(self.cursor.bindvars[0].type,
391 oracledb.DB_TYPE_BINARY_DOUBLE)
392 value, = self.cursor.fetchone()
393 self.assertEqual(value, 1.5)
394 self.cursor.execute(statement, (decimal.Decimal("NaN"),))
395 self.assertEqual(self.cursor.bindvars[0].type,
396 oracledb.DB_TYPE_BINARY_DOUBLE)
397 value, = self.cursor.fetchone()
398 self.assertEqual(str(value), str(float("NaN")))
399
400 def test_2232_fetch_binary_int(self):
401 "2232 - test fetching numbers as binary integers"
402 self.cursor.outputtypehandler = self.output_type_handler_binary_int
403 for value in (1, 2 ** 31, 2 ** 63 - 1, -1, -2 ** 31, -2 ** 63 + 1):
404 self.cursor.execute("select :1 from dual", [str(value)])
405 fetched_value, = self.cursor.fetchone()
406 self.assertEqual(value, fetched_value)
407
408 if __name__ == "__main__":
409 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 2300 - Module for testing object variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import datetime
17 import decimal
18
19 class TestCase(test_env.BaseTestCase):
20
21 def __get_object_as_tuple(self, obj):
22 if obj.type.iscollection:
23 value = []
24 for v in obj.aslist():
25 if isinstance(v, oracledb.Object):
26 v = self.__get_object_as_tuple(v)
27 elif isinstance(value, oracledb.LOB):
28 v = v.read()
29 value.append(v)
30 return value
31 attribute_values = []
32 for attribute in obj.type.attributes:
33 value = getattr(obj, attribute.name)
34 if isinstance(value, oracledb.Object):
35 value = self.__get_object_as_tuple(value)
36 elif isinstance(value, oracledb.LOB):
37 value = value.read()
38 attribute_values.append(value)
39 return tuple(attribute_values)
40
41 def __test_data(self, expected_int_value, expected_obj_value,
42 expected_array_value):
43 int_value, object_value, array_value = self.cursor.fetchone()
44 if object_value is not None:
45 object_value = self.__get_object_as_tuple(object_value)
46 if array_value is not None:
47 array_value = array_value.aslist()
48 self.assertEqual(int_value, expected_int_value)
49 self.assertEqual(object_value, expected_obj_value)
50 self.assertEqual(array_value, expected_array_value)
51
52 def test_2300_bind_null_in(self):
53 "2300 - test binding a null value (IN)"
54 var = self.cursor.var(oracledb.DB_TYPE_OBJECT, typename="UDT_OBJECT")
55 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
56 (var,))
57 self.assertEqual(result, "null")
58
59 def test_2301_bind_object_in(self):
60 "2301 - test binding an object (IN)"
61 type_obj = self.connection.gettype("UDT_OBJECT")
62 obj = type_obj.newobject()
63 obj.NUMBERVALUE = 13
64 obj.STRINGVALUE = "Test String"
65 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
66 (obj,))
67 exp = "udt_Object(13, 'Test String', null, null, null, null, null)"
68 self.assertEqual(result, exp)
69 obj.NUMBERVALUE = None
70 obj.STRINGVALUE = "Test With Dates"
71 obj.DATEVALUE = datetime.datetime(2016, 2, 10)
72 obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 10, 14, 13, 50)
73 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
74 (obj,))
75 self.assertEqual(result,
76 "udt_Object(null, 'Test With Dates', null, " \
77 "to_date('2016-02-10', 'YYYY-MM-DD'), " \
78 "to_timestamp('2016-02-10 14:13:50', " \
79 "'YYYY-MM-DD HH24:MI:SS'), " \
80 "null, null)")
81 obj.DATEVALUE = None
82 obj.TIMESTAMPVALUE = None
83 sub_type_obj = self.connection.gettype("UDT_SUBOBJECT")
84 sub_obj = sub_type_obj.newobject()
85 sub_obj.SUBNUMBERVALUE = decimal.Decimal("18.25")
86 sub_obj.SUBSTRINGVALUE = "Sub String"
87 obj.SUBOBJECTVALUE = sub_obj
88 result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
89 (obj,))
90 self.assertEqual(result,
91 "udt_Object(null, 'Test With Dates', null, null, " \
92 "null, udt_SubObject(18.25, 'Sub String'), null)")
93
94 def test_2302_copy_object(self):
95 "2302 - test copying an object"
96 type_obj = self.connection.gettype("UDT_OBJECT")
97 obj = type_obj()
98 obj.NUMBERVALUE = 5124
99 obj.STRINGVALUE = "A test string"
100 obj.DATEVALUE = datetime.datetime(2016, 2, 24)
101 obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 24, 13, 39, 10)
102 copied_obj = obj.copy()
103 self.assertEqual(obj.NUMBERVALUE, copied_obj.NUMBERVALUE)
104 self.assertEqual(obj.STRINGVALUE, copied_obj.STRINGVALUE)
105 self.assertEqual(obj.DATEVALUE, copied_obj.DATEVALUE)
106 self.assertEqual(obj.TIMESTAMPVALUE, copied_obj.TIMESTAMPVALUE)
107
108 def test_2303_empty_collection_as_list(self):
109 "2303 - test getting an empty collection as a list"
110 type_obj = self.connection.gettype("UDT_ARRAY")
111 obj = type_obj.newobject()
112 self.assertEqual(obj.aslist(), [])
113
114 def test_2304_fetch_data(self):
115 "2304 - test fetching objects"
116 self.cursor.execute("alter session set time_zone = 'UTC'")
117 self.cursor.execute("""
118 select
119 IntCol,
120 ObjectCol,
121 ArrayCol
122 from TestObjects
123 order by IntCol""")
124 expected_value = [
125 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
126 ('OBJECTCOL', oracledb.DB_TYPE_OBJECT, None, None, None, None, 1),
127 ('ARRAYCOL', oracledb.DB_TYPE_OBJECT, None, None, None, None, 1)
128 ]
129 self.assertEqual(self.cursor.description, expected_value)
130 expected_value = (
131 1,
132 'First row',
133 'First ',
134 'N First Row',
135 'N First ',
136 b'Raw Data 1',
137 2,
138 5,
139 12.125,
140 0.5,
141 12.5,
142 25.25,
143 50.125,
144 oracledb.Timestamp(2007, 3, 6, 0, 0, 0),
145 oracledb.Timestamp(2008, 9, 12, 16, 40),
146 oracledb.Timestamp(2009, 10, 13, 17, 50),
147 oracledb.Timestamp(2010, 11, 14, 18, 55),
148 'Short CLOB value',
149 'Short NCLOB Value',
150 b'Short BLOB value',
151 (11, 'Sub object 1'),
152 [(5, 'first element'), (6, 'second element')]
153 )
154 self.__test_data(1, expected_value, [5, 10, None, 20])
155 self.__test_data(2, None, [3, None, 9, 12, 15])
156 expected_value = (
157 3,
158 'Third row',
159 'Third ',
160 'N Third Row',
161 'N Third ',
162 b'Raw Data 3',
163 4,
164 10,
165 6.5,
166 0.75,
167 43.25,
168 86.5,
169 192.125,
170 oracledb.Timestamp(2007, 6, 21, 0, 0, 0),
171 oracledb.Timestamp(2007, 12, 13, 7, 30, 45),
172 oracledb.Timestamp(2017, 6, 21, 23, 18, 45),
173 oracledb.Timestamp(2017, 7, 21, 8, 27, 13),
174 'Another short CLOB value',
175 'Another short NCLOB Value',
176 b'Yet another short BLOB value',
177 (13, 'Sub object 3'),
178 [
179 (10, 'element #1'),
180 (20, 'element #2'),
181 (30, 'element #3'),
182 (40, 'element #4')
183 ]
184 )
185 self.__test_data(3, expected_value, None)
186
187 def test_2305_get_object_type(self):
188 "2305 - test getting object type"
189 type_obj = self.connection.gettype("UDT_OBJECT")
190 self.assertEqual(type_obj.iscollection, False)
191 self.assertEqual(type_obj.schema, self.connection.username.upper())
192 self.assertEqual(type_obj.name, "UDT_OBJECT")
193 sub_object_value_type = self.connection.gettype("UDT_SUBOBJECT")
194 sub_object_array_type = self.connection.gettype("UDT_OBJECTARRAY")
195 expected_attr_names = [
196 "NUMBERVALUE",
197 "STRINGVALUE",
198 "FIXEDCHARVALUE",
199 "NSTRINGVALUE",
200 "NFIXEDCHARVALUE",
201 "RAWVALUE",
202 "INTVALUE",
203 "SMALLINTVALUE",
204 "REALVALUE",
205 "DOUBLEPRECISIONVALUE",
206 "FLOATVALUE",
207 "BINARYFLOATVALUE",
208 "BINARYDOUBLEVALUE",
209 "DATEVALUE",
210 "TIMESTAMPVALUE",
211 "TIMESTAMPTZVALUE",
212 "TIMESTAMPLTZVALUE",
213 "CLOBVALUE",
214 "NCLOBVALUE",
215 "BLOBVALUE",
216 "SUBOBJECTVALUE",
217 "SUBOBJECTARRAY"
218 ]
219 actual_attr_names = [a.name for a in type_obj.attributes]
220 self.assertEqual(actual_attr_names, expected_attr_names)
221 expected_attr_types = [
222 oracledb.DB_TYPE_NUMBER,
223 oracledb.DB_TYPE_VARCHAR,
224 oracledb.DB_TYPE_CHAR,
225 oracledb.DB_TYPE_NVARCHAR,
226 oracledb.DB_TYPE_NCHAR,
227 oracledb.DB_TYPE_RAW,
228 oracledb.DB_TYPE_NUMBER,
229 oracledb.DB_TYPE_NUMBER,
230 oracledb.DB_TYPE_NUMBER,
231 oracledb.DB_TYPE_NUMBER,
232 oracledb.DB_TYPE_NUMBER,
233 oracledb.DB_TYPE_BINARY_FLOAT,
234 oracledb.DB_TYPE_BINARY_DOUBLE,
235 oracledb.DB_TYPE_DATE,
236 oracledb.DB_TYPE_TIMESTAMP,
237 oracledb.DB_TYPE_TIMESTAMP_TZ,
238 oracledb.DB_TYPE_TIMESTAMP_LTZ,
239 oracledb.DB_TYPE_CLOB,
240 oracledb.DB_TYPE_NCLOB,
241 oracledb.DB_TYPE_BLOB,
242 sub_object_value_type,
243 sub_object_array_type
244 ]
245 actual_attr_types = [a.type for a in type_obj.attributes]
246 self.assertEqual(actual_attr_types, expected_attr_types)
247 self.assertEqual(sub_object_array_type.iscollection, True)
248 self.assertEqual(sub_object_array_type.attributes, [])
249
250 def test_2306_object_type(self):
251 "2306 - test object type data"
252 self.cursor.execute("""
253 select ObjectCol
254 from TestObjects
255 where ObjectCol is not null
256 and rownum <= 1""")
257 obj, = self.cursor.fetchone()
258 self.assertEqual(obj.type.schema, self.connection.username.upper())
259 self.assertEqual(obj.type.name, "UDT_OBJECT")
260 self.assertEqual(obj.type.attributes[0].name, "NUMBERVALUE")
261
262 def test_2307_round_trip_object(self):
263 "2307 - test inserting and then querying object with all data types"
264 self.cursor.execute("alter session set time_zone = 'UTC'")
265 self.cursor.execute("truncate table TestClobs")
266 self.cursor.execute("truncate table TestNClobs")
267 self.cursor.execute("truncate table TestBlobs")
268 self.cursor.execute("insert into TestClobs values " \
269 "(1, 'A short CLOB')")
270 self.cursor.execute("insert into TestNClobs values " \
271 "(1, 'A short NCLOB')")
272 self.cursor.execute("insert into TestBlobs values " \
273 "(1, utl_raw.cast_to_raw('A short BLOB'))")
274 self.connection.commit()
275 self.cursor.execute("select CLOBCol from TestClobs")
276 clob, = self.cursor.fetchone()
277 self.cursor.execute("select NCLOBCol from TestNClobs")
278 nclob, = self.cursor.fetchone()
279 self.cursor.execute("select BLOBCol from TestBlobs")
280 blob, = self.cursor.fetchone()
281 type_obj = self.connection.gettype("UDT_OBJECT")
282 obj = type_obj.newobject()
283 obj.NUMBERVALUE = 5
284 obj.STRINGVALUE = "A string"
285 obj.FIXEDCHARVALUE = "Fixed str"
286 obj.NSTRINGVALUE = "A NCHAR string"
287 obj.NFIXEDCHARVALUE = "Fixed N"
288 obj.RAWVALUE = b"Raw Value"
289 obj.INTVALUE = 27
290 obj.SMALLINTVALUE = 13
291 obj.REALVALUE = 184.875
292 obj.DOUBLEPRECISIONVALUE = 1.375
293 obj.FLOATVALUE = 23.75
294 obj.DATEVALUE = datetime.date(2017, 5, 9)
295 obj.TIMESTAMPVALUE = datetime.datetime(2017, 5, 9, 9, 41, 13)
296 obj.TIMESTAMPTZVALUE = datetime.datetime(1986, 8, 2, 15, 27, 38)
297 obj.TIMESTAMPLTZVALUE = datetime.datetime(1999, 11, 12, 23, 5, 2)
298 obj.BINARYFLOATVALUE = 14.25
299 obj.BINARYDOUBLEVALUE = 29.1625
300 obj.CLOBVALUE = clob
301 obj.NCLOBVALUE = nclob
302 obj.BLOBVALUE = blob
303 sub_type_obj = self.connection.gettype("UDT_SUBOBJECT")
304 sub_obj = sub_type_obj.newobject()
305 sub_obj.SUBNUMBERVALUE = 23
306 sub_obj.SUBSTRINGVALUE = "Substring value"
307 obj.SUBOBJECTVALUE = sub_obj
308 self.cursor.execute("insert into TestObjects (IntCol, ObjectCol) " \
309 "values (4, :obj)", obj = obj)
310 self.cursor.execute("""
311 select IntCol, ObjectCol, ArrayCol
312 from TestObjects
313 where IntCol = 4""")
314 expected_value = (
315 5,
316 'A string',
317 'Fixed str ',
318 'A NCHAR string',
319 'Fixed N ',
320 b'Raw Value',
321 27,
322 13,
323 184.875,
324 1.375,
325 23.75,
326 14.25,
327 29.1625,
328 oracledb.Timestamp(2017, 5, 9, 0, 0, 0),
329 oracledb.Timestamp(2017, 5, 9, 9, 41, 13),
330 oracledb.Timestamp(1986, 8, 2, 15, 27, 38),
331 oracledb.Timestamp(1999, 11, 12, 23, 5, 2),
332 'A short CLOB',
333 'A short NCLOB',
334 b'A short BLOB',
335 (23, 'Substring value'),
336 None
337 )
338 self.__test_data(4, expected_value, None)
339 obj.CLOBVALUE = "A short CLOB (modified)"
340 obj.NCLOBVALUE = "A short NCLOB (modified)"
341 obj.BLOBVALUE = "A short BLOB (modified)"
342 self.cursor.execute("insert into TestObjects (IntCol, ObjectCol) " \
343 "values (5, :obj)", obj = obj)
344 self.cursor.execute("""
345 select IntCol, ObjectCol, ArrayCol
346 from TestObjects
347 where IntCol = 5""")
348 expected_value = (
349 5,
350 'A string',
351 'Fixed str ',
352 'A NCHAR string',
353 'Fixed N ',
354 b'Raw Value',
355 27,
356 13,
357 184.875,
358 1.375,
359 23.75,
360 14.25,
361 29.1625,
362 oracledb.Timestamp(2017, 5, 9, 0, 0, 0),
363 oracledb.Timestamp(2017, 5, 9, 9, 41, 13),
364 oracledb.Timestamp(1986, 8, 2, 15, 27, 38),
365 oracledb.Timestamp(1999, 11, 12, 23, 5, 2),
366 'A short CLOB (modified)',
367 'A short NCLOB (modified)',
368 b'A short BLOB (modified)',
369 (23, 'Substring value'),
370 None
371 )
372 self.__test_data(5, expected_value, None)
373 self.connection.rollback()
374
375 def test_2308_invalid_type_object(self):
376 "2308 - test trying to find an object type that does not exist"
377 self.assertRaises(oracledb.DatabaseError, self.connection.gettype,
378 "A TYPE THAT DOES NOT EXIST")
379
380 def test_2309_appending_wrong_object_type(self):
381 "2309 - test appending an object of the wrong type to a collection"
382 collection_obj_type = self.connection.gettype("UDT_OBJECTARRAY")
383 collection_obj = collection_obj_type.newobject()
384 array_obj_type = self.connection.gettype("UDT_ARRAY")
385 array_obj = array_obj_type.newobject()
386 self.assertRaises(oracledb.DatabaseError, collection_obj.append,
387 array_obj)
388
389 def test_2310_referencing_sub_obj(self):
390 "2310 - test that referencing a sub object affects the parent object"
391 obj_type = self.connection.gettype("UDT_OBJECT")
392 sub_obj_type = self.connection.gettype("UDT_SUBOBJECT")
393 obj = obj_type.newobject()
394 obj.SUBOBJECTVALUE = sub_obj_type.newobject()
395 obj.SUBOBJECTVALUE.SUBNUMBERVALUE = 5
396 obj.SUBOBJECTVALUE.SUBSTRINGVALUE = "Substring"
397 self.assertEqual(obj.SUBOBJECTVALUE.SUBNUMBERVALUE, 5)
398 self.assertEqual(obj.SUBOBJECTVALUE.SUBSTRINGVALUE, "Substring")
399
400 def test_2311_access_sub_object_parent_object_destroyed(self):
401 "2311 - test accessing sub object after parent object destroyed"
402 obj_type = self.connection.gettype("UDT_OBJECT")
403 sub_obj_type = self.connection.gettype("UDT_SUBOBJECT")
404 array_type = self.connection.gettype("UDT_OBJECTARRAY")
405 sub_obj1 = sub_obj_type.newobject()
406 sub_obj1.SUBNUMBERVALUE = 2
407 sub_obj1.SUBSTRINGVALUE = "AB"
408 sub_obj2 = sub_obj_type.newobject()
409 sub_obj2.SUBNUMBERVALUE = 3
410 sub_obj2.SUBSTRINGVALUE = "CDE"
411 obj = obj_type.newobject()
412 obj.SUBOBJECTARRAY = array_type.newobject([sub_obj1, sub_obj2])
413 sub_obj_array = obj.SUBOBJECTARRAY
414 del obj
415 self.assertEqual(self.__get_object_as_tuple(sub_obj_array),
416 [(2, "AB"), (3, "CDE")])
417
418 def test_2312_setting_attr_wrong_object_type(self):
419 "2312 - test assigning an object of wrong type to an object attribute"
420 obj_type = self.connection.gettype("UDT_OBJECT")
421 obj = obj_type.newobject()
422 wrong_obj_type = self.connection.gettype("UDT_OBJECTARRAY")
423 wrong_obj = wrong_obj_type.newobject()
424 self.assertRaises(oracledb.DatabaseError, setattr, obj,
425 "SUBOBJECTVALUE", wrong_obj)
426
427 def test_2313_setting_var_wrong_object_type(self):
428 "2313 - test setting value of object variable to wrong object type"
429 wrong_obj_type = self.connection.gettype("UDT_OBJECTARRAY")
430 wrong_obj = wrong_obj_type.newobject()
431 var = self.cursor.var(oracledb.DB_TYPE_OBJECT, typename="UDT_OBJECT")
432 self.assertRaises(oracledb.DatabaseError, var.setvalue, 0, wrong_obj)
433
434 def test_2314_string_format(self):
435 "2314 - test object string format"
436 obj_type = self.connection.gettype("UDT_OBJECT")
437 user = test_env.get_main_user()
438 self.assertEqual(str(obj_type),
439 "<cx_Oracle.ObjectType %s.UDT_OBJECT>" % user.upper())
440 self.assertEqual(str(obj_type.attributes[0]),
441 "<cx_Oracle.ObjectAttribute NUMBERVALUE>")
442
443 def test_2315_trim_collection_list(self):
444 "2315 - test Trim number of elements from collection"
445 sub_obj_type = self.connection.gettype("UDT_SUBOBJECT")
446 array_type = self.connection.gettype("UDT_OBJECTARRAY")
447 data = [(1, "AB"), (2, "CDE"), (3, "FGH"), (4, "IJK")]
448 array_obj = array_type()
449 for num_val, str_val in data:
450 subObj = sub_obj_type()
451 subObj.SUBNUMBERVALUE = num_val
452 subObj.SUBSTRINGVALUE = str_val
453 array_obj.append(subObj)
454 self.assertEqual(self.__get_object_as_tuple(array_obj), data)
455 array_obj.trim(2)
456 self.assertEqual(self.__get_object_as_tuple(array_obj), data[:2])
457 array_obj.trim(1)
458 self.assertEqual(self.__get_object_as_tuple(array_obj), data[:1])
459 array_obj.trim(0)
460 self.assertEqual(self.__get_object_as_tuple(array_obj), data[:1])
461 array_obj.trim(1)
462 self.assertEqual(self.__get_object_as_tuple(array_obj), [])
463
464 if __name__ == "__main__":
465 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 2400 - Module for testing session pools
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import threading
17
18 class TestCase(test_env.BaseTestCase):
19 require_connection = False
20
21 def __connect_and_drop(self):
22 connection = self.pool.acquire()
23 cursor = connection.cursor()
24 cursor.execute("select count(*) from TestNumbers")
25 count, = cursor.fetchone()
26 self.assertEqual(count, 10)
27
28 def __connect_and_generate_error(self):
29 connection = self.pool.acquire()
30 cursor = connection.cursor()
31 self.assertRaises(oracledb.DatabaseError, cursor.execute,
32 "select 1 / 0 from dual")
33
34 def __verify_connection(self, connection, expected_user,
35 expected_proxy_user=None):
36 cursor = connection.cursor()
37 cursor.execute("""
38 select
39 sys_context('userenv', 'session_user'),
40 sys_context('userenv', 'proxy_user')
41 from dual""")
42 actual_user, actual_proxy_user = cursor.fetchone()
43 self.assertEqual(actual_user, expected_user.upper())
44 self.assertEqual(actual_proxy_user,
45 expected_proxy_user and expected_proxy_user.upper())
46
47 def test_2400_pool(self):
48 "2400 - test that the pool is created and has the right attributes"
49 pool = test_env.get_pool(min=2, max=8, increment=3,
50 getmode=oracledb.SPOOL_ATTRVAL_WAIT)
51 self.assertEqual(pool.username, test_env.get_main_user(),
52 "user name differs")
53 self.assertEqual(pool.tnsentry, test_env.get_connect_string(),
54 "tnsentry differs")
55 self.assertEqual(pool.dsn, test_env.get_connect_string(),
56 "dsn differs")
57 self.assertEqual(pool.max, 8, "max differs")
58 self.assertEqual(pool.min, 2, "min differs")
59 self.assertEqual(pool.increment, 3, "increment differs")
60 self.assertEqual(pool.opened, 2, "opened differs")
61 self.assertEqual(pool.busy, 0, "busy not 0 at start")
62 connection_1 = pool.acquire()
63 self.assertEqual(pool.busy, 1, "busy not 1 after acquire")
64 self.assertEqual(pool.opened, 2, "opened not unchanged (1)")
65 connection_2 = pool.acquire()
66 self.assertEqual(pool.busy, 2, "busy not 2 after acquire")
67 self.assertEqual(pool.opened, 2, "opened not unchanged (2)")
68 connection_3 = pool.acquire()
69 self.assertEqual(pool.busy, 3, "busy not 3 after acquire")
70 self.assertEqual(pool.opened, 5, "opened not changed (3)")
71 pool.release(connection_3)
72 self.assertEqual(pool.busy, 2, "busy not 2 after release")
73 del connection_2
74 self.assertEqual(pool.busy, 1, "busy not 1 after del")
75 pool.getmode = oracledb.SPOOL_ATTRVAL_NOWAIT
76 self.assertEqual(pool.getmode, oracledb.SPOOL_ATTRVAL_NOWAIT)
77 if test_env.get_client_version() >= (12, 2):
78 pool.getmode = oracledb.SPOOL_ATTRVAL_TIMEDWAIT
79 self.assertEqual(pool.getmode, oracledb.SPOOL_ATTRVAL_TIMEDWAIT)
80 pool.stmtcachesize = 50
81 self.assertEqual(pool.stmtcachesize, 50)
82 pool.timeout = 10
83 self.assertEqual(pool.timeout, 10)
84 if test_env.get_client_version() >= (12, 1):
85 pool.max_lifetime_session = 10
86 self.assertEqual(pool.max_lifetime_session, 10)
87
88 def test_2401_proxy_auth(self):
89 "2401 - test that proxy authentication is possible"
90 pool = test_env.get_pool(min=2, max=8, increment=3,
91 getmode=oracledb.SPOOL_ATTRVAL_WAIT)
92 self.assertEqual(pool.homogeneous, True,
93 "homogeneous should be True by default")
94 self.assertRaises(oracledb.DatabaseError, pool.acquire,
95 user="missing_proxyuser")
96 pool = test_env.get_pool(min=2, max=8, increment=3,
97 getmode=oracledb.SPOOL_ATTRVAL_WAIT,
98 homogeneous=False)
99 msg = "homogeneous should be False after setting it in the constructor"
100 self.assertEqual(pool.homogeneous, False, msg)
101 connection = pool.acquire(user=test_env.get_proxy_user())
102 cursor = connection.cursor()
103 cursor.execute('select user from dual')
104 result, = cursor.fetchone()
105 self.assertEqual(result, test_env.get_proxy_user().upper())
106
107 def test_2402_rollback_on_del(self):
108 "2402 - connection rolls back before being destroyed"
109 pool = test_env.get_pool()
110 connection = pool.acquire()
111 cursor = connection.cursor()
112 cursor.execute("truncate table TestTempTable")
113 cursor.execute("insert into TestTempTable (IntCol) values (1)")
114 pool = test_env.get_pool()
115 connection = pool.acquire()
116 cursor = connection.cursor()
117 cursor.execute("select count(*) from TestTempTable")
118 count, = cursor.fetchone()
119 self.assertEqual(count, 0)
120
121 def test_2403_rollback_on_release(self):
122 "2403 - connection rolls back before released back to the pool"
123 pool = test_env.get_pool()
124 connection = pool.acquire()
125 cursor = connection.cursor()
126 cursor.execute("truncate table TestTempTable")
127 cursor.execute("insert into TestTempTable (IntCol) values (1)")
128 cursor.close()
129 pool.release(connection)
130 pool = test_env.get_pool()
131 connection = pool.acquire()
132 cursor = connection.cursor()
133 cursor.execute("select count(*) from TestTempTable")
134 count, = cursor.fetchone()
135 self.assertEqual(count, 0)
136
137 def test_2404_threading(self):
138 "2404 - test session pool with multiple threads"
139 self.pool = test_env.get_pool(min=5, max=20, increment=2,
140 threaded=True,
141 getmode=oracledb.SPOOL_ATTRVAL_WAIT)
142 threads = []
143 for i in range(20):
144 thread = threading.Thread(None, self.__connect_and_drop)
145 threads.append(thread)
146 thread.start()
147 for thread in threads:
148 thread.join()
149
150 def test_2405_threading_with_errors(self):
151 "2405 - test session pool with multiple threads (with errors)"
152 self.pool = test_env.get_pool(min=5, max=20, increment=2,
153 threaded=True,
154 getmode=oracledb.SPOOL_ATTRVAL_WAIT)
155 threads = []
156 for i in range(20):
157 thread = threading.Thread(None, self.__connect_and_generate_error)
158 threads.append(thread)
159 thread.start()
160 for thread in threads:
161 thread.join()
162
163 def test_2406_purity(self):
164 "2406 - test session pool with various types of purity"
165 pool = test_env.get_pool(min=1, max=8, increment=1,
166 getmode=oracledb.SPOOL_ATTRVAL_WAIT)
167
168 # get connection and set the action
169 action = "TEST_ACTION"
170 connection = pool.acquire()
171 connection.action = action
172 cursor = connection.cursor()
173 cursor.execute("select 1 from dual")
174 cursor.close()
175 pool.release(connection)
176 self.assertEqual(pool.opened, 1, "opened (1)")
177
178 # verify that the connection still has the action set on it
179 connection = pool.acquire()
180 cursor = connection.cursor()
181 cursor.execute("select sys_context('userenv', 'action') from dual")
182 result, = cursor.fetchone()
183 self.assertEqual(result, action)
184 cursor.close()
185 pool.release(connection)
186 self.assertEqual(pool.opened, 1, "opened (2)")
187
188 # get a new connection with new purity (should not have state)
189 connection = pool.acquire(purity=oracledb.ATTR_PURITY_NEW)
190 cursor = connection.cursor()
191 cursor.execute("select sys_context('userenv', 'action') from dual")
192 result, = cursor.fetchone()
193 self.assertEqual(result, None)
194 cursor.close()
195 self.assertEqual(pool.opened, 2, "opened (3)")
196 pool.drop(connection)
197 self.assertEqual(pool.opened, 1, "opened (4)")
198
199 def test_2407_heterogeneous(self):
200 "2407 - test heterogeneous pool with user and password specified"
201 pool = test_env.get_pool(min=2, max=8, increment=3, homogeneous=False,
202 getmode=oracledb.SPOOL_ATTRVAL_WAIT)
203 self.assertEqual(pool.homogeneous, 0)
204 self.__verify_connection(pool.acquire(), test_env.get_main_user())
205 self.__verify_connection(pool.acquire(test_env.get_main_user(),
206 test_env.get_main_password()),
207 test_env.get_main_user())
208 self.__verify_connection(pool.acquire(test_env.get_proxy_user(),
209 test_env.get_proxy_password()),
210 test_env.get_proxy_user())
211 user_str = "%s[%s]" % \
212 (test_env.get_main_user(), test_env.get_proxy_user())
213 self.__verify_connection(pool.acquire(user_str,
214 test_env.get_main_password()),
215 test_env.get_proxy_user(),
216 test_env.get_main_user())
217
218 def test_2408_heterogenous_without_user(self):
219 "2408 - test heterogeneous pool without user and password specified"
220 pool = test_env.get_pool(user="", password="", min=2, max=8,
221 increment=3,
222 getmode=oracledb.SPOOL_ATTRVAL_WAIT,
223 homogeneous=False)
224 self.__verify_connection(pool.acquire(test_env.get_main_user(),
225 test_env.get_main_password()),
226 test_env.get_main_user())
227 self.__verify_connection(pool.acquire(test_env.get_proxy_user(),
228 test_env.get_proxy_password()),
229 test_env.get_proxy_user())
230 user_str = "%s[%s]" % \
231 (test_env.get_main_user(), test_env.get_proxy_user())
232 self.__verify_connection(pool.acquire(user_str,
233 test_env.get_main_password()),
234 test_env.get_proxy_user(),
235 test_env.get_main_user())
236
237 def test_2409_heterogeneous_wrong_password(self):
238 "2409 - test heterogeneous pool with wrong password specified"
239 pool = test_env.get_pool(min=2, max=8, increment=3,
240 getmode=oracledb.SPOOL_ATTRVAL_WAIT,
241 homogeneous=False)
242 self.assertRaises(oracledb.DatabaseError, pool.acquire,
243 test_env.get_proxy_user(),
244 "this is the wrong password")
245
246 def test_2410_tagging_session(self):
247 "2410 - test tagging a session"
248 pool = test_env.get_pool(min=2, max=8, increment=3,
249 getmode=oracledb.SPOOL_ATTRVAL_NOWAIT)
250
251 tag_mst = "TIME_ZONE=MST"
252 tag_utc = "TIME_ZONE=UTC"
253
254 conn = pool.acquire()
255 self.assertEqual(conn.tag, None)
256 pool.release(conn, tag=tag_mst)
257
258 conn = pool.acquire()
259 self.assertEqual(conn.tag, None)
260 conn.tag = tag_utc
261 conn.close()
262
263 conn = pool.acquire(tag=tag_mst)
264 self.assertEqual(conn.tag, tag_mst)
265 conn.close()
266
267 conn = pool.acquire(tag=tag_utc)
268 self.assertEqual(conn.tag, tag_utc)
269 conn.close()
270
271 def test_2411_plsql_session_callbacks(self):
272 "2411 - test PL/SQL session callbacks"
273 if test_env.get_client_version() < (12, 2):
274 self.skipTest("PL/SQL session callbacks not supported before 12.2")
275 callback = "pkg_SessionCallback.TheCallback"
276 pool = test_env.get_pool(min=2, max=8, increment=3,
277 getmode=oracledb.SPOOL_ATTRVAL_NOWAIT,
278 sessionCallback=callback)
279 tags = [
280 "NLS_DATE_FORMAT=SIMPLE",
281 "NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC",
282 "NLS_DATE_FORMAT=FULL;TIME_ZONE=MST"
283 ]
284 actual_tags = [None, None, "NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC"]
285
286 # truncate PL/SQL session callback log
287 conn = pool.acquire()
288 cursor = conn.cursor()
289 cursor.execute("truncate table PLSQLSessionCallbacks")
290 conn.close()
291
292 # request sessions with each of the first two tags
293 for tag in tags[:2]:
294 conn = pool.acquire(tag=tag)
295 conn.close()
296
297 # for the last tag, use the matchanytag flag
298 conn = pool.acquire(tag=tags[2], matchanytag=True)
299 conn.close()
300
301 # verify the PL/SQL session callback log is accurate
302 conn = pool.acquire()
303 cursor = conn.cursor()
304 cursor.execute("""
305 select RequestedTag, ActualTag
306 from PLSQLSessionCallbacks
307 order by FixupTimestamp""")
308 results = cursor.fetchall()
309 expected_results = list(zip(tags, actual_tags))
310 self.assertEqual(results, expected_results)
311
312 def test_2412_tagging_invalid_key(self):
313 "2412 - testTagging with Invalid key"
314 pool = test_env.get_pool(getmode=oracledb.SPOOL_ATTRVAL_NOWAIT)
315 conn = pool.acquire()
316 self.assertRaises(TypeError, pool.release, conn, tag=12345)
317 if test_env.get_client_version() >= (12, 2):
318 self.assertRaises(oracledb.DatabaseError, pool.release, conn,
319 tag="INVALID_TAG")
320
321 if __name__ == "__main__":
322 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 2500 - Module for testing string variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import datetime
17 import string
18 import random
19
20 class TestCase(test_env.BaseTestCase):
21
22 def setUp(self):
23 super().setUp()
24 self.raw_data = []
25 self.data_by_key = {}
26 for i in range(1, 11):
27 string_col = "String %d" % i
28 fixed_char_col = ("Fixed Char %d" % i).ljust(40)
29 raw_col = ("Raw %d" % i).encode("ascii")
30 if i % 2:
31 nullable_col = "Nullable %d" % i
32 else:
33 nullable_col = None
34 data_tuple = (i, string_col, raw_col, fixed_char_col, nullable_col)
35 self.raw_data.append(data_tuple)
36 self.data_by_key[i] = data_tuple
37
38 def test_2500_array_with_increased_size(self):
39 "2500 - test creating array var and then increasing the internal size"
40 val = ["12345678901234567890"] * 3
41 var = self.cursor.arrayvar(str, len(val), 4)
42 var.setvalue(0, val)
43 self.assertEqual(var.getvalue(), val)
44
45 def test_2501_bind_string(self):
46 "2501 - test binding in a string"
47 self.cursor.execute("""
48 select * from TestStrings
49 where StringCol = :value""",
50 value="String 5")
51 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]])
52
53 def test_2502_bind_different_var(self):
54 "2502 - test binding a different variable on second execution"
55 retval_1 = self.cursor.var(oracledb.STRING, 30)
56 retval_2 = self.cursor.var(oracledb.STRING, 30)
57 self.cursor.execute("begin :retval := 'Called'; end;",
58 retval=retval_1)
59 self.assertEqual(retval_1.getvalue(), "Called")
60 self.cursor.execute("begin :retval := 'Called'; end;",
61 retval=retval_2)
62 self.assertEqual(retval_2.getvalue(), "Called")
63
64 def test_2503_exceeds_num_elements(self):
65 "2503 - test exceeding the number of elements returns IndexError"
66 var = self.cursor.var(str)
67 self.assertRaises(IndexError, var.getvalue, 1)
68
69 def test_2504_bind_string_after_number(self):
70 "2504 - test binding in a string after setting input sizes to a number"
71 self.cursor.setinputsizes(value = oracledb.NUMBER)
72 self.cursor.execute("""
73 select * from TestStrings
74 where StringCol = :value""",
75 value="String 6")
76 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[6]])
77
78 def test_2505_bind_string_array_direct(self):
79 "2505 - test binding in a string array"
80 return_value = self.cursor.var(oracledb.NUMBER)
81 array = [r[1] for r in self.raw_data]
82 statement = """
83 begin
84 :return_value := pkg_TestStringArrays.TestInArrays(
85 :integer_value, :array);
86 end;"""
87 self.cursor.execute(statement, return_value=return_value,
88 integer_value=5, array=array)
89 self.assertEqual(return_value.getvalue(), 86)
90 array = [ "String - %d" % i for i in range(15) ]
91 self.cursor.execute(statement, integer_value=8, array=array)
92 self.assertEqual(return_value.getvalue(), 163)
93
94 def test_2506_bind_string_array_by_sizes(self):
95 "2506 - test binding in a string array (with setinputsizes)"
96 return_value = self.cursor.var(oracledb.NUMBER)
97 self.cursor.setinputsizes(array=[oracledb.STRING, 10])
98 array = [r[1] for r in self.raw_data]
99 self.cursor.execute("""
100 begin
101 :return_value := pkg_TestStringArrays.TestInArrays(
102 :integer_value, :array);
103 end;""",
104 return_value=return_value,
105 integer_value=6,
106 array=array)
107 self.assertEqual(return_value.getvalue(), 87)
108
109 def test_2507_bind_string_array_by_var(self):
110 "2507 - test binding in a string array (with arrayvar)"
111 return_value = self.cursor.var(oracledb.NUMBER)
112 array = self.cursor.arrayvar(oracledb.STRING, 10, 20)
113 array.setvalue(0, [r[1] for r in self.raw_data])
114 self.cursor.execute("""
115 begin
116 :return_value := pkg_TestStringArrays.TestInArrays(
117 :integer_value, :array);
118 end;""",
119 return_value=return_value,
120 integer_value=7,
121 array=array)
122 self.assertEqual(return_value.getvalue(), 88)
123
124 def test_2508_bind_in_out_string_array_by_var(self):
125 "2508 - test binding in/out a string array (with arrayvar)"
126 array = self.cursor.arrayvar(oracledb.STRING, 10, 100)
127 original_data = [r[1] for r in self.raw_data]
128 expected_data = ["Converted element # %d originally had length %d" % \
129 (i, len(original_data[i - 1])) \
130 for i in range(1, 6)] + original_data[5:]
131 array.setvalue(0, original_data)
132 self.cursor.execute("""
133 begin
134 pkg_TestStringArrays.TestInOutArrays(:num_elems, :array);
135 end;""",
136 num_elems=5,
137 array=array)
138 self.assertEqual(array.getvalue(), expected_data)
139
140 def test_2509_bind_out_string_array_by_var(self):
141 "2509 - test binding out a string array (with arrayvar)"
142 array = self.cursor.arrayvar(oracledb.STRING, 6, 100)
143 expected_data = ["Test out element # %d" % i for i in range(1, 7)]
144 self.cursor.execute("""
145 begin
146 pkg_TestStringArrays.TestOutArrays(:num_elems, :array);
147 end;""",
148 num_elems=6,
149 array=array)
150 self.assertEqual(array.getvalue(), expected_data)
151
152 def test_2510_bind_raw(self):
153 "2510 - test binding in a raw"
154 self.cursor.setinputsizes(value = oracledb.BINARY)
155 self.cursor.execute("""
156 select * from TestStrings
157 where RawCol = :value""",
158 value="Raw 4".encode())
159 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[4]])
160
161 def test_2511_bind_and_fetch_rowid(self):
162 "2511 - test binding (and fetching) a rowid"
163 self.cursor.execute("""
164 select rowid
165 from TestStrings
166 where IntCol = 3""")
167 rowid, = self.cursor.fetchone()
168 self.cursor.execute("""
169 select *
170 from TestStrings
171 where rowid = :value""",
172 value=rowid)
173 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[3]])
174
175 def test_2512_bind_and_fetch_universal_rowids(self):
176 "2512 - test binding (and fetching) universal rowids"
177 self.cursor.execute("truncate table TestUniversalRowids")
178 data = [
179 (1, "ABC" * 75, datetime.datetime(2017, 4, 11)),
180 (2, "DEF" * 80, datetime.datetime(2017, 4, 12))
181 ]
182 for row in data:
183 self.cursor.execute("""
184 insert into TestUniversalRowids
185 values (:1, :2, :3)""", row)
186 self.connection.commit()
187 self.cursor.execute("""
188 select rowid
189 from TestUniversalRowIds
190 order by IntCol""")
191 rowids = [r for r, in self.cursor]
192 fetched_data = []
193 for rowid in rowids:
194 self.cursor.execute("""
195 select *
196 from TestUniversalRowids
197 where rowid = :rid""",
198 rid=rowid)
199 fetched_data.extend(self.cursor.fetchall())
200 self.assertEqual(fetched_data, data)
201
202 def test_2513_bind_null(self):
203 "2513 - test binding in a null"
204 self.cursor.execute("""
205 select * from TestStrings
206 where StringCol = :value""",
207 value=None)
208 self.assertEqual(self.cursor.fetchall(), [])
209
210 def test_2514_bind_out_set_input_sizes_by_type(self):
211 "2514 - test binding out with set input sizes defined (by type)"
212 bind_vars = self.cursor.setinputsizes(value=oracledb.STRING)
213 self.cursor.execute("""
214 begin
215 :value := 'TSI';
216 end;""")
217 self.assertEqual(bind_vars["value"].getvalue(), "TSI")
218
219 def test_2515_bind_out_set_input_sizes_by_integer(self):
220 "2515 - test binding out with set input sizes defined (by integer)"
221 bind_vars = self.cursor.setinputsizes(value=30)
222 self.cursor.execute("""
223 begin
224 :value := 'TSI (I)';
225 end;""")
226 self.assertEqual(bind_vars["value"].getvalue(), "TSI (I)")
227
228 def test_2516_bind_in_out_set_input_sizes_by_type(self):
229 "2516 - test binding in/out with set input sizes defined (by type)"
230 bind_vars = self.cursor.setinputsizes(value=oracledb.STRING)
231 self.cursor.execute("""
232 begin
233 :value := :value || ' TSI';
234 end;""",
235 value="InVal")
236 self.assertEqual(bind_vars["value"].getvalue(), "InVal TSI")
237
238 def test_2517_bind_in_out_set_input_sizes_by_integer(self):
239 "2517 - test binding in/out with set input sizes defined (by integer)"
240 bind_vars = self.cursor.setinputsizes(value=30)
241 self.cursor.execute("""
242 begin
243 :value := :value || ' TSI (I)';
244 end;""",
245 value="InVal")
246 self.assertEqual(bind_vars["value"].getvalue(), "InVal TSI (I)")
247
248 def test_2518_bind_out_var(self):
249 "2518 - test binding out with cursor.var() method"
250 var = self.cursor.var(oracledb.STRING)
251 self.cursor.execute("""
252 begin
253 :value := 'TSI (VAR)';
254 end;""",
255 value=var)
256 self.assertEqual(var.getvalue(), "TSI (VAR)")
257
258 def test_2519_bind_in_out_var_direct_set(self):
259 "2519 - test binding in/out with cursor.var() method"
260 var = self.cursor.var(oracledb.STRING)
261 var.setvalue(0, "InVal")
262 self.cursor.execute("""
263 begin
264 :value := :value || ' TSI (VAR)';
265 end;""",
266 value=var)
267 self.assertEqual(var.getvalue(), "InVal TSI (VAR)")
268
269 def test_2520_bind_long_string(self):
270 "2520 - test that binding a long string succeeds"
271 self.cursor.setinputsizes(big_string=oracledb.DB_TYPE_LONG)
272 self.cursor.execute("""
273 declare
274 t_Temp varchar2(20000);
275 begin
276 t_Temp := :big_string;
277 end;""",
278 big_string="X" * 10000)
279
280 def test_2521_bind_long_string_after_setting_size(self):
281 "2521 - test that setinputsizes() returns a long variable"
282 var = self.cursor.setinputsizes(test=90000)["test"]
283 in_string = "1234567890" * 9000
284 var.setvalue(0, in_string)
285 out_string = var.getvalue()
286 msg = f"output does not match: in was {len(in_string)}, " \
287 f"out was {len(out_string)}"
288 self.assertEqual(in_string, out_string, msg)
289
290 def test_2522_cursor_description(self):
291 "2522 - test cursor description is accurate"
292 self.cursor.execute("select * from TestStrings")
293 expected_value = [
294 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
295 ('STRINGCOL', oracledb.DB_TYPE_VARCHAR, 20,
296 20 * test_env.get_charset_ratio(), None, None, 0),
297 ('RAWCOL', oracledb.DB_TYPE_RAW, 30, 30, None, None, 0),
298 ('FIXEDCHARCOL', oracledb.DB_TYPE_CHAR, 40,
299 40 * test_env.get_charset_ratio(), None, None, 0),
300 ('NULLABLECOL', oracledb.DB_TYPE_VARCHAR, 50,
301 50 * test_env.get_charset_ratio(), None, None, 1)
302 ]
303 self.assertEqual(self.cursor.description, expected_value)
304
305 def test_2523_fetchall(self):
306 "2523 - test that fetching all of the data returns the correct results"
307 self.cursor.execute("select * From TestStrings order by IntCol")
308 self.assertEqual(self.cursor.fetchall(), self.raw_data)
309 self.assertEqual(self.cursor.fetchall(), [])
310
311 def test_2524_fetchmany(self):
312 "2524 - test that fetching data in chunks returns the correct results"
313 self.cursor.execute("select * From TestStrings order by IntCol")
314 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3])
315 self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5])
316 self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9])
317 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:])
318 self.assertEqual(self.cursor.fetchmany(3), [])
319
320 def test_2525_fetchone(self):
321 "2525 - test that fetching a single row returns the correct results"
322 self.cursor.execute("""
323 select *
324 from TestStrings
325 where IntCol in (3, 4)
326 order by IntCol""")
327 self.assertEqual(self.cursor.fetchone(), self.data_by_key[3])
328 self.assertEqual(self.cursor.fetchone(), self.data_by_key[4])
329 self.assertEqual(self.cursor.fetchone(), None)
330
331 def test_2526_supplemental_characters(self):
332 "2526 - test binding and fetching supplemental charcters"
333 self.cursor.execute("""
334 select value
335 from nls_database_parameters
336 where parameter = 'NLS_CHARACTERSET'""")
337 charset, = self.cursor.fetchone()
338 if charset != "AL32UTF8":
339 self.skipTest("Database character set must be AL32UTF8")
340 supplemental_chars = "𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿 𠸎 𠸏 𠹷 𠺝 " \
341 "𠺢 𠻗 𠻹 𠻺 𠼭 𠼮 𠽌 𠾴 𠾼 𠿪 𡁜 𡁯 𡁵 𡁶 𡁻 𡃁 𡃉 𡇙 𢃇 " \
342 "𢞵 𢫕 𢭃 𢯊 𢱑 𢱕 𢳂 𢴈 𢵌 𢵧 𢺳 𣲷 𤓓 𤶸 𤷪 𥄫 𦉘 𦟌 𦧲 " \
343 "𦧺 𧨾 𨅝 𨈇 𨋢 𨳊 𨳍 𨳒 𩶘"
344 self.cursor.execute("truncate table TestTempTable")
345 self.cursor.execute("""
346 insert into TestTempTable (IntCol, StringCol)
347 values (:1, :2)""",
348 (1, supplemental_chars))
349 self.connection.commit()
350 self.cursor.execute("select StringCol from TestTempTable")
351 value, = self.cursor.fetchone()
352 self.assertEqual(value, supplemental_chars)
353
354 def test_2527_bind_twice_with_large_string_second(self):
355 "2527 - test binding twice with a larger string the second time"
356 self.cursor.execute("truncate table TestTempTable")
357 sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)"
358 short_string = "short string"
359 long_string = "long string " * 30
360 self.cursor.execute(sql, (1, short_string))
361 self.cursor.execute(sql, (2, long_string))
362 self.connection.commit()
363 self.cursor.execute("""
364 select IntCol, StringCol
365 from TestTempTable
366 order by IntCol""")
367 self.assertEqual(self.cursor.fetchall(),
368 [(1, short_string), (2, long_string)])
369
370 def test_2528_issue_50(self):
371 "2528 - test issue 50 - avoid error ORA-24816"
372 cursor = self.connection.cursor()
373 try:
374 cursor.execute("drop table issue_50 purge")
375 except oracledb.DatabaseError:
376 pass
377 cursor.execute("""
378 create table issue_50 (
379 Id number(11) primary key,
380 Str1 nvarchar2(256),
381 Str2 nvarchar2(256),
382 Str3 nvarchar2(256),
383 NClob1 nclob,
384 NClob2 nclob
385 )""")
386 id_var = cursor.var(oracledb.NUMBER)
387 cursor.execute("""
388 insert into issue_50 (Id, Str2, Str3, NClob1, NClob2, Str1)
389 values (:arg0, :arg1, :arg2, :arg3, :arg4, :arg5)
390 returning id into :arg6""",
391 [1, '555a4c78', 'f319ef0e', '23009914', '', '', id_var])
392 cursor = self.connection.cursor()
393 cursor.execute("""
394 insert into issue_50 (Id, Str2, Str3, NClob1, NClob2, Str1)
395 values (:arg0, :arg1, :arg2, :arg3, :arg4, :arg5)
396 returning id into :arg6""",
397 [2, u'd5ff845a', u'94275767', u'bf161ff6', u'', u'', id_var])
398 cursor.execute("drop table issue_50 purge")
399
400 def test_2529_set_rowid_to_string(self):
401 "2529 - test assigning a string to rowid"
402 var = self.cursor.var(oracledb.ROWID)
403 self.assertRaises(oracledb.NotSupportedError, var.setvalue, 0,
404 "ABDHRYTHFJGKDKKDH")
405
406 def test_2530_short_xml_as_string(self):
407 "2530 - test fetching XMLType object as a string"
408 self.cursor.execute("""
409 select XMLElement("string", stringCol)
410 from TestStrings
411 where intCol = 1""")
412 actual_value, = self.cursor.fetchone()
413 expected_value = "<string>String 1</string>"
414 self.assertEqual(actual_value, expected_value)
415
416 def test_2531_long_xml_as_string(self):
417 "2531 - test inserting and fetching an XMLType object (1K) as a string"
418 chars = string.ascii_uppercase + string.ascii_lowercase
419 random_string = ''.join(random.choice(chars) for _ in range(1024))
420 int_val = 200
421 xml_string = '<data>' + random_string + '</data>'
422 self.cursor.execute("""
423 insert into TestXML (IntCol, XMLCol)
424 values (:1, :2)""", (int_val, xml_string))
425 self.cursor.execute("select XMLCol from TestXML where intCol = :1",
426 (int_val,))
427 actual_value, = self.cursor.fetchone()
428 self.assertEqual(actual_value.strip(), xml_string)
429
430 if __name__ == "__main__":
431 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 2600 - Module for testing timestamp variables
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import time
17
18 class TestCase(test_env.BaseTestCase):
19
20 def setUp(self):
21 super().setUp()
22 self.raw_data = []
23 self.data_by_key = {}
24 for i in range(1, 11):
25 time_tuple = (2002, 12, 9, 0, 0, 0, 0, 0, -1)
26 time_in_ticks = time.mktime(time_tuple) + i * 86400
27 date_value = oracledb.TimestampFromTicks(int(time_in_ticks))
28 str_value = str(i * 50)
29 fsecond = int(str_value + "0" * (6 - len(str_value)))
30 date_col = oracledb.Timestamp(date_value.year, date_value.month,
31 date_value.day, date_value.hour,
32 date_value.minute, i * 2, fsecond)
33 if i % 2:
34 time_in_ticks = time.mktime(time_tuple) + i * 86400 + 86400
35 date_value = oracledb.TimestampFromTicks(int(time_in_ticks))
36 str_value = str(i * 125)
37 fsecond = int(str_value + "0" * (6 - len(str_value)))
38 nullable_col = oracledb.Timestamp(date_value.year,
39 date_value.month,
40 date_value.day,
41 date_value.hour,
42 date_value.minute, i * 3,
43 fsecond)
44 else:
45 nullable_col = None
46 data_tuple = (i, date_col, nullable_col)
47 self.raw_data.append(data_tuple)
48 self.data_by_key[i] = data_tuple
49
50 def test_2600_bind_timestamp(self):
51 "2600 - test binding in a timestamp"
52 self.cursor.setinputsizes(value=oracledb.DB_TYPE_TIMESTAMP)
53 self.cursor.execute("""
54 select * from TestTimestamps
55 where TimestampCol = :value""",
56 value=oracledb.Timestamp(2002, 12, 14, 0, 0, 10, 250000))
57 self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]])
58
59 def test_2601_bind_null(self):
60 "2601 - test binding in a null"
61 self.cursor.setinputsizes(value=oracledb.DB_TYPE_TIMESTAMP)
62 self.cursor.execute("""
63 select * from TestTimestamps
64 where TimestampCol = :value""",
65 value=None)
66 self.assertEqual(self.cursor.fetchall(), [])
67
68 def test_2602_bind_out_set_input_sizes(self):
69 "2602 - test binding out with set input sizes defined"
70 bind_vars = self.cursor.setinputsizes(value=oracledb.DB_TYPE_TIMESTAMP)
71 self.cursor.execute("""
72 begin
73 :value := to_timestamp('20021209', 'YYYYMMDD');
74 end;""")
75 self.assertEqual(bind_vars["value"].getvalue(),
76 oracledb.Timestamp(2002, 12, 9))
77
78 def test_2603_bind_in_out_set_input_sizes(self):
79 "2603 - test binding in/out with set input sizes defined"
80 bind_vars = self.cursor.setinputsizes(value=oracledb.DB_TYPE_TIMESTAMP)
81 self.cursor.execute("""
82 begin
83 :value := :value + 5.25;
84 end;""",
85 value = oracledb.Timestamp(2002, 12, 12, 10, 0, 0))
86 self.assertEqual(bind_vars["value"].getvalue(),
87 oracledb.Timestamp(2002, 12, 17, 16, 0, 0))
88
89 def test_2604_bind_out_var(self):
90 "2604 - test binding out with cursor.var() method"
91 var = self.cursor.var(oracledb.DB_TYPE_TIMESTAMP)
92 self.cursor.execute("""
93 begin
94 :value := to_date('20021231 12:31:00',
95 'YYYYMMDD HH24:MI:SS');
96 end;""",
97 value=var)
98 self.assertEqual(var.getvalue(),
99 oracledb.Timestamp(2002, 12, 31, 12, 31, 0))
100
101 def test_2605_bind_in_out_var_direct_set(self):
102 "2605 - test binding in/out with cursor.var() method"
103 var = self.cursor.var(oracledb.DB_TYPE_TIMESTAMP)
104 var.setvalue(0, oracledb.Timestamp(2002, 12, 9, 6, 0, 0))
105 self.cursor.execute("""
106 begin
107 :value := :value + 5.25;
108 end;""",
109 value = var)
110 self.assertEqual(var.getvalue(),
111 oracledb.Timestamp(2002, 12, 14, 12, 0, 0))
112
113 def test_2606_cursor_description(self):
114 "2606 - test cursor description is accurate"
115 self.cursor.execute("select * from TestTimestamps")
116 expected_value = [
117 ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
118 ('TIMESTAMPCOL', oracledb.DB_TYPE_TIMESTAMP, 23, None, 0, 6, 0),
119 ('NULLABLECOL', oracledb.DB_TYPE_TIMESTAMP, 23, None, 0, 6, 1)
120 ]
121 self.assertEqual(self.cursor.description, expected_value)
122
123 def test_2607_fetchall(self):
124 "2607 - test that fetching all of the data returns the correct results"
125 self.cursor.execute("select * From TestTimestamps order by IntCol")
126 self.assertEqual(self.cursor.fetchall(), self.raw_data)
127 self.assertEqual(self.cursor.fetchall(), [])
128
129 def test_2608_fetchmany(self):
130 "2608 - test that fetching data in chunks returns the correct results"
131 self.cursor.execute("select * From TestTimestamps order by IntCol")
132 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3])
133 self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5])
134 self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9])
135 self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:])
136 self.assertEqual(self.cursor.fetchmany(3), [])
137
138 def test_2609_fetchone(self):
139 "2609 - test that fetching a single row returns the correct results"
140 self.cursor.execute("""
141 select *
142 from TestTimestamps
143 where IntCol in (3, 4)
144 order by IntCol""")
145 self.assertEqual(self.cursor.fetchone(), self.data_by_key[3])
146 self.assertEqual(self.cursor.fetchone(), self.data_by_key[4])
147 self.assertEqual(self.cursor.fetchone(), None)
148
149 if __name__ == "__main__":
150 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 2700 - Module for testing AQ
6 """
7
8 import test_env
9
10 import cx_Oracle as oracledb
11 import decimal
12 import threading
13
14 class TestCase(test_env.BaseTestCase):
15 book_type_name = "UDT_BOOK"
16 book_queue_name = "TEST_BOOK_QUEUE"
17 book_data = [
18 ("Wings of Fire", "A.P.J. Abdul Kalam", decimal.Decimal("15.75")),
19 ("The Story of My Life", "Hellen Keller", decimal.Decimal("10.50")),
20 ("The Chronicles of Narnia", "C.S. Lewis", decimal.Decimal("25.25"))
21 ]
22
23 def __clear_books_queue(self):
24 books_type = self.connection.gettype(self.book_type_name)
25 book = books_type.newobject()
26 options = self.connection.deqoptions()
27 options.wait = oracledb.DEQ_NO_WAIT
28 options.deliverymode = oracledb.MSG_PERSISTENT_OR_BUFFERED
29 options.visibility = oracledb.ENQ_IMMEDIATE
30 props = self.connection.msgproperties()
31 while self.connection.deq(self.book_queue_name, options, props, book):
32 pass
33
34 def __deq_in_thread(self, results):
35 connection = test_env.get_connection()
36 books_type = connection.gettype(self.book_type_name)
37 book = books_type.newobject()
38 options = connection.deqoptions()
39 options.wait = 10
40 props = connection.msgproperties()
41 if connection.deq(self.book_queue_name, options, props, book):
42 results.append((book.TITLE, book.AUTHORS, book.PRICE))
43 connection.commit()
44
45 def __verify_attr(self, obj, attrName, value):
46 setattr(obj, attrName, value)
47 self.assertEqual(getattr(obj, attrName), value)
48
49 def test_2700_deq_empty(self):
50 "2700 - test dequeuing an empty queue"
51 self.__clear_books_queue()
52 books_type = self.connection.gettype(self.book_type_name)
53 book = books_type.newobject()
54 options = self.connection.deqoptions()
55 options.wait = oracledb.DEQ_NO_WAIT
56 props = self.connection.msgproperties()
57 message_id = self.connection.deq(self.book_queue_name, options, props,
58 book)
59 self.assertTrue(message_id is None)
60
61 def test_2701_deq_enq(self):
62 "2701 - test enqueuing and dequeuing multiple messages"
63 self.__clear_books_queue()
64 books_type = self.connection.gettype(self.book_type_name)
65 options = self.connection.enqoptions()
66 props = self.connection.msgproperties()
67 for title, authors, price in self.book_data:
68 book = books_type.newobject()
69 book.TITLE = title
70 book.AUTHORS = authors
71 book.PRICE = price
72 self.connection.enq(self.book_queue_name, options, props, book)
73 options = self.connection.deqoptions()
74 options.navigation = oracledb.DEQ_FIRST_MSG
75 options.wait = oracledb.DEQ_NO_WAIT
76 results = []
77 while self.connection.deq(self.book_queue_name, options, props, book):
78 row = (book.TITLE, book.AUTHORS, book.PRICE)
79 results.append(row)
80 self.connection.commit()
81 self.assertEqual(results, self.book_data)
82
83 def test_2702_deq_mode_remove_no_data(self):
84 "2702 - test dequeuing with DEQ_REMOVE_NODATA option"
85 self.__clear_books_queue()
86 books_type = self.connection.gettype(self.book_type_name)
87 book = books_type.newobject()
88 title, authors, price = self.book_data[1]
89 book.TITLE = title
90 book.AUTHORS = authors
91 book.PRICE = price
92 options = self.connection.enqoptions()
93 props = self.connection.msgproperties()
94 self.connection.enq(self.book_queue_name, options, props, book)
95 options = self.connection.deqoptions()
96 options.navigation = oracledb.DEQ_FIRST_MSG
97 options.wait = oracledb.DEQ_NO_WAIT
98 options.mode = oracledb.DEQ_REMOVE_NODATA
99 book = books_type.newobject()
100 message_id = self.connection.deq(self.book_queue_name, options, props,
101 book)
102 self.connection.commit()
103 self.assertTrue(message_id is not None)
104 self.assertEqual(book.TITLE, "")
105
106 def test_2703_deq_options(self):
107 "2703 - test getting/setting dequeue options attributes"
108 options = self.connection.deqoptions()
109 self.__verify_attr(options, "condition", "TEST_CONDITION")
110 self.__verify_attr(options, "consumername", "TEST_CONSUMERNAME")
111 self.__verify_attr(options, "correlation", "TEST_CORRELATION")
112 self.__verify_attr(options, "mode", oracledb.DEQ_LOCKED)
113 self.__verify_attr(options, "navigation",
114 oracledb.DEQ_NEXT_TRANSACTION)
115 self.__verify_attr(options, "transformation", "TEST_TRANSFORMATION")
116 self.__verify_attr(options, "visibility", oracledb.ENQ_IMMEDIATE)
117 self.__verify_attr(options, "wait", 1287)
118 self.__verify_attr(options, "msgid", b'mID')
119
120 def test_2704_deq_with_wait(self):
121 "2704 - test waiting for dequeue"
122 self.__clear_books_queue()
123 results = []
124 thread = threading.Thread(target = self.__deq_in_thread,
125 args = (results,))
126 thread.start()
127 books_type = self.connection.gettype(self.book_type_name)
128 book = books_type.newobject()
129 title, authors, price = self.book_data[0]
130 book.TITLE = title
131 book.AUTHORS = authors
132 book.PRICE = price
133 options = self.connection.enqoptions()
134 props = self.connection.msgproperties()
135 self.connection.enq(self.book_queue_name, options, props, book)
136 self.connection.commit()
137 thread.join()
138 self.assertEqual(results, [(title, authors, price)])
139
140 def test_2705_enq_options(self):
141 "2705 - test getting/setting enqueue options attributes"
142 options = self.connection.enqoptions()
143 self.__verify_attr(options, "visibility", oracledb.ENQ_IMMEDIATE)
144
145 def test_2706_errors_for_invalid_values(self):
146 "2706 - test errors for invalid values for options"
147 books_type = self.connection.gettype(self.book_type_name)
148 book = books_type.newobject()
149 options = self.connection.enqoptions()
150 props = self.connection.msgproperties()
151 self.assertRaises(TypeError, self.connection.deq, self.book_queue_name,
152 options, props, book)
153 options = self.connection.deqoptions()
154 self.assertRaises(TypeError, self.connection.enq, self.book_queue_name,
155 options, props, book)
156
157 def test_2707_msg_props(self):
158 "2707 - test getting/setting message properties attributes"
159 props = self.connection.msgproperties()
160 self.__verify_attr(props, "correlation", "TEST_CORRELATION")
161 self.__verify_attr(props, "delay", 60)
162 self.__verify_attr(props, "exceptionq", "TEST_EXCEPTIONQ")
163 self.__verify_attr(props, "expiration", 30)
164 self.assertEqual(props.attempts, 0)
165 self.__verify_attr(props, "priority", 1)
166 self.__verify_attr(props, "msgid", b'mID')
167 self.assertEqual(props.state, oracledb.MSG_READY)
168 self.assertEqual(props.deliverymode, 0)
169
170 def test_2708_visibility_mode_commit(self):
171 "2708 - test enqueue visibility option - ENQ_ON_COMMIT"
172 self.__clear_books_queue()
173 books_type = self.connection.gettype(self.book_type_name)
174 book = books_type.newobject()
175 book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0]
176 enq_options = self.connection.enqoptions()
177 enq_options.visibility = oracledb.ENQ_ON_COMMIT
178 props = self.connection.msgproperties()
179 self.connection.enq(self.book_queue_name, enq_options, props, book)
180
181 other_connection = test_env.get_connection()
182 deq_options = other_connection.deqoptions()
183 deq_options.navigation = oracledb.DEQ_FIRST_MSG
184 deq_options.wait = oracledb.DEQ_NO_WAIT
185 books_type = other_connection.gettype(self.book_type_name)
186 book = books_type.newobject()
187 props = other_connection.msgproperties()
188 message_id = other_connection.deq(self.book_queue_name, deq_options,
189 props, book)
190 self.assertTrue(message_id is None)
191 self.connection.commit()
192 message_id = other_connection.deq(self.book_queue_name, deq_options,
193 props, book)
194 self.assertTrue(message_id is not None)
195
196 def test_2709_visibility_mode_immediate(self):
197 "2709 - test enqueue visibility option - ENQ_IMMEDIATE"
198 self.__clear_books_queue()
199 books_type = self.connection.gettype(self.book_type_name)
200 book = books_type.newobject()
201 book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0]
202 enq_options = self.connection.enqoptions()
203 enq_options.visibility = oracledb.ENQ_IMMEDIATE
204 props = self.connection.msgproperties()
205 self.connection.enq(self.book_queue_name, enq_options, props, book)
206
207 other_connection = test_env.get_connection()
208 deq_options = other_connection.deqoptions()
209 deq_options.navigation = oracledb.DEQ_FIRST_MSG
210 deq_options.visibility = oracledb.DEQ_ON_COMMIT
211 deq_options.wait = oracledb.DEQ_NO_WAIT
212 books_type = other_connection.gettype(self.book_type_name)
213 book = books_type.newobject()
214 props = other_connection.msgproperties()
215 other_connection.deq(self.book_queue_name, deq_options, props, book)
216 results = (book.TITLE, book.AUTHORS, book.PRICE)
217 other_connection.commit()
218 self.assertEqual(results, self.book_data[0])
219
220 def test_2710_delivery_mode_same_buffered(self):
221 "2710 - test enqueue/dequeue delivery modes identical - buffered"
222 self.__clear_books_queue()
223 books_type = self.connection.gettype(self.book_type_name)
224 book = books_type.newobject()
225 book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0]
226 enq_options = self.connection.enqoptions()
227 enq_options.deliverymode = oracledb.MSG_BUFFERED
228 enq_options.visibility = oracledb.ENQ_IMMEDIATE
229 props = self.connection.msgproperties()
230 self.connection.enq(self.book_queue_name, enq_options, props, book)
231
232 other_connection = test_env.get_connection()
233 deq_options = other_connection.deqoptions()
234 deq_options.deliverymode = oracledb.MSG_BUFFERED
235 deq_options.navigation = oracledb.DEQ_FIRST_MSG
236 deq_options.visibility = oracledb.DEQ_IMMEDIATE
237 deq_options.wait = oracledb.DEQ_NO_WAIT
238 books_type = other_connection.gettype(self.book_type_name)
239 book = books_type.newobject()
240 props = other_connection.msgproperties()
241 other_connection.deq(self.book_queue_name, deq_options, props, book)
242 results = (book.TITLE, book.AUTHORS, book.PRICE)
243 other_connection.commit()
244 self.assertEqual(results, self.book_data[0])
245
246 def test_2711_delivery_mode_same_persistent(self):
247 "2711 - test enqueue/dequeue delivery modes identical - persistent"
248 self.__clear_books_queue()
249 books_type = self.connection.gettype(self.book_type_name)
250 book = books_type.newobject()
251 book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0]
252 enq_options = self.connection.enqoptions()
253 enq_options.deliverymode = oracledb.MSG_PERSISTENT
254 enq_options.visibility = oracledb.ENQ_IMMEDIATE
255 props = self.connection.msgproperties()
256 self.connection.enq(self.book_queue_name, enq_options, props, book)
257
258 other_connection = test_env.get_connection()
259 deq_options = other_connection.deqoptions()
260 deq_options.deliverymode = oracledb.MSG_PERSISTENT
261 deq_options.navigation = oracledb.DEQ_FIRST_MSG
262 deq_options.visibility = oracledb.DEQ_IMMEDIATE
263 deq_options.wait = oracledb.DEQ_NO_WAIT
264 books_type = other_connection.gettype(self.book_type_name)
265 book = books_type.newobject()
266 props = other_connection.msgproperties()
267 other_connection.deq(self.book_queue_name, deq_options, props, book)
268 results = (book.TITLE, book.AUTHORS, book.PRICE)
269 other_connection.commit()
270 self.assertEqual(results, self.book_data[0])
271
272 def test_2712_delivery_mode_same_persistent_buffered(self):
273 "2712 - test enqueue/dequeue delivery modes the same"
274 self.__clear_books_queue()
275 books_type = self.connection.gettype(self.book_type_name)
276 book = books_type.newobject()
277 book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0]
278 enq_options = self.connection.enqoptions()
279 enq_options.deliverymode = oracledb.MSG_PERSISTENT_OR_BUFFERED
280 enq_options.visibility = oracledb.ENQ_IMMEDIATE
281 props = self.connection.msgproperties()
282 self.connection.enq(self.book_queue_name, enq_options, props, book)
283
284 other_connection = test_env.get_connection()
285 deq_options = other_connection.deqoptions()
286 deq_options.deliverymode = oracledb.MSG_PERSISTENT_OR_BUFFERED
287 deq_options.navigation = oracledb.DEQ_FIRST_MSG
288 deq_options.visibility = oracledb.DEQ_IMMEDIATE
289 deq_options.wait = oracledb.DEQ_NO_WAIT
290 books_type = other_connection.gettype(self.book_type_name)
291 book = books_type.newobject()
292 props = other_connection.msgproperties()
293 other_connection.deq(self.book_queue_name, deq_options, props, book)
294 results = (book.TITLE, book.AUTHORS, book.PRICE)
295 other_connection.commit()
296 self.assertEqual(results, self.book_data[0])
297
298 def test_2713_delivery_mode_different(self):
299 "2713 - test enqueue/dequeue delivery modes different"
300 self.__clear_books_queue()
301 books_type = self.connection.gettype(self.book_type_name)
302 book = books_type.newobject()
303 book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0]
304 enq_options = self.connection.enqoptions()
305 enq_options.deliverymode = oracledb.MSG_BUFFERED
306 enq_options.visibility = oracledb.ENQ_IMMEDIATE
307 props = self.connection.msgproperties()
308 self.connection.enq(self.book_queue_name, enq_options, props, book)
309
310 other_connection = test_env.get_connection()
311 deq_options = other_connection.deqoptions()
312 deq_options.deliverymode = oracledb.MSG_PERSISTENT
313 deq_options.navigation = oracledb.DEQ_FIRST_MSG
314 deq_options.visibility = oracledb.DEQ_IMMEDIATE
315 deq_options.wait = oracledb.DEQ_NO_WAIT
316 books_type = other_connection.gettype(self.book_type_name)
317 book = books_type.newobject()
318 props = other_connection.msgproperties()
319 message_id = other_connection.deq(self.book_queue_name, deq_options,
320 props, book)
321 self.assertTrue(message_id is None)
322
323 def test_2714_dequeue_transformation(self):
324 "2714 - test dequeue transformation"
325 self.__clear_books_queue()
326 books_type = self.connection.gettype(self.book_type_name)
327 book = books_type.newobject()
328 book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0]
329 expectedPrice = book.PRICE + 10
330 enq_options = self.connection.enqoptions()
331 props = self.connection.msgproperties()
332 self.connection.enq(self.book_queue_name, enq_options, props, book)
333 self.connection.commit()
334
335 other_connection = test_env.get_connection()
336 deq_options = other_connection.deqoptions()
337 deq_options.navigation = oracledb.DEQ_FIRST_MSG
338 deq_options.visibility = oracledb.DEQ_IMMEDIATE
339 deq_options.transformation = "%s.transform2" % self.connection.username
340 deq_options.wait = oracledb.DEQ_NO_WAIT
341 books_type = other_connection.gettype(self.book_type_name)
342 book = books_type.newobject()
343 props = other_connection.msgproperties()
344 other_connection.deq(self.book_queue_name, deq_options, props, book)
345 otherPrice = book.PRICE
346 self.assertEqual(otherPrice, expectedPrice)
347
348 def test_2715_enqueue_transformation(self):
349 "2715 - test enqueue transformation"
350 self.__clear_books_queue()
351 books_type = self.connection.gettype(self.book_type_name)
352 book = books_type.newobject()
353 book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0]
354 expectedPrice = book.PRICE + 5
355 enq_options = self.connection.enqoptions()
356 enq_options.transformation = "%s.transform1" % self.connection.username
357 props = self.connection.msgproperties()
358 self.connection.enq(self.book_queue_name, enq_options, props, book)
359 self.connection.commit()
360
361 other_connection = test_env.get_connection()
362 deq_options = other_connection.deqoptions()
363 deq_options.navigation = oracledb.DEQ_FIRST_MSG
364 deq_options.visibility = oracledb.DEQ_IMMEDIATE
365 deq_options.wait = oracledb.DEQ_NO_WAIT
366 books_type = other_connection.gettype(self.book_type_name)
367 book = books_type.newobject()
368 props = other_connection.msgproperties()
369 other_connection.deq(self.book_queue_name, deq_options, props, book)
370 otherPrice = book.PRICE
371 self.assertEqual(otherPrice, expectedPrice)
372
373 if __name__ == "__main__":
374 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 2800 - Module for testing AQ Bulk enqueue/dequeue
6 """
7
8 import test_env
9
10 import cx_Oracle as oracledb
11 import decimal
12 import threading
13
14 RAW_QUEUE_NAME = "TEST_RAW_QUEUE"
15 RAW_PAYLOAD_DATA = [
16 "The first message",
17 "The second message",
18 "The third message",
19 "The fourth message",
20 "The fifth message",
21 "The sixth message",
22 "The seventh message",
23 "The eighth message",
24 "The ninth message",
25 "The tenth message",
26 "The eleventh message",
27 "The twelfth and final message"
28 ]
29
30 class TestCase(test_env.BaseTestCase):
31
32 def __deq_in_thread(self, results):
33 connection = test_env.get_connection(threaded=True)
34 queue = connection.queue(RAW_QUEUE_NAME)
35 queue.deqOptions.wait = 10
36 queue.deqOptions.navigation = oracledb.DEQ_FIRST_MSG
37 while len(results) < len(RAW_PAYLOAD_DATA):
38 messages = queue.deqMany(5)
39 if not messages:
40 break
41 for m in messages:
42 results.append(m.payload.decode(connection.encoding))
43 connection.commit()
44
45 def __get_and_clear_raw_queue(self):
46 queue = self.connection.queue(RAW_QUEUE_NAME)
47 queue.deqOptions.wait = oracledb.DEQ_NO_WAIT
48 queue.deqOptions.navigation = oracledb.DEQ_FIRST_MSG
49 while queue.deqOne():
50 pass
51 self.connection.commit()
52 return queue
53
54 def test_2800_enq_and_deq(self):
55 "2800 - test bulk enqueue and dequeue"
56 queue = self.__get_and_clear_raw_queue()
57 messages = [self.connection.msgproperties(payload=d) \
58 for d in RAW_PAYLOAD_DATA]
59 queue.enqMany(messages)
60 messages = queue.deqMany(len(RAW_PAYLOAD_DATA))
61 data = [m.payload.decode(self.connection.encoding) for m in messages]
62 self.connection.commit()
63 self.assertEqual(data, RAW_PAYLOAD_DATA)
64
65 def test_2801_dequeue_empty(self):
66 "2801 - test empty bulk dequeue"
67 queue = self.__get_and_clear_raw_queue()
68 messages = queue.deqMany(5)
69 self.connection.commit()
70 self.assertEqual(messages, [])
71
72 def test_2802_deq_with_wait(self):
73 "2802 - test bulk dequeue with wait"
74 queue = self.__get_and_clear_raw_queue()
75 results = []
76 thread = threading.Thread(target=self.__deq_in_thread, args=(results,))
77 thread.start()
78 messages = [self.connection.msgproperties(payload=d) \
79 for d in RAW_PAYLOAD_DATA]
80 queue.enqOptions.visibility = oracledb.ENQ_IMMEDIATE
81 queue.enqMany(messages)
82 thread.join()
83 self.assertEqual(results, RAW_PAYLOAD_DATA)
84
85 def test_2803_enq_and_deq_multiple_times(self):
86 "2803 - test enqueue and dequeue multiple times"
87 queue = self.__get_and_clear_raw_queue()
88 data_to_enqueue = RAW_PAYLOAD_DATA
89 for num in (2, 6, 4):
90 messages = [self.connection.msgproperties(payload=d) \
91 for d in data_to_enqueue[:num]]
92 data_to_enqueue = data_to_enqueue[num:]
93 queue.enqMany(messages)
94 self.connection.commit()
95 all_data = []
96 for num in (3, 5, 10):
97 messages = queue.deqMany(num)
98 all_data.extend(m.payload.decode(self.connection.encoding) \
99 for m in messages)
100 self.connection.commit()
101 self.assertEqual(all_data, RAW_PAYLOAD_DATA)
102
103 def test_2804_enq_and_deq_visibility(self):
104 "2804 - test visibility option for enqueue and dequeue"
105 queue = self.__get_and_clear_raw_queue()
106
107 # first test with ENQ_ON_COMMIT (commit required)
108 queue.enqOptions.visibility = oracledb.ENQ_ON_COMMIT
109 props1 = self.connection.msgproperties(payload="A first message")
110 props2 = self.connection.msgproperties(payload="A second message")
111 queue.enqMany([props1, props2])
112 other_connection = test_env.get_connection()
113 other_queue = other_connection.queue(RAW_QUEUE_NAME)
114 other_queue.deqOptions.wait = oracledb.DEQ_NO_WAIT
115 other_queue.deqOptions.visibility = oracledb.DEQ_ON_COMMIT
116 messages = other_queue.deqMany(5)
117 self.assertEqual(len(messages), 0)
118 self.connection.commit()
119 messages = other_queue.deqMany(5)
120 self.assertEqual(len(messages), 2)
121 other_connection.rollback()
122
123 # second test with ENQ_IMMEDIATE (no commit required)
124 queue.enqOptions.visibility = oracledb.ENQ_IMMEDIATE
125 other_queue.deqOptions.visibility = oracledb.DEQ_IMMEDIATE
126 queue.enqMany([props1, props2])
127 messages = other_queue.deqMany(5)
128 self.assertEqual(len(messages), 4)
129 other_connection.rollback()
130 messages = other_queue.deqMany(5)
131 self.assertEqual(len(messages), 0)
132
133 if __name__ == "__main__":
134 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 2900 - Module for testing Rowids
6 """
7
8 import test_env
9
10 import cx_Oracle as oracledb
11
12 class TestCase(test_env.BaseTestCase):
13
14 def __test_select_rowids(self, table_name):
15 self.cursor.execute("select rowid, IntCol from %s""" % table_name)
16 rowid_dict = dict(self.cursor)
17 sql = "select IntCol from %s where rowid = :val" % table_name
18 for rowid, int_val in rowid_dict.items():
19 self.cursor.execute(sql, val = rowid)
20 rows = self.cursor.fetchall()
21 self.assertEqual(len(rows), 1)
22 self.assertEqual(rows[0][0], int_val)
23
24 def test_2900_select_rowids_regular(self):
25 "2900 - test selecting all rowids from a regular table"
26 self.__test_select_rowids("TestNumbers")
27
28 def test_2901_select_rowids_index_organised(self):
29 "2901 - test selecting all rowids from an index organised table"
30 self.__test_select_rowids("TestUniversalRowids")
31
32 def test_2902_insert_invalid_rowid(self):
33 "2902 - test inserting an invalid rowid"
34 sql = "insert into TestRowids (IntCol, RowidCol) values (1, :rid)"
35 self.assertRaises(oracledb.DatabaseError, self.cursor.execute, sql,
36 rid=12345)
37 self.assertRaises(oracledb.DatabaseError, self.cursor.execute, sql,
38 rid="523lkhlf")
39
40 def test_2903_insert_rowids(self):
41 "2903 - test inserting rowids and verify they are inserted correctly"
42 self.cursor.execute("select IntCol, rowid from TestNumbers")
43 rows = self.cursor.fetchall()
44 self.cursor.execute("truncate table TestRowids")
45 self.cursor.executemany("""
46 insert into TestRowids
47 (IntCol, RowidCol)
48 values (:1, :2)""", rows)
49 self.connection.commit()
50 self.cursor.execute("select IntCol, RowidCol from TestRowids")
51 rows = self.cursor.fetchall()
52 sql = "select IntCol from TestNumbers where rowid = :val"
53 for int_val, rowid in rows:
54 self.cursor.execute(sql, val = rowid)
55 rows = self.cursor.fetchall()
56 self.assertEqual(len(rows), 1)
57 self.assertEqual(rows[0][0], int_val)
58
59 if __name__ == "__main__":
60 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 3000 - Module for testing subscriptions
6 """
7
8 import test_env
9
10 import cx_Oracle as oracledb
11 import threading
12
13 class SubscriptionData(object):
14
15 def __init__(self, num_messages_expected):
16 self.condition = threading.Condition()
17 self.num_messages_expected = num_messages_expected
18 self.num_messages_received = 0
19 self.table_operations = []
20 self.row_operations = []
21 self.rowids = []
22
23 def CallbackHandler(self, message):
24 if message.type != oracledb.EVENT_DEREG:
25 table, = message.tables
26 self.table_operations.append(table.operation)
27 for row in table.rows:
28 self.row_operations.append(row.operation)
29 self.rowids.append(row.rowid)
30 self.num_messages_received += 1
31 if message.type == oracledb.EVENT_DEREG or \
32 self.num_messages_received == self.num_messages_expected:
33 self.condition.acquire()
34 self.condition.notify()
35 self.condition.release()
36
37
38 class TestCase(test_env.BaseTestCase):
39
40 def test_3000_subscription(self):
41 "3000 - test Subscription for insert, update, delete and truncate"
42
43 # skip if running on the Oracle Cloud, which does not support
44 # subscriptions currently
45 if self.is_on_oracle_cloud():
46 message = "Oracle Cloud does not support subscriptions currently"
47 self.skipTest(message)
48
49 # truncate table in order to run test in known state
50 self.cursor.execute("truncate table TestTempTable")
51
52 # expected values
53 table_operations = [
54 oracledb.OPCODE_INSERT,
55 oracledb.OPCODE_UPDATE,
56 oracledb.OPCODE_INSERT,
57 oracledb.OPCODE_DELETE,
58 oracledb.OPCODE_ALTER | oracledb.OPCODE_ALLROWS
59 ]
60 row_operations = [
61 oracledb.OPCODE_INSERT,
62 oracledb.OPCODE_UPDATE,
63 oracledb.OPCODE_INSERT,
64 oracledb.OPCODE_DELETE
65 ]
66 rowids = []
67
68 # set up subscription
69 data = SubscriptionData(5)
70 connection = test_env.get_connection(threaded=True, events=True)
71 sub = connection.subscribe(callback=data.CallbackHandler,
72 timeout=10, qos=oracledb.SUBSCR_QOS_ROWIDS)
73 sub.registerquery("select * from TestTempTable")
74 connection.autocommit = True
75 cursor = connection.cursor()
76
77 # insert statement
78 cursor.execute("""
79 insert into TestTempTable (IntCol, StringCol)
80 values (1, 'test')""")
81 cursor.execute("select rowid from TestTempTable where IntCol = 1")
82 rowids.extend(r for r, in cursor)
83
84 # update statement
85 cursor.execute("""
86 update TestTempTable set
87 StringCol = 'update'
88 where IntCol = 1""")
89 cursor.execute("select rowid from TestTempTable where IntCol = 1")
90 rowids.extend(r for r, in cursor)
91
92 # second insert statement
93 cursor.execute("""
94 insert into TestTempTable (IntCol, StringCol)
95 values (2, 'test2')""")
96 cursor.execute("select rowid from TestTempTable where IntCol = 2")
97 rowids.extend(r for r, in cursor)
98
99 # delete statement
100 cursor.execute("delete TestTempTable where IntCol = 2")
101 rowids.append(rowids[-1])
102
103 # truncate table
104 cursor.execute("truncate table TestTempTable")
105
106 # wait for all messages to be sent
107 data.condition.acquire()
108 data.condition.wait(10)
109
110 # verify the correct messages were sent
111 self.assertEqual(data.table_operations, table_operations)
112 self.assertEqual(data.row_operations, row_operations)
113 self.assertEqual(data.rowids, rowids)
114
115 # test string format of subscription object is as expected
116 fmt = "<cx_Oracle.Subscription on <cx_Oracle.Connection to %s@%s>>"
117 expected = fmt % \
118 (test_env.get_main_user(), test_env.get_connect_string())
119 self.assertEqual(str(sub), expected)
120
121 if __name__ == "__main__":
122 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 3100 - Module for testing boolean variables
11 """
12
13 import unittest
14 import test_env
15
16 import cx_Oracle as oracledb
17
18 @unittest.skipUnless(test_env.get_client_version() >= (12, 1),
19 "unsupported client")
20 class TestCase(test_env.BaseTestCase):
21
22 def __test_bind_value_as_boolean(self, value):
23 expected_result = str(bool(value)).upper()
24 var = self.cursor.var(bool)
25 var.setvalue(0, value)
26 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
27 (var,))
28 self.assertEqual(result, expected_result)
29
30 def test_3100_bind_false(self):
31 "3100 - test binding in a False value"
32 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
33 (False,))
34 self.assertEqual(result, "FALSE")
35
36 def test_3101_bind_float_as_boolean(self):
37 "3101 - test binding in a float as a boolean"
38 self.__test_bind_value_as_boolean(0.0)
39 self.__test_bind_value_as_boolean(1.0)
40
41 def test_3102_bind_integer_as_boolean(self):
42 "3102 - test binding in an integer as a boolean"
43 self.__test_bind_value_as_boolean(0)
44 self.__test_bind_value_as_boolean(1)
45
46 def test_3103_bind_null(self):
47 "3103 - test binding in a null value"
48 self.cursor.setinputsizes(None, bool)
49 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
50 (None,))
51 self.assertEqual(result, "NULL")
52
53 def test_3104_bind_out_false(self):
54 "3104 - test binding out a boolean value (False)"
55 result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10",
56 oracledb.DB_TYPE_BOOLEAN, (15,))
57 self.assertEqual(result, False)
58
59 def test_3105_bind_out_true(self):
60 "3105 - test binding out a boolean value (True)"
61 result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10", bool,
62 (5,))
63 self.assertEqual(result, True)
64
65 def test_3106_bind_string_as_boolean(self):
66 "3106 - test binding in a string as a boolean"
67 self.__test_bind_value_as_boolean("")
68 self.__test_bind_value_as_boolean("0")
69
70 def test_3107_bind_true(self):
71 "3107 - test binding in a True value"
72 result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
73 (True,))
74 self.assertEqual(result, "TRUE")
75
76 if __name__ == "__main__":
77 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """
10 3200 - Module for testing features introduced in 12.1
11 """
12
13 import test_env
14
15 import cx_Oracle as oracledb
16 import datetime
17 import unittest
18
19 @unittest.skipUnless(test_env.get_client_version() >= (12, 1),
20 "unsupported client")
21 class TestCase(test_env.BaseTestCase):
22
23 def test_3200_array_dml_row_counts_off(self):
24 "3200 - test executing with arraydmlrowcounts mode disabled"
25 self.cursor.execute("truncate table TestArrayDML")
26 rows = [(1, "First"), (2, "Second")]
27 sql = "insert into TestArrayDML (IntCol,StringCol) values (:1,:2)"
28 self.cursor.executemany(sql, rows, arraydmlrowcounts=False)
29 self.assertRaises(oracledb.DatabaseError,
30 self.cursor.getarraydmlrowcounts)
31 rows = [(3, "Third"), (4, "Fourth")]
32 self.cursor.executemany(sql, rows)
33 self.assertRaises(oracledb.DatabaseError,
34 self.cursor.getarraydmlrowcounts)
35
36 def test_3201_array_dml_row_counts_on(self):
37 "3201 - test executing with arraydmlrowcounts mode enabled"
38 self.cursor.execute("truncate table TestArrayDML")
39 rows = [
40 (1, "First", 100),
41 (2, "Second", 200),
42 (3, "Third", 300),
43 (4, "Fourth", 300),
44 (5, "Fifth", 300)
45 ]
46 sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \
47 "values (:1,:2,:3)"
48 self.cursor.executemany(sql, rows, arraydmlrowcounts=True)
49 self.connection.commit()
50 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 1, 1, 1])
51 self.cursor.execute("select count(*) from TestArrayDML")
52 count, = self.cursor.fetchone()
53 self.assertEqual(count, len(rows))
54
55 def test_3202_bind_plsql_boolean_collection_in(self):
56 "3202 - test binding a boolean collection (in)"
57 type_obj = self.connection.gettype("PKG_TESTBOOLEANS.UDT_BOOLEANLIST")
58 obj = type_obj.newobject()
59 obj.setelement(1, True)
60 obj.extend([True, False, True, True, False, True])
61 result = self.cursor.callfunc("pkg_TestBooleans.TestInArrays", int,
62 (obj,))
63 self.assertEqual(result, 5)
64
65 def test_3203_bind_plsql_boolean_collection_out(self):
66 "3203 - test binding a boolean collection (out)"
67 type_obj = self.connection.gettype("PKG_TESTBOOLEANS.UDT_BOOLEANLIST")
68 obj = type_obj.newobject()
69 self.cursor.callproc("pkg_TestBooleans.TestOutArrays", (6, obj))
70 self.assertEqual(obj.aslist(), [True, False, True, False, True, False])
71
72 def test_3204_bind_plql_date_collection_in(self):
73 "3204 - test binding a PL/SQL date collection (in)"
74 type_obj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST")
75 obj = type_obj.newobject()
76 obj.setelement(1, datetime.datetime(2016, 2, 5))
77 obj.append(datetime.datetime(2016, 2, 8, 12, 15, 30))
78 obj.append(datetime.datetime(2016, 2, 12, 5, 44, 30))
79 result = self.cursor.callfunc("pkg_TestDateArrays.TestInArrays",
80 oracledb.NUMBER,
81 (2, datetime.datetime(2016, 2, 1), obj))
82 self.assertEqual(result, 24.75)
83
84 def test_3205_bind_plqsl_date_collection_in_out(self):
85 "3205 - test binding a PL/SQL date collection (in/out)"
86 type_obj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST")
87 obj = type_obj.newobject()
88 obj.setelement(1, datetime.datetime(2016, 1, 1))
89 obj.append(datetime.datetime(2016, 1, 7))
90 obj.append(datetime.datetime(2016, 1, 13))
91 obj.append(datetime.datetime(2016, 1, 19))
92 self.cursor.callproc("pkg_TestDateArrays.TestInOutArrays", (4, obj))
93 expected_values = [
94 datetime.datetime(2016, 1, 8),
95 datetime.datetime(2016, 1, 14),
96 datetime.datetime(2016, 1, 20),
97 datetime.datetime(2016, 1, 26)
98 ]
99 self.assertEqual(obj.aslist(), expected_values)
100
101 def test_3206_bind_plsql_date_collection_out(self):
102 "3206 - test binding a PL/SQL date collection (out)"
103 type_obj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST")
104 obj = type_obj.newobject()
105 self.cursor.callproc("pkg_TestDateArrays.TestOutArrays", (3, obj))
106 expected_values = [
107 datetime.datetime(2002, 12, 13, 4, 48),
108 datetime.datetime(2002, 12, 14, 9, 36),
109 datetime.datetime(2002, 12, 15, 14, 24)
110 ]
111 self.assertEqual(obj.aslist(), expected_values)
112
113 def test_3207_bind_plsql_number_collection_in(self):
114 "3207 - test binding a PL/SQL number collection (in)"
115 type_name = "PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST"
116 type_obj = self.connection.gettype(type_name)
117 obj = type_obj.newobject()
118 obj.setelement(1, 10)
119 obj.extend([20, 30, 40, 50])
120 result = self.cursor.callfunc("pkg_TestNumberArrays.TestInArrays", int,
121 (5, obj))
122 self.assertEqual(result, 155)
123
124 def test_3208_bind_plsql_number_collection_in_out(self):
125 "3208 - test binding a PL/SQL number collection (in/out)"
126 type_name = "PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST"
127 type_obj = self.connection.gettype(type_name)
128 obj = type_obj.newobject()
129 obj.setelement(1, 5)
130 obj.extend([8, 3, 2])
131 self.cursor.callproc("pkg_TestNumberArrays.TestInOutArrays", (4, obj))
132 self.assertEqual(obj.aslist(), [50, 80, 30, 20])
133
134 def test_3209_bind_plsql_number_collection_out(self):
135 "3209 - test binding a PL/SQL number collection (out)"
136 type_name = "PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST"
137 type_obj = self.connection.gettype(type_name)
138 obj = type_obj.newobject()
139 self.cursor.callproc("pkg_TestNumberArrays.TestOutArrays", (3, obj))
140 self.assertEqual(obj.aslist(), [100, 200, 300])
141
142 def test_3210_bind_plsql_record_array(self):
143 "3210 - test binding an array of PL/SQL records (in)"
144 rec_type = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD")
145 array_type = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORDARRAY")
146 array_obj = array_type.newobject()
147 for i in range(3):
148 obj = rec_type.newobject()
149 obj.NUMBERVALUE = i + 1
150 obj.STRINGVALUE = "String in record #%d" % (i + 1)
151 obj.DATEVALUE = datetime.datetime(2017, i + 1, 1)
152 obj.TIMESTAMPVALUE = datetime.datetime(2017, 1, i + 1)
153 obj.BOOLEANVALUE = (i % 2) == 1
154 obj.PLSINTEGERVALUE = i * 5
155 obj.BINARYINTEGERVALUE = i * 2
156 array_obj.append(obj)
157 result = self.cursor.callfunc("pkg_TestRecords.TestInArrays", str,
158 (array_obj,))
159 self.assertEqual(result,
160 "udt_Record(1, 'String in record #1', " \
161 "to_date('2017-01-01', 'YYYY-MM-DD'), " \
162 "to_timestamp('2017-01-01 00:00:00', " \
163 "'YYYY-MM-DD HH24:MI:SS'), false, 0, 0); " \
164 "udt_Record(2, 'String in record #2', " \
165 "to_date('2017-02-01', 'YYYY-MM-DD'), " \
166 "to_timestamp('2017-01-02 00:00:00', " \
167 "'YYYY-MM-DD HH24:MI:SS'), true, 5, 2); " \
168 "udt_Record(3, 'String in record #3', " \
169 "to_date('2017-03-01', 'YYYY-MM-DD'), " \
170 "to_timestamp('2017-01-03 00:00:00', " \
171 "'YYYY-MM-DD HH24:MI:SS'), false, 10, 4)")
172
173 def test_3211_bind_plsql_record_in(self):
174 "3211 - test binding a PL/SQL record (in)"
175 type_obj = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD")
176 obj = type_obj.newobject()
177 obj.NUMBERVALUE = 18
178 obj.STRINGVALUE = "A string in a record"
179 obj.DATEVALUE = datetime.datetime(2016, 2, 15)
180 obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 12, 14, 25, 36)
181 obj.BOOLEANVALUE = False
182 obj.PLSINTEGERVALUE = 21
183 obj.BINARYINTEGERVALUE = 5
184 result = self.cursor.callfunc("pkg_TestRecords.GetStringRep", str,
185 (obj,))
186 self.assertEqual(result,
187 "udt_Record(18, 'A string in a record', " \
188 "to_date('2016-02-15', 'YYYY-MM-DD'), " \
189 "to_timestamp('2016-02-12 14:25:36', " \
190 "'YYYY-MM-DD HH24:MI:SS'), false, 21, 5)")
191
192 def test_3212_bind_plsql_record_out(self):
193 "3212 - test binding a PL/SQL record (out)"
194 type_obj = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD")
195 obj = type_obj.newobject()
196 obj.NUMBERVALUE = 5
197 obj.STRINGVALUE = "Test value"
198 obj.DATEVALUE = datetime.datetime.today()
199 obj.TIMESTAMPVALUE = datetime.datetime.today()
200 obj.BOOLEANVALUE = False
201 obj.PLSINTEGERVALUE = 23
202 obj.BINARYINTEGERVALUE = 9
203 self.cursor.callproc("pkg_TestRecords.TestOut", (obj,))
204 self.assertEqual(obj.NUMBERVALUE, 25)
205 self.assertEqual(obj.STRINGVALUE, "String in record")
206 self.assertEqual(obj.DATEVALUE, datetime.datetime(2016, 2, 16))
207 self.assertEqual(obj.TIMESTAMPVALUE,
208 datetime.datetime(2016, 2, 16, 18, 23, 55))
209 self.assertEqual(obj.BOOLEANVALUE, True)
210 self.assertEqual(obj.PLSINTEGERVALUE, 45)
211 self.assertEqual(obj.BINARYINTEGERVALUE, 10)
212
213 def test_3213_bind_plsql_string_collection_in(self):
214 "3213 - test binding a PL/SQL string collection (in)"
215 type_name = "PKG_TESTSTRINGARRAYS.UDT_STRINGLIST"
216 type_obj = self.connection.gettype(type_name)
217 obj = type_obj.newobject()
218 obj.setelement(1, "First element")
219 obj.setelement(2, "Second element")
220 obj.setelement(3, "Third element")
221 result = self.cursor.callfunc("pkg_TestStringArrays.TestInArrays", int,
222 (5, obj))
223 self.assertEqual(result, 45)
224
225 def test_3214_bind_plsql_string_collection_in_out(self):
226 "3214 - test binding a PL/SQL string collection (in/out)"
227 type_name = "PKG_TESTSTRINGARRAYS.UDT_STRINGLIST"
228 type_obj = self.connection.gettype(type_name)
229 obj = type_obj.newobject()
230 obj.setelement(1, "The first element")
231 obj.append("The second element")
232 obj.append("The third and final element")
233 self.cursor.callproc("pkg_TestStringArrays.TestInOutArrays", (3, obj))
234 expected_values = [
235 'Converted element # 1 originally had length 17',
236 'Converted element # 2 originally had length 18',
237 'Converted element # 3 originally had length 27'
238 ]
239 self.assertEqual(obj.aslist(), expected_values)
240
241 def test_3215_bind_plsql_string_collection_out(self):
242 "3215 - test binding a PL/SQL string collection (out)"
243 type_name = "PKG_TESTSTRINGARRAYS.UDT_STRINGLIST"
244 type_obj = self.connection.gettype(type_name)
245 obj = type_obj.newobject()
246 self.cursor.callproc("pkg_TestStringArrays.TestOutArrays", (4, obj))
247 expected_values = [
248 'Test out element # 1',
249 'Test out element # 2',
250 'Test out element # 3',
251 'Test out element # 4'
252 ]
253 self.assertEqual(obj.aslist(), expected_values)
254
255 def test_3216_bind_plsql_string_collection_out_with_holes(self):
256 "3216 - test binding a PL/SQL string collection (out with holes)"
257 type_name = "PKG_TESTSTRINGARRAYS.UDT_STRINGLIST"
258 type_obj = self.connection.gettype(type_name)
259 obj = type_obj.newobject()
260 self.cursor.callproc("pkg_TestStringArrays.TestIndexBy", (obj,))
261 self.assertEqual(obj.first(), -1048576)
262 self.assertEqual(obj.last(), 8388608)
263 self.assertEqual(obj.next(-576), 284)
264 self.assertEqual(obj.prev(284), -576)
265 self.assertEqual(obj.size(), 4)
266 self.assertEqual(obj.exists(-576), True)
267 self.assertEqual(obj.exists(-577), False)
268 self.assertEqual(obj.getelement(284), 'Third element')
269 expected_list = [
270 "First element",
271 "Second element",
272 "Third element",
273 "Fourth element"
274 ]
275 self.assertEqual(obj.aslist(), expected_list)
276 expected_dict = {
277 -1048576: 'First element',
278 -576: 'Second element',
279 284: 'Third element',
280 8388608: 'Fourth element'
281 }
282 self.assertEqual(obj.asdict(), expected_dict)
283 obj.delete(-576)
284 obj.delete(284)
285 expected_list.pop(2)
286 expected_list.pop(1)
287 self.assertEqual(obj.aslist(), expected_list)
288 expected_dict.pop(-576)
289 expected_dict.pop(284)
290 self.assertEqual(obj.asdict(), expected_dict)
291
292 def test_3217_exception_in_iteration(self):
293 "3217 - test executing with arraydmlrowcounts with exception"
294 self.cursor.execute("truncate table TestArrayDML")
295 rows = [
296 (1, "First"),
297 (2, "Second"),
298 (2, "Third"),
299 (4, "Fourth")
300 ]
301 sql = "insert into TestArrayDML (IntCol,StringCol) values (:1,:2)"
302 self.assertRaises(oracledb.DatabaseError, self.cursor.executemany,
303 sql, rows, arraydmlrowcounts=True)
304 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1])
305
306 def test_3218_executing_delete(self):
307 "3218 - test executing delete statement with arraydmlrowcount mode"
308 self.cursor.execute("truncate table TestArrayDML")
309 rows = [
310 (1, "First", 100),
311 (2, "Second", 200),
312 (3, "Third", 300),
313 (4, "Fourth", 300),
314 (5, "Fifth", 300),
315 (6, "Sixth", 400),
316 (7, "Seventh", 400),
317 (8, "Eighth", 500)
318 ]
319 sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \
320 "values (:1, :2, :3)"
321 self.cursor.executemany(sql, rows)
322 rows = [(200,), (300,), (400,)]
323 statement = "delete from TestArrayDML where IntCol2 = :1"
324 self.cursor.executemany(statement, rows, arraydmlrowcounts=True)
325 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 3, 2])
326 self.assertEqual(self.cursor.rowcount, 6)
327
328 def test_3219_executing_update(self):
329 "3219 - test executing update statement with arraydmlrowcount mode"
330 self.cursor.execute("truncate table TestArrayDML")
331 rows = [
332 (1, "First",100),
333 (2, "Second",200),
334 (3, "Third",300),
335 (4, "Fourth",300),
336 (5, "Fifth",300),
337 (6, "Sixth",400),
338 (7, "Seventh",400),
339 (8, "Eighth",500)
340 ]
341 sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \
342 "values (:1, :2, :3)"
343 self.cursor.executemany(sql, rows)
344 rows = [
345 ("One", 100),
346 ("Two", 200),
347 ("Three", 300),
348 ("Four", 400)
349 ]
350 sql = "update TestArrayDML set StringCol = :1 where IntCol2 = :2"
351 self.cursor.executemany(sql, rows, arraydmlrowcounts=True)
352 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 3, 2])
353 self.assertEqual(self.cursor.rowcount, 7)
354
355 def test_3220_implicit_results(self):
356 "3220 - test getimplicitresults() returns the correct data"
357 self.cursor.execute("""
358 declare
359 c1 sys_refcursor;
360 c2 sys_refcursor;
361 begin
362
363 open c1 for
364 select NumberCol
365 from TestNumbers
366 where IntCol between 3 and 5;
367
368 dbms_sql.return_result(c1);
369
370 open c2 for
371 select NumberCol
372 from TestNumbers
373 where IntCol between 7 and 10;
374
375 dbms_sql.return_result(c2);
376
377 end;""")
378 results = self.cursor.getimplicitresults()
379 self.assertEqual(len(results), 2)
380 self.assertEqual([n for n, in results[0]], [3.75, 5, 6.25])
381 self.assertEqual([n for n, in results[1]], [8.75, 10, 11.25, 12.5])
382
383 def test_3221_implicit_results_no_statement(self):
384 "3221 - test getimplicitresults() without executing a statement"
385 self.assertRaises(oracledb.InterfaceError,
386 self.cursor.getimplicitresults)
387
388 def test_3222_insert_with_batch_error(self):
389 "3222 - test executing insert with multiple distinct batch errors"
390 self.cursor.execute("truncate table TestArrayDML")
391 rows = [
392 (1, "First", 100),
393 (2, "Second", 200),
394 (2, "Third", 300),
395 (4, "Fourth", 400),
396 (5, "Fourth", 1000)
397 ]
398 sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \
399 "values (:1, :2, :3)"
400 self.cursor.executemany(sql, rows, batcherrors=True,
401 arraydmlrowcounts=True)
402 user = test_env.get_main_user()
403 expected_errors = [
404 ( 4, 1438, "ORA-01438: value larger than specified " \
405 "precision allowed for this column" ),
406 ( 2, 1, "ORA-00001: unique constraint " \
407 "(%s.TESTARRAYDML_PK) violated" % user.upper())
408 ]
409 actual_errors = [(e.offset, e.code, e.message) \
410 for e in self.cursor.getbatcherrors()]
411 self.assertEqual(actual_errors, expected_errors)
412 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 0, 1, 0])
413
414 def test_3223_batch_error_false(self):
415 "3223 - test batcherrors mode set to False"
416 self.cursor.execute("truncate table TestArrayDML")
417 rows = [
418 (1, "First", 100),
419 (2, "Second", 200),
420 (2, "Third", 300)
421 ]
422 sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \
423 "values (:1, :2, :3)"
424 self.assertRaises(oracledb.IntegrityError, self.cursor.executemany,
425 sql, rows, batcherrors=False)
426
427 def test_3224_update_with_batch_error(self):
428 "3224 - test executing in succession with batch error"
429 self.cursor.execute("truncate table TestArrayDML")
430 rows = [
431 (1, "First", 100),
432 (2, "Second", 200),
433 (3, "Third", 300),
434 (4, "Second", 300),
435 (5, "Fifth", 300),
436 (6, "Sixth", 400),
437 (6, "Seventh", 400),
438 (8, "Eighth", 100)
439 ]
440 sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \
441 "values (:1, :2, :3)"
442 self.cursor.executemany(sql, rows, batcherrors=True)
443 user = test_env.get_main_user()
444 expected_errors = [
445 ( 6, 1, "ORA-00001: unique constraint " \
446 "(%s.TESTARRAYDML_PK) violated" % user.upper())
447 ]
448 actual_errors = [(e.offset, e.code, e.message) \
449 for e in self.cursor.getbatcherrors()]
450 self.assertEqual(actual_errors, expected_errors)
451 rows = [
452 (101, "First"),
453 (201, "Second"),
454 (3000, "Third"),
455 (900, "Ninth"),
456 (301, "Third")
457 ]
458 sql = "update TestArrayDML set IntCol2 = :1 where StringCol = :2"
459 self.cursor.executemany(sql, rows, arraydmlrowcounts=True,
460 batcherrors=True)
461 expected_errors = [
462 (2, 1438, "ORA-01438: value larger than specified " \
463 "precision allowed for this column")
464 ]
465 actual_errors = [(e.offset, e.code, e.message) \
466 for e in self.cursor.getbatcherrors()]
467 self.assertEqual(actual_errors, expected_errors)
468 self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 2, 0, 0, 1])
469 self.assertEqual(self.cursor.rowcount, 4)
470
471 if __name__ == "__main__":
472 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 3300 - Module for testing Simple Oracle Document Access (SODA) Database
6 """
7
8 import test_env
9
10 import cx_Oracle as oracledb
11 import json
12 import unittest
13
14 @unittest.skipIf(test_env.skip_soda_tests(),
15 "unsupported client/server combination")
16 class TestCase(test_env.BaseTestCase):
17
18 def __drop_existing_collections(self, soda_db):
19 for name in soda_db.getCollectionNames():
20 soda_db.openCollection(name).drop()
21
22 def __verify_doc(self, doc, raw_content, str_content=None, content=None,
23 key=None, media_type='application/json'):
24 self.assertEqual(doc.getContentAsBytes(), raw_content)
25 if str_content is not None:
26 self.assertEqual(doc.getContentAsString(), str_content)
27 if content is not None:
28 self.assertEqual(doc.getContent(), content)
29 self.assertEqual(doc.key, key)
30 self.assertEqual(doc.mediaType, media_type)
31
32 def test_3300_create_document_with_json(self):
33 "3300 - test creating documents with JSON data"
34 soda_db = self.connection.getSodaDatabase()
35 val = {"testKey1": "testValue1", "testKey2": "testValue2"}
36 str_val = json.dumps(val)
37 bytes_val = str_val.encode()
38 key = "MyKey"
39 media_type = "text/plain"
40 doc = soda_db.createDocument(val)
41 self.__verify_doc(doc, bytes_val, str_val, val)
42 doc = soda_db.createDocument(str_val, key)
43 self.__verify_doc(doc, bytes_val, str_val, val, key)
44 doc = soda_db.createDocument(bytes_val, key, media_type)
45 self.__verify_doc(doc, bytes_val, str_val, val, key, media_type)
46
47 def test_3301_create_document_with_raw(self):
48 "3301 - test creating documents with raw data"
49 soda_db = self.connection.getSodaDatabase()
50 val = b"<html/>"
51 key = "MyRawKey"
52 media_type = "text/html"
53 doc = soda_db.createDocument(val)
54 self.__verify_doc(doc, val)
55 doc = soda_db.createDocument(val, key)
56 self.__verify_doc(doc, val, key=key)
57 doc = soda_db.createDocument(val, key, media_type)
58 self.__verify_doc(doc, val, key=key, media_type=media_type)
59
60 def test_3302_get_collection_names(self):
61 "3302 - test getting collection names from the database"
62 soda_db = self.connection.getSodaDatabase()
63 self.__drop_existing_collections(soda_db)
64 self.assertEqual(soda_db.getCollectionNames(), [])
65 names = ["zCol", "dCol", "sCol", "aCol", "gCol"]
66 sorted_names = list(sorted(names))
67 for name in names:
68 soda_db.createCollection(name)
69 self.assertEqual(soda_db.getCollectionNames(), sorted_names)
70 self.assertEqual(soda_db.getCollectionNames(limit=2), sorted_names[:2])
71 self.assertEqual(soda_db.getCollectionNames("a"), sorted_names)
72 self.assertEqual(soda_db.getCollectionNames("C"), sorted_names)
73 self.assertEqual(soda_db.getCollectionNames("b", limit=3),
74 sorted_names[1:4])
75 self.assertEqual(soda_db.getCollectionNames("z"), sorted_names[-1:])
76
77 def test_3303_open_collection(self):
78 "3303 - test opening a collection"
79 soda_db = self.connection.getSodaDatabase()
80 self.__drop_existing_collections(soda_db)
81 coll = soda_db.openCollection("CollectionThatDoesNotExist")
82 self.assertEqual(coll, None)
83 created_coll = soda_db.createCollection("TestOpenCollection")
84 coll = soda_db.openCollection(created_coll.name)
85 self.assertEqual(coll.name, created_coll.name)
86 coll.drop()
87
88 def test_3304_repr(self):
89 "3304 - test SodaDatabase representation"
90 con1 = self.connection
91 con2 = test_env.get_connection()
92 soda_db1 = self.connection.getSodaDatabase()
93 soda_db2 = con1.getSodaDatabase()
94 soda_db3 = con2.getSodaDatabase()
95 self.assertEqual(str(soda_db1), str(soda_db2))
96 self.assertEqual(str(soda_db2), str(soda_db3))
97
98 def test_3305_negative(self):
99 "3305 - test negative cases for SODA database methods"
100 soda_db = self.connection.getSodaDatabase()
101 self.assertRaises(TypeError, soda_db.createCollection)
102 self.assertRaises(TypeError, soda_db.createCollection, 1)
103 self.assertRaises(oracledb.DatabaseError, soda_db.createCollection,
104 None)
105 self.assertRaises(TypeError, soda_db.getCollectionNames, 1)
106
107 if __name__ == "__main__":
108 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 3400 - Module for testing Simple Oracle Document Access (SODA) Collections
6 """
7
8 import test_env
9
10 import cx_Oracle as oracledb
11 import unittest
12
13 @unittest.skipIf(test_env.skip_soda_tests(),
14 "unsupported client/server combination")
15 class TestCase(test_env.BaseTestCase):
16
17 def __test_skip(self, coll, num_to_skip, expected_content):
18 filter_spec = {'$orderby': [{'path': 'name', 'order': 'desc'}]}
19 doc = coll.find().filter(filter_spec).skip(num_to_skip).getOne()
20 content = doc.getContent() if doc is not None else None
21 self.assertEqual(content, expected_content)
22
23 def test_3400_invalid_json(self):
24 "3400 - test inserting invalid JSON value into SODA collection"
25 invalid_json = "{testKey:testValue}"
26 soda_db = self.connection.getSodaDatabase()
27 coll = soda_db.createCollection("InvalidJSON")
28 doc = soda_db.createDocument(invalid_json)
29 self.assertRaises(oracledb.DatabaseError, coll.insertOne, doc)
30 coll.drop()
31
32 def test_3401_insert_documents(self):
33 "3401 - test inserting documents into a SODA collection"
34 soda_db = self.connection.getSodaDatabase()
35 coll = soda_db.createCollection("cxoInsertDocs")
36 coll.find().remove()
37 values_to_insert = [
38 {"name": "George", "age": 47},
39 {"name": "Susan", "age": 39},
40 {"name": "John", "age": 50},
41 {"name": "Jill", "age": 54}
42 ]
43 inserted_keys = []
44 for value in values_to_insert:
45 doc = coll.insertOneAndGet(value)
46 inserted_keys.append(doc.key)
47 self.connection.commit()
48 self.assertEqual(coll.find().count(), len(values_to_insert))
49 for key, value in zip(inserted_keys, values_to_insert):
50 doc = coll.find().key(key).getOne()
51 self.assertEqual(doc.getContent(), value)
52 coll.drop()
53
54 def test_3402_skip_documents(self):
55 "3402 - test skipping documents in a SODA collection"
56 soda_db = self.connection.getSodaDatabase()
57 coll = soda_db.createCollection("cxoSkipDocs")
58 coll.find().remove()
59 values_to_insert = [
60 {"name": "Anna", "age": 62},
61 {"name": "Mark", "age": 37},
62 {"name": "Martha", "age": 43},
63 {"name": "Matthew", "age": 28}
64 ]
65 for value in values_to_insert:
66 coll.insertOne(value)
67 self.connection.commit()
68 self.__test_skip(coll, 0, values_to_insert[3])
69 self.__test_skip(coll, 1, values_to_insert[2])
70 self.__test_skip(coll, 3, values_to_insert[0])
71 self.__test_skip(coll, 4, None)
72 self.__test_skip(coll, 125, None)
73
74 def test_3403_replace_document(self):
75 "3403 - test replace documents in SODA collection"
76 soda_db = self.connection.getSodaDatabase()
77 coll = soda_db.createCollection("cxoReplaceDoc")
78 coll.find().remove()
79 content = {'name': 'John', 'address': {'city': 'Sydney'}}
80 doc = coll.insertOneAndGet(content)
81 new_content = {'name': 'John', 'address': {'city':'Melbourne'}}
82 coll.find().key(doc.key).replaceOne(new_content)
83 self.connection.commit()
84 self.assertEqual(coll.find().key(doc.key).getOne().getContent(),
85 new_content)
86 coll.drop()
87
88 def test_3404_search_documents_with_content(self):
89 "3404 - test search documents with content using $like and $regex"
90 soda_db = self.connection.getSodaDatabase()
91 coll = soda_db.createCollection("cxoSearchDocContent")
92 coll.find().remove()
93 data = [
94 {'name': 'John', 'address': {'city': 'Bangalore'}},
95 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
96 {'name': 'Joseph', 'address': {'city': 'Bangalore'}},
97 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
98 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
99 {'name': 'Matthew', 'address': {'city': 'Mumbai'}}
100 ]
101 for value in data:
102 coll.insertOne(value)
103 self.connection.commit()
104 filter_specs = [
105 ({'name': {'$like': 'And%'}}, 1),
106 ({'name': {'$like': 'J%n'}}, 3),
107 ({'name': {'$like': '%hn%'}}, 2),
108 ({'address.city': {'$like': 'Ban%'}}, 3),
109 ({'address.city': {'$like': '%bad'}}, 2),
110 ({'address.city': {'$like': 'Hyderabad'}}, 1),
111 ({'address.city': {'$like': 'China%'}}, 0),
112 ({'name': {'$regex': 'Jo.*'}}, 3),
113 ({'name': {'$regex': '.*[ho]n'}}, 2),
114 ({'name': {'$regex': 'J.*h'}}, 1),
115 ({'address.city': {'$regex': 'Ba.*'}}, 3),
116 ({'address.city': {'$regex': '.*bad'}}, 2),
117 ({'address.city': {'$regex': 'Hyderabad'}}, 1),
118 ({'name': {'$regex': 'Js.*n'}}, 0)
119 ]
120 for filter_spec, expected_count in filter_specs:
121 self.assertEqual(coll.find().filter(filter_spec).count(),
122 expected_count, filter_spec)
123 coll.drop()
124
125 def test_3405_document_remove(self):
126 "3405 - test removing documents"
127 soda_db = self.connection.getSodaDatabase()
128 coll = soda_db.createCollection("cxoRemoveDocs")
129 coll.find().remove()
130 data = [
131 {'name': 'John', 'address': {'city': 'Bangalore'}},
132 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
133 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
134 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
135 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
136 {'name': 'Matthew', 'address': {'city': 'Mumbai'}}
137 ]
138 docs = [coll.insertOneAndGet(v) for v in data]
139 coll.find().key(docs[3].key).remove()
140 self.assertEqual(coll.find().count(), len(data) - 1)
141 searchResults = coll.find().filter({'name': {'$like': 'Jibin'}})
142 self.assertEqual(searchResults.count(), 0)
143 coll.find().filter({'name': {'$like': 'John%'}}).remove()
144 self.assertEqual(coll.find().count(), len(data) - 3)
145 coll.find().filter({'name': {'$regex': 'J.*'}}).remove()
146 self.assertEqual(coll.find().count(), len(data) - 4)
147 self.connection.commit()
148 coll.drop()
149
150 def test_3406_CreateAndDropIndex(self):
151 "3406 - test create and drop Index"
152 index_name = "cxoTestIndexes_ix_1"
153 index_spec = {
154 'name': index_name,
155 'fields': [
156 {
157 'path': 'address.city',
158 'datatype': 'string',
159 'order': 'asc'
160 }
161 ]
162 }
163 soda_db = self.connection.getSodaDatabase()
164 coll = soda_db.createCollection("TestIndexes")
165 coll.find().remove()
166 self.connection.commit()
167 coll.dropIndex(index_name)
168 coll.createIndex(index_spec)
169 self.assertRaises(oracledb.DatabaseError, coll.createIndex, index_spec)
170 self.assertEqual(coll.dropIndex(index_name), True)
171 self.assertEqual(coll.dropIndex(index_name), False)
172 coll.drop()
173
174 def test_3407_get_documents(self):
175 "3407 - test getting documents from Collection"
176 self.connection.autocommit = True
177 soda_db = self.connection.getSodaDatabase()
178 coll = soda_db.createCollection("cxoTestGetDocs")
179 coll.find().remove()
180 data = [
181 {'name': 'John', 'address': {'city': 'Bangalore'}},
182 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
183 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
184 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
185 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}}
186 ]
187 inserted_keys = list(sorted(coll.insertOneAndGet(v).key for v in data))
188 fetched_keys = list(sorted(d.key for d in coll.find().getDocuments()))
189 self.assertEqual(fetched_keys, inserted_keys)
190 coll.drop()
191
192 def test_3408_cursor(self):
193 "3408 - test fetching documents from a cursor"
194 self.connection.autocommit = True
195 soda_db = self.connection.getSodaDatabase()
196 coll = soda_db.createCollection("cxoFindViaCursor")
197 coll.find().remove()
198 data = [
199 {'name': 'John', 'address': {'city': 'Bangalore'}},
200 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
201 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
202 ]
203 inserted_keys = list(sorted(coll.insertOneAndGet(v).key for v in data))
204 fetched_keys = list(sorted(d.key for d in coll.find().getCursor()))
205 self.assertEqual(fetched_keys, inserted_keys)
206 coll.drop()
207
208 def test_3409_multiple_document_remove(self):
209 "3409 - test removing multiple documents using multiple keys"
210 soda_db = self.connection.getSodaDatabase()
211 coll = soda_db.createCollection("cxoRemoveMultipleDocs")
212 coll.find().remove()
213 data = [
214 {'name': 'John', 'address': {'city': 'Bangalore'}},
215 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
216 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
217 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
218 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
219 {'name': 'Matthew', 'address': {'city': 'Mumbai'}}
220 ]
221 docs = [coll.insertOneAndGet(v) for v in data]
222 keys = [docs[i].key for i in (1, 3, 5)]
223 num_removed = coll.find().keys(keys).remove()
224 self.assertEqual(num_removed, len(keys))
225 self.assertEqual(coll.find().count(), len(data) - len(keys))
226 self.connection.commit()
227 coll.drop()
228
229 def test_3410_document_version(self):
230 "3410 - test using version to get documents and remove them"
231 soda_db = self.connection.getSodaDatabase()
232 coll = soda_db.createCollection("cxoDocumentVersion")
233 coll.find().remove()
234 content = {'name': 'John', 'address': {'city': 'Bangalore'}}
235 inserted_doc = coll.insertOneAndGet(content)
236 key = inserted_doc.key
237 version = inserted_doc.version
238 doc = coll.find().key(key).version(version).getOne()
239 self.assertEqual(doc.getContent(), content)
240 new_content = {'name': 'James', 'address': {'city': 'Delhi'}}
241 replacedDoc = coll.find().key(key).replaceOneAndGet(new_content)
242 new_version = replacedDoc.version
243 doc = coll.find().key(key).version(version).getOne()
244 self.assertEqual(doc, None)
245 doc = coll.find().key(key).version(new_version).getOne()
246 self.assertEqual(doc.getContent(), new_content)
247 self.assertEqual(coll.find().key(key).version(version).remove(), 0)
248 self.assertEqual(coll.find().key(key).version(new_version).remove(), 1)
249 self.assertEqual(coll.find().count(), 0)
250 self.connection.commit()
251 coll.drop()
252
253 def test_3411_get_cursor(self):
254 "3411 - test keys with GetCursor"
255 soda_db = self.connection.getSodaDatabase()
256 coll = soda_db.createCollection("cxoKeysWithGetCursor")
257 coll.find().remove()
258 data = [
259 {'name': 'John', 'address': {'city': 'Bangalore'}},
260 {'name': 'Johnson', 'address': {'city': 'Banaras'}},
261 {'name': 'Joseph', 'address': {'city': 'Mangalore'}},
262 {'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
263 {'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
264 {'name': 'Matthew', 'address': {'city': 'Mumbai'}}
265 ]
266 docs = [coll.insertOneAndGet(v) for v in data]
267 keys = [docs[i].key for i in (2, 4, 5)]
268 fetched_keys = [d.key for d in coll.find().keys(keys).getCursor()]
269 self.assertEqual(list(sorted(fetched_keys)), list(sorted(keys)))
270 self.connection.commit()
271 coll.drop()
272
273 def test_3412_created_on(self):
274 "3412 - test createdOn attribute of Document"
275 soda_db = self.connection.getSodaDatabase()
276 coll = soda_db.createCollection("CreatedOn")
277 coll.find().remove()
278 data = {'name': 'John', 'address': {'city': 'Bangalore'}}
279 doc = coll.insertOneAndGet(data)
280 self.assertEqual(doc.createdOn, doc.lastModified)
281
282 @unittest.skipIf(test_env.get_client_version() < (20, 1),
283 "unsupported client")
284 def test_3413_soda_truncate(self):
285 "3413 - test Soda truncate"
286 soda_db = self.connection.getSodaDatabase()
287 coll = soda_db.createCollection("cxoTruncateDocs")
288 coll.find().remove()
289 values_to_insert = [
290 {"name": "George", "age": 47},
291 {"name": "Susan", "age": 39},
292 {"name": "John", "age": 50},
293 {"name": "Jill", "age": 54}
294 ]
295 for value in values_to_insert:
296 coll.insertOne(value)
297 self.connection.commit()
298 self.assertEqual(coll.find().count(), len(values_to_insert))
299 coll.truncate()
300 self.assertEqual(coll.find().count(), 0)
301 coll.drop()
302
303 if __name__ == "__main__":
304 test_env.run_test_cases()
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 """
5 3500 - Module for testing the JSON data type.
6 """
7
8 import cx_Oracle as oracledb
9 import test_env
10 import datetime
11 import decimal
12 import unittest
13
14 @unittest.skipUnless(test_env.get_client_version() >= (21, 0),
15 "unsupported client")
16 @unittest.skipUnless(test_env.get_server_version() >= (21, 0),
17 "unsupported server")
18 class TestCase(test_env.BaseTestCase):
19
20 json_data = [
21 True,
22 False,
23 'String',
24 b'Some Bytes',
25 {},
26 {"name": None},
27 {"name": "John"},
28 {"age": 30},
29 {"Permanent": True},
30 {
31 "employee": {
32 "name":"John",
33 "age": 30,
34 "city": "Delhi",
35 "Parmanent": True
36 }
37 },
38 {
39 "employees": ["John", "Matthew", "James"]
40 },
41 {
42 "employees": [
43 {
44 "employee1": {"name": "John", "city": "Delhi"}
45 },
46 {
47 "employee2": {"name": "Matthew", "city": "Mumbai"}
48 },
49 {
50 "employee3": {"name": "James", "city": "Bangalore"}
51 }
52 ]
53 }
54 ]
55
56 def __bind_scalar_as_json(self, data):
57 self.cursor.execute("truncate table TestJson")
58 out_var = self.cursor.var(oracledb.DB_TYPE_JSON,
59 arraysize=len(data))
60 self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON, out_var)
61 bind_data = list(enumerate(data))
62 self.cursor.executemany("""
63 insert into TestJson values (:1, :2)
64 returning JsonCol into :json_out""", bind_data)
65 self.connection.commit()
66 self.assertEqual(out_var.values, [[v] for v in data])
67
68 def test_3500_insert_and_fetch_single_json(self):
69 "3500 - insert and fetch single row with JSON"
70 self.cursor.execute("truncate table TestJson")
71 self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
72 self.cursor.execute("insert into TestJson values (:1, :2)",
73 [1, self.json_data])
74 self.cursor.execute("select JsonCol from TestJson")
75 result, = self.cursor.fetchone()
76 self.assertEqual(result, self.json_data)
77
78 def test_3501_execute_with_dml_returning(self):
79 "3502 - inserting single rows with JSON and DML returning"
80 json_val = self.json_data[11]
81 self.cursor.execute("truncate table TestJson")
82 json_out = self.cursor.var(oracledb.DB_TYPE_JSON)
83 self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON, json_out)
84 self.cursor.execute("""
85 insert into TestJson values (:1, :2)
86 returning JsonCol into :json_out""",
87 [1, json_val])
88 self.assertEqual(json_out.getvalue(0), [json_val])
89
90 def test_3502_insert_and_fetch_multiple_json(self):
91 "3502 - insert and fetch multiple rows with JSON"
92 self.cursor.execute("truncate table TestJson")
93 self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
94 data = list(enumerate(self.json_data))
95 self.cursor.executemany("insert into TestJson values(:1, :2)", data)
96 self.cursor.execute("select * from TestJson")
97 fetched_data = self.cursor.fetchall()
98 self.assertEqual(fetched_data, data)
99
100 def test_3503_executemany_with_dml_returning(self):
101 "3503 - inserting multiple rows with JSON and DML returning"
102 self.cursor.execute("truncate table TestJson")
103 int_values = [i for i in range(len(self.json_data))]
104 out_int_var = self.cursor.var(int, arraysize=len(int_values))
105 out_json_var = self.cursor.var(oracledb.DB_TYPE_JSON,
106 arraysize=len(int_values))
107 self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON, out_int_var,
108 out_json_var)
109 data = list(zip(int_values, self.json_data))
110 self.cursor.executemany("""
111 insert into TestJson
112 values(:int_val, :json_val)
113 returning IntCol, JsonCol into :int_var, :json_var""", data)
114 self.assertEqual(out_int_var.values, [[v] for v in int_values])
115 self.assertEqual(out_json_var.values, [[v] for v in self.json_data])
116
117 def test_3504_boolean(self):
118 "3509 - test binding boolean values as scalar JSON values"
119 data = [
120 True,
121 False,
122 True,
123 True,
124 False,
125 True
126 ]
127 self.__bind_scalar_as_json(data)
128
129 def test_3505_strings_and_bytes(self):
130 "3509 - test binding strings/bytes values as scalar JSON values"
131 data = [
132 "String 1",
133 b"A raw value",
134 "A much longer string",
135 b"A much longer RAW value",
136 "Short string",
137 b"Y"
138 ]
139 self.__bind_scalar_as_json(data)
140
141 def test_3506_datetime(self):
142 "3506 - test binding dates/intervals as scalar JSON values"
143 data = [
144 datetime.datetime.today(),
145 datetime.datetime(2004, 2, 1, 3, 4, 5),
146 datetime.datetime(2020, 12, 2, 13, 29, 14),
147 datetime.timedelta(8.5),
148 datetime.datetime(2002, 12, 13, 9, 36, 0),
149 oracledb.Timestamp(2002, 12, 13, 9, 36, 0),
150 datetime.datetime(2002, 12, 13)
151 ]
152 self.__bind_scalar_as_json(data)
153
154 def test_3507_bind_number(self):
155 "3507 - test binding number in json values"
156 data = [
157 0,
158 1,
159 25.25,
160 6088343244,
161 -9999999999999999999,
162 decimal.Decimal("0.25"),
163 decimal.Decimal("10.25"),
164 decimal.Decimal("319438950232418390.273596")
165 ]
166 self.__bind_scalar_as_json(data)
167
168 if __name__ == "__main__":
169 test_env.run_test_cases()
+0
-58
test/test_dbapi20.py less more
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 """Driver specific portion of the DB API test suite provided by Stuart Bishop
10 available at http://stuartbishop.net/Software/DBAPI20TestSuite/"""
11
12 from __future__ import print_function
13
14 import cx_Oracle
15 import dbapi20
16 import unittest
17
18 import TestEnv
19
20 class TestSuite(dbapi20.DatabaseAPI20Test):
21
22 connect_args = (TestEnv.GetMainUser(), TestEnv.GetMainPassword(),
23 TestEnv.GetConnectString())
24 driver = cx_Oracle
25
26 # not implemented; see cx_Oracle specific test suite instead
27 def test_callproc(self):
28 pass
29
30 # not implemented; see cx_Oracle specific test suite instead
31 def test_fetchmany(self):
32 pass
33
34 # not implemented; Oracle does not support the concept
35 def test_nextset(self):
36 pass
37
38 # not implemented; see cx_Oracle specific test suite instead
39 def test_rowcount(self):
40 pass
41
42 # not implemented; see cx_Oracle specific test suite instead
43 def test_setinputsizes(self):
44 pass
45
46 # not implemented; not used by cx_Oracle
47 def test_setoutputsize(self):
48 pass
49
50 # not implemented; Oracle does not support the concept
51 def test_Time(self):
52 pass
53
54 if __name__ == "__main__":
55 print("Testing cx_Oracle version", cx_Oracle.__version__)
56 TestEnv.RunTestCases()
57
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2 #
3 # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
4 #
5 # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
6 # Canada. All rights reserved.
7 #------------------------------------------------------------------------------
8
9 #------------------------------------------------------------------------------
10 # Sets the environment used by the cx_Oracle test suite. Production
11 # applications should consider using External Authentication to
12 # avoid hard coded credentials.
13 #
14 # You can set values in environment variables to bypass having the test suite
15 # request the information it requires.
16 #
17 # CX_ORACLE_TEST_MAIN_USER: user used for most test cases
18 # CX_ORACLE_TEST_MAIN_PASSWORD: password of user used for most test cases
19 # CX_ORACLE_TEST_PROXY_USER: user for testing proxying
20 # CX_ORACLE_TEST_PROXY_PASSWORD: password of user for testing proxying
21 # CX_ORACLE_TEST_CONNECT_STRING: connect string for test suite
22 # CX_ORACLE_TEST_ADMIN_USER: administrative user for test suite
23 # CX_ORACLE_TEST_ADMIN_PASSWORD: administrative password for test suite
24 #
25 # CX_ORACLE_TEST_CONNECT_STRING can be set to an Easy Connect string, or a
26 # Net Service Name from a tnsnames.ora file or external naming service,
27 # or it can be the name of a local Oracle database instance.
28 #
29 # If cx_Oracle is using Instant Client, then an Easy Connect string is
30 # generally appropriate. The syntax is:
31 #
32 # [//]host_name[:port][/service_name][:server_type][/instance_name]
33 #
34 # Commonly just the host_name and service_name are needed
35 # e.g. "localhost/orclpdb1" or "localhost/XEPDB1"
36 #
37 # If using a tnsnames.ora file, the file can be in a default
38 # location such as $ORACLE_HOME/network/admin/tnsnames.ora or
39 # /etc/tnsnames.ora. Alternatively set the TNS_ADMIN environment
40 # variable and put the file in $TNS_ADMIN/tnsnames.ora.
41 #
42 # The administrative user for cloud databases is ADMIN and the administrative
43 # user for on premises databases is SYSTEM.
44 #------------------------------------------------------------------------------
45
46 import cx_Oracle as oracledb
47
48 import getpass
49 import os
50 import sys
51 import unittest
52
53 # default values
54 DEFAULT_MAIN_USER = "pythontest"
55 DEFAULT_PROXY_USER = "pythontestproxy"
56 DEFAULT_CONNECT_STRING = "localhost/orclpdb1"
57
58 # dictionary containing all parameters; these are acquired as needed by the
59 # methods below (which should be used instead of consulting this dictionary
60 # directly) and then stored so that a value is not requested more than once
61 PARAMETERS = {}
62
63 def get_value(name, label, default_value=""):
64 value = PARAMETERS.get(name)
65 if value is not None:
66 return value
67 env_name = "CX_ORACLE_TEST_" + name
68 value = os.environ.get(env_name)
69 if value is None:
70 if default_value:
71 label += " [%s]" % default_value
72 label += ": "
73 if default_value:
74 value = input(label).strip()
75 else:
76 value = getpass.getpass(label)
77 if not value:
78 value = default_value
79 PARAMETERS[name] = value
80 return value
81
82 def get_admin_connect_string():
83 admin_user = get_value("ADMIN_USER", "Administrative user", "admin")
84 admin_password = get_value("ADMIN_PASSWORD",
85 "Password for %s" % admin_user)
86 return "%s/%s@%s" % (admin_user, admin_password, get_connect_string())
87
88 def get_charset_ratio():
89 value = PARAMETERS.get("CS_RATIO")
90 if value is None:
91 connection = get_connection()
92 cursor = connection.cursor()
93 cursor.execute("select 'X' from dual")
94 column_info, = cursor.description
95 value = PARAMETERS["CS_RATIO"] = column_info[3]
96 return value
97
98 def get_client_version():
99 name = "CLIENT_VERSION"
100 value = PARAMETERS.get(name)
101 if value is None:
102 value = oracledb.clientversion()[:2]
103 PARAMETERS[name] = value
104 return value
105
106 def get_connection(**kwargs):
107 return oracledb.connect(dsn=get_connect_string(), user=get_main_user(),
108 password=get_main_password(), **kwargs)
109
110 def get_connect_string():
111 return get_value("CONNECT_STRING", "Connect String",
112 DEFAULT_CONNECT_STRING)
113
114 def get_main_password():
115 return get_value("MAIN_PASSWORD", "Password for %s" % get_main_user())
116
117 def get_main_user():
118 return get_value("MAIN_USER", "Main User Name", DEFAULT_MAIN_USER)
119
120 def get_pool(user=None, password=None, **kwargs):
121 if user is None:
122 user = get_main_user()
123 if password is None:
124 password = get_main_password()
125 return oracledb.SessionPool(user, password, get_connect_string(),
126 **kwargs)
127
128 def get_proxy_password():
129 return get_value("PROXY_PASSWORD", "Password for %s" % get_proxy_user())
130
131 def get_proxy_user():
132 return get_value("PROXY_USER", "Proxy User Name", DEFAULT_PROXY_USER)
133
134 def get_server_version():
135 name = "SERVER_VERSION"
136 value = PARAMETERS.get(name)
137 if value is None:
138 conn = get_connection()
139 value = tuple(int(s) for s in conn.version.split("."))[:2]
140 PARAMETERS[name] = value
141 return value
142
143 def run_sql_script(conn, script_name, **kwargs):
144 statement_parts = []
145 cursor = conn.cursor()
146 replace_values = [("&" + k + ".", v) for k, v in kwargs.items()] + \
147 [("&" + k, v) for k, v in kwargs.items()]
148 script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
149 file_name = os.path.join(script_dir, "sql", script_name + "Exec.sql")
150 for line in open(file_name):
151 if line.strip() == "/":
152 statement = "".join(statement_parts).strip()
153 if statement:
154 for search_value, replace_value in replace_values:
155 statement = statement.replace(search_value, replace_value)
156 try:
157 cursor.execute(statement)
158 except:
159 print("Failed to execute SQL:", statement)
160 raise
161 statement_parts = []
162 else:
163 statement_parts.append(line)
164 cursor.execute("""
165 select name, type, line, position, text
166 from dba_errors
167 where owner = upper(:owner)
168 order by name, type, line, position""",
169 owner = get_main_user())
170 prev_name = prev_obj_type = None
171 for name, obj_type, line_num, position, text in cursor:
172 if name != prev_name or obj_type != prev_obj_type:
173 print("%s (%s)" % (name, obj_type))
174 prev_name = name
175 prev_obj_type = obj_type
176 print(" %s/%s %s" % (line_num, position, text))
177
178 def run_test_cases():
179 print("Running tests for cx_Oracle version", oracledb.version,
180 "built at", oracledb.buildtime)
181 print("File:", oracledb.__file__)
182 print("Client Version:",
183 ".".join(str(i) for i in oracledb.clientversion()))
184 with get_connection() as connection:
185 print("Server Version:", connection.version)
186 print()
187 unittest.main(testRunner=unittest.TextTestRunner(verbosity=2))
188
189 def skip_soda_tests():
190 client = get_client_version()
191 if client < (18, 3):
192 return True
193 server = get_server_version()
194 if server < (18, 0):
195 return True
196 if server > (20, 1) and client < (20, 1):
197 return True
198 return False
199
200 class RoundTripInfo:
201
202 def __init__(self, connection):
203 self.prev_round_trips = 0
204 self.admin_conn = oracledb.connect(get_admin_connect_string())
205 with connection.cursor() as cursor:
206 cursor.execute("select sys_context('userenv', 'sid') from dual")
207 self.sid, = cursor.fetchone()
208 self.get_round_trips()
209
210 def get_round_trips(self):
211 with self.admin_conn.cursor() as cursor:
212 cursor.execute("""
213 select ss.value
214 from v$sesstat ss, v$statname sn
215 where ss.sid = :sid
216 and ss.statistic# = sn.statistic#
217 and sn.name like '%roundtrip%client%'""", sid=self.sid)
218 current_round_trips, = cursor.fetchone()
219 diff_round_trips = current_round_trips - self.prev_round_trips
220 self.prev_round_trips = current_round_trips
221 return diff_round_trips
222
223 class BaseTestCase(unittest.TestCase):
224 requires_connection = True
225
226 def assertRoundTrips(self, n):
227 self.assertEqual(self.round_trip_info.get_round_trips(), n)
228
229 def get_soda_database(self, minclient=(18, 3), minserver=(18, 0),
230 message="not supported with this client/server " \
231 "combination"):
232 client = get_client_version()
233 if client < minclient:
234 self.skipTest(message)
235 server = get_server_version()
236 if server < minserver:
237 self.skipTest(message)
238 if server > (20, 1) and client < (20, 1):
239 self.skipTest(message)
240 return self.connection.getSodaDatabase()
241
242 def is_on_oracle_cloud(self, connection=None):
243 if connection is None:
244 connection = self.connection
245 cursor = connection.cursor()
246 cursor.execute("""
247 select sys_context('userenv', 'service_name')
248 from dual""")
249 service_name, = cursor.fetchone()
250 return service_name.endswith("oraclecloud.com")
251
252 def setUp(self):
253 if self.requires_connection:
254 self.connection = get_connection()
255 self.cursor = self.connection.cursor()
256
257 def setup_round_trip_checker(self):
258 self.round_trip_info = RoundTripInfo(self.connection)
259
260 def tearDown(self):
261 if self.requires_connection:
262 self.connection.close()
263 del self.cursor
264 del self.connection
0 [tox]
1 envlist = py{36,37,38,39}
2
3 [testenv]
4 commands = {envpython} -m unittest discover -v -s test
5 passenv =
6 CX_ORACLE_TEST_MAIN_USER
7 CX_ORACLE_TEST_MAIN_PASSWORD
8 CX_ORACLE_TEST_PROXY_USER
9 CX_ORACLE_TEST_PROXY_PASSWORD
10 CX_ORACLE_TEST_CONNECT_STRING
11 CX_ORACLE_TEST_ADMIN_USER
12 CX_ORACLE_TEST_ADMIN_PASSWORD
13 DPI_DEBUG_LEVEL
14 ORACLE_HOME