Codebase list python-cx-oracle / upstream/latest
New upstream version 7.1 Sophie Brun 5 years ago
189 changed file(s) with 34764 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 * text=auto
1
2 *.c text
3 *.h text
4 *.in text
5 *.py text
6 *.rst text
7 *.txt text
0 # Python cx_Oracle Support
1
2 cx_Oracle is an Open Source project, so do some searching and reading
3 before asking questions.
4
5 ## cx_Oracle Installation issues
6
7 Read the [Installation instructions](http://cx-oracle.readthedocs.io/en/latest/installation.html)
8
9 ## SQL and PL/SQL Questions
10
11 Ask SQL and PL/SQL questions at [AskTOM](https://asktom.oracle.com/)
12
13 Try out SQL and find code snippets on our hosted database
14 with [LIVE SQL](https://livesql.oracle.com/)
15
16 ## Database and other Oracle Issues
17
18 Ask Database and other Oracle issues on
19 an [OTN Forum](https://community.oracle.com/community/database/)
20
21 ## cx_Oracle Documentation
22
23 The cx_Oracle documentation
24 is [here](http://cx-oracle.readthedocs.io/en/latest/)
25
26 ## Got a cx_Oracle question?
27
28 Ask at [GitHub](https://github.com/oracle/python-cx_Oracle/issues)
29
30 When opening a new issue, fill in the template that will be shown.
31 Include enough information for people to understand your problem.
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 Thanks for contributing!
1
2 Before submitting PRs for cx_Oracle you must have your signed *Oracle
3 Contributor Agreement* accepted. See
4 https://www.oracle.com/technetwork/community/oca-486395.html
5
6 If the problem solved is small, you may find it easier to open an Issue
7 describing the problem and its cause so we can create the fix.
8
9 The bottom of your commit message must have the following line using your name
10 and e-mail address as it appears in the OCA Signatories list.
11
12 ```
13 Signed-off-by: Your Name <[email protected]>
14 ```
15
16 This can be automatically added to pull requests by committing with:
17
18 ```
19 git commit --signoff
20 ````
0 *.pyc
1 build/
2 dist/
3 doc/build
4 cx_Oracle.egg-info/
5 MANIFEST
0 [submodule "odpi"]
1 path = odpi
2 url = ../odpi.git
3 branch = v3.1.x
0 # Contributing to cx_Oracle
1
2 *Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.*
3
4 Pull requests can be made under
5 [The Oracle Contributor Agreement](https://www.oracle.com/technetwork/community/oca-486395.html)
6 (OCA).
7
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.
11
12 ```
13 Signed-off-by: Your Name <[email protected]>
14 ```
15
16 This can be automatically added to pull requests by committing with:
17
18 ```
19 git commit --signoff
20 ````
21
22 Only pull requests from committers that can be verified as having
23 signed the OCA can be accepted.
0 LICENSE AGREEMENT FOR CX_ORACLE
1
2 Copyright 2016, 2018, 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 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11
12 1. Redistributions of source code must retain the above copyright notice, this
13 list of conditions, and the disclaimer that follows.
14
15 2. Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions, and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
18
19 3. Neither the names of the copyright holders nor the names of any contributors
20 may be used to endorse or promote products derived from this software
21 without specific prior written permission.
22
23 DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 *AS IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
27 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
30 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34 Computronix is a registered trademark of Computronix (Canada) Ltd.
35
0 include MANIFEST.in
1 include *.txt
2 recursive-include odpi *.c
3 recursive-include odpi *.h
4 prune odpi/test
5 prune odpi/samples
6 recursive-include src *.c
7 recursive-include src *.h
8 recursive-include samples *.py *.sql
9 recursive-include test *.py *.sql
0 # cx_Oracle version 7.1
1
2 cx_Oracle is a Python extension module that enables access to Oracle
3 Database. It conforms to the [Python database API 2.0
4 specification][1] with a considerable number of additions and a couple
5 of exclusions. See the
6 [homepage](https://oracle.github.io/python-cx_Oracle/index.html) for a
7 feature list.
8
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.
15
16 ## Installation
17
18 See [cx_Oracle Installation][15].
19
20 ## Documentation
21
22 See the [cx_Oracle Documentation][2] and [Release Notes][14].
23
24 ## Samples
25
26 See the [/samples][12] directory and the [tutorial][6]. You can also
27 look at the scripts in [cx_OracleTools][7] and the modules in
28 [cx_PyOracleLib][8].
29
30 ## Help
31
32 Issues and questions can be raised with the cx_Oracle community on
33 [GitHub][9] or on the [mailing list][5].
34
35 ## Tests
36
37 See [/test][11].
38
39 ## Contributing
40
41 See [CONTRIBUTING](https://github.com/oracle/python-cx_Oracle/blob/master/CONTRIBUTING.md)
42
43 ## License
44
45 cx_Oracle is licensed under a BSD license which you can find [here][3].
46
47 [1]: https://www.python.org/dev/peps/pep-0249
48 [2]: http://cx-oracle.readthedocs.io
49 [3]: https://github.com/oracle/python-cx_Oracle/blob/master/LICENSE.txt
50 [5]: http://lists.sourceforge.net/lists/listinfo/cx-oracle-users
51 [6]: https://github.com/oracle/python-cx_Oracle/tree/master/samples/tutorial
52 [7]: http://cx-oracletools.sourceforge.net
53 [8]: http://cx-pyoraclelib.sourceforge.net
54 [9]: https://github.com/oracle/python-cx_Oracle/issues
55 [11]: https://github.com/oracle/python-cx_Oracle/tree/master/test
56 [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
0 Please see the cx_Oracle home page for links to documentation, source, build
1 and installation instructions:
2
3 https://oracle.github.io/python-cx_Oracle/index.html
4
0 # Makefile to generate cx_Oracle documentation using Sphinx
1
2 SPHINXOPTS =
3 SPHINXBUILD = sphinx-build
4 SOURCEDIR = src
5 BUILDDIR = build
6
7 .PHONY: html
8 html:
9 @$(SPHINXBUILD) -M html $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS)
10
11 .PHONY: epub
12 epub:
13 @$(SPHINXBUILD) -M epub $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS)
14
15 .PHONY: pdf
16 pdf:
17 @$(SPHINXBUILD) -M latexpdf $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS)
18
19 .PHONY: clean
20 clean:
21 rm -rf $(BUILDDIR)/*
22
0 The generated cx_Oracle documentation is at http://cx-oracle.readthedocs.io/
1
2 This directory contains the documentation source. It is written using reST
3 (re-Structured Text) format source files which are processed using Sphinx and
4 turned into HTML, PDF or ePub documents. If you wish to build these yourself,
5 you need to install Sphinx. Sphinx is available on many Linux distributions as a
6 pre-built package. You can also install Sphinx on all platforms using the Python
7 package manager "pip". For more information on Sphinx, please visit this page:
8
9 http://www.sphinx-doc.org
10
11 Once Sphinx is installed, the supplied Makefile can be used to build the
12 different targets.
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
0 # -*- coding: utf-8 -*-
1 #
2 # cx_Oracle documentation build configuration file
3 #
4 # This file is execfile()d with the current directory set to its containing dir.
5 #
6 # The contents of this file are pickled, so don't put values in the namespace
7 # that aren't pickleable (module imports are okay, they're removed automatically).
8 #
9 # All configuration values have a default value; values that are commented out
10 # serve to show the default value.
11
12 import sys
13
14 # If your extensions are in another directory, add it here.
15 #sys.path.append('some/directory')
16
17 # General configuration
18 # ---------------------
19
20 # Add any Sphinx extension module names here, as strings. They can be extensions
21 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
22 #extensions = []
23
24 # Add any paths that contain templates here, relative to this directory.
25 templates_path = ['.templates']
26
27 # The suffix of source filenames.
28 source_suffix = '.rst'
29
30 # The master toctree document.
31 master_doc = 'index'
32
33 # General substitutions.
34 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'
36 author = 'Oracle'
37
38 # The default replacements for |version| and |release|, also used in various
39 # other places throughout the built documents.
40 #
41 # The short X.Y version.
42 version = '7.1'
43 # The full version, including alpha/beta/rc tags.
44 release = '7.1.0'
45
46 # There are two options for replacing |today|: either, you set today to some
47 # non-false value, then it is used:
48 #today = ''
49 # Else, today_fmt is used as the format for a strftime call.
50 today_fmt = '%B %d, %Y'
51
52 # List of documents that shouldn't be included in the build.
53 #unused_docs = []
54
55 # If true, '()' will be appended to :func: etc. cross-reference text.
56 #add_function_parentheses = True
57
58 # If true, the current module name will be prepended to all description
59 # unit titles (such as .. function::).
60 #add_module_names = True
61
62 # If true, sectionauthor and moduleauthor directives will be shown in the
63 # output. They are ignored by default.
64 #show_authors = False
65
66 # The name of the Pygments (syntax highlighting) style to use.
67 pygments_style = 'sphinx'
68
69
70 # Options for HTML output
71 # -----------------------
72
73 # The style sheet to use for HTML and HTML Help pages. A file of that name
74 # must exist either in Sphinx' static/ path, or in one of the custom paths
75 # given in html_static_path.
76 html_style = 'default.css'
77
78 # Add any paths that contain custom static files (such as style sheets) here,
79 # relative to this directory. They are copied after the builtin static files,
80 # so a file named "default.css" will overwrite the builtin "default.css".
81 #html_static_path = ['.static']
82
83 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
84 # using the given strftime format.
85 html_last_updated_fmt = '%b %d, %Y'
86
87 # If true, SmartyPants will be used to convert quotes and dashes to
88 # typographically correct entities.
89 #html_use_smartypants = True
90
91 # Content template for the index page.
92 #html_index = ''
93
94 # Custom sidebar templates, maps document names to template names.
95 #html_sidebars = {}
96
97 # Additional templates that should be rendered to pages, maps page names to
98 # template names.
99 #html_additional_pages = {}
100
101 # If false, no module index is generated.
102 #html_use_modindex = True
103
104 # If true, the reST sources are included in the HTML build as _sources/<name>.
105 html_copy_source = False
106
107 # Output file base name for HTML help builder.
108 htmlhelp_basename = 'cx_Oracledoc'
109
110
111 # Options for LaTeX output
112 # ------------------------
113
114 # The paper size ('letter' or 'a4').
115 #latex_paper_size = 'letter'
116
117 # The font size ('10pt', '11pt' or '12pt').
118 #latex_font_size = '10pt'
119
120 # Grouping the document tree into LaTeX files. List of tuples
121 # (source start file, target name, title, author, document class [howto/manual]).
122 #latex_documents = []
123
124 # Additional stuff for the LaTeX preamble.
125 #latex_preamble = ''
126
127 # Documents to append as an appendix to all manuals.
128 #latex_appendices = []
129
130 # If false, no module index is generated.
131 #latex_use_modindex = True
132
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 .. _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
1 Welcome to cx_Oracle's documentation!
2 =====================================
3
4 **cx_Oracle** is a module that enables access to Oracle Database and conforms
5 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.
8
9 **cx_Oracle** is distributed under an open-source :ref:`license <license>`
10 (the BSD license).
11
12 Contents:
13
14 .. toctree::
15 :maxdepth: 2
16
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
31
32
33 Indices and tables
34 ==================
35
36 * :ref:`genindex`
37 * :ref:`modindex`
38 * :ref:`search`
39
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 .. _license:
1
2 *******
3 License
4 *******
5
6 .. include:: <isonum.txt>
7
8 .. centered:: **LICENSE AGREEMENT FOR CX_ORACLE**
9
10 Copyright |copy| 2016, 2018, Oracle and/or its affiliates. All rights reserved.
11
12 Copyright |copy| 2007-2015, Anthony Tuininga. All rights reserved.
13
14 Copyright |copy| 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
15 Canada. All rights reserved.
16
17 Redistribution and use in source and binary forms, with or without modification,
18 are permitted provided that the following conditions are met:
19
20 #. Redistributions of source code must retain the above copyright notice, this
21 list of conditions, and the disclaimer that follows.
22
23 #. Redistributions in binary form must reproduce the above copyright notice,
24 this list of conditions, and the following disclaimer in the documentation
25 and/or other materials provided with the distribution.
26
27 #. Neither the names of the copyright holders nor the names of any contributors
28 may be used to endorse or promote products derived from this software without
29 specific prior written permission.
30
31 DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 \*AS IS\* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
33 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
35 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
38 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41
42 Computronix |reg| is a registered trademark of Computronix (Canada) Ltd.
43
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 .. 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 .. _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 .. _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 .. _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 .. _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 .. _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 .. _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 .. _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 #------------------------------------------------------------------------------
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
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # AdvancedQueuingNotification.py
6 # This script demonstrates using advanced queuing notification. Once this
7 # 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.
10 #
11 # This script requires cx_Oracle 6.4 and higher.
12 #------------------------------------------------------------------------------
13
14 from __future__ import print_function
15
16 import cx_Oracle
17 import SampleEnv
18 import threading
19 import time
20
21 registered = True
22
23 def callback(message):
24 global registered
25 print("Message type:", message.type)
26 if message.type == cx_Oracle.EVENT_DEREG:
27 print("Deregistration has taken place...")
28 registered = False
29 return
30 print("Queue name:", message.queueName)
31 print("Consumer name:", message.consumerName)
32
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)
36 print("Subscription:", sub)
37 print("--> Connection:", sub.connection)
38 print("--> Callback:", sub.callback)
39 print("--> Namespace:", sub.namespace)
40 print("--> Protocol:", sub.protocol)
41 print("--> Timeout:", sub.timeout)
42
43 while registered:
44 print("Waiting for notifications....")
45 time.sleep(5)
46
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 # AppContext.py
11 # This script demonstrates the use of application context. Application
12 # context is available within logon triggers and can be retrieved by using the
13 # function sys_context().
14 #
15 # This script requires cx_Oracle 5.3 and higher.
16 #------------------------------------------------------------------------------
17
18 from __future__ import print_function
19
20 import cx_Oracle
21 import SampleEnv
22
23 # define constants used throughout the script; adjust as desired
24 APP_CTX_NAMESPACE = "CLIENTCONTEXT"
25 APP_CTX_ENTRIES = [
26 ( APP_CTX_NAMESPACE, "ATTR1", "VALUE1" ),
27 ( APP_CTX_NAMESPACE, "ATTR2", "VALUE2" ),
28 ( APP_CTX_NAMESPACE, "ATTR3", "VALUE3" )
29 ]
30
31 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(),
32 appcontext = APP_CTX_ENTRIES)
33 cursor = connection.cursor()
34 for namespace, name, value in APP_CTX_ENTRIES:
35 cursor.execute("select sys_context(:1, :2) from dual", (namespace, name))
36 value, = cursor.fetchone()
37 print("Value of context key", name, "is", value)
38
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # ArrayDMLRowCounts.py
6 #
7 # Demonstrate the use of the 12.1 feature that allows cursor.executemany()
8 # to return the number of rows affected by each individual execution as a list.
9 # The parameter "arraydmlrowcounts" must be set to True in the call to
10 # cursor.executemany() after which cursor.getarraydmlrowcounts() can be called.
11 #
12 # This script requires cx_Oracle 5.2 and higher.
13 #------------------------------------------------------------------------------
14
15 from __future__ import print_function
16
17 import cx_Oracle
18 import SampleEnv
19
20 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
21 cursor = connection.cursor()
22
23 # show the number of rows for each parent ID as a means of verifying the
24 # output from the delete statement
25 for parentId, count in cursor.execute("""
26 select ParentId, count(*)
27 from ChildTable
28 group by ParentId
29 order by ParentId"""):
30 print("Parent ID:", parentId, "has", int(count), "rows.")
31 print()
32
33 # delete the following parent IDs only
34 parentIdsToDelete = [20, 30, 50]
35
36 print("Deleting Parent IDs:", parentIdsToDelete)
37 print()
38
39 # enable array DML row counts for each iteration executed in executemany()
40 cursor.executemany("""
41 delete from ChildTable
42 where ParentId = :1""",
43 [(i,) for i in parentIdsToDelete],
44 arraydmlrowcounts = True)
45
46 # 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.")
50
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # BatchErrors.py
6 #
7 # Demonstrate the use of the Oracle Database 12.1 feature that allows
8 # cursor.executemany() to complete successfully, even if errors take
9 # place during the execution of one or more of the individual
10 # executions. The parameter "batcherrors" must be set to True in the
11 # call to cursor.executemany() after which cursor.getbatcherrors() can
12 # be called, which will return a list of error objects.
13 #
14 # This script requires cx_Oracle 5.2 and higher.
15 #------------------------------------------------------------------------------
16
17 from __future__ import print_function
18
19 import cx_Oracle
20 import SampleEnv
21
22 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
23 cursor = connection.cursor()
24
25 # define data to insert
26 dataToInsert = [
27 (1016, 10, 'Child B of Parent 10'),
28 (1017, 10, 'Child C of Parent 10'),
29 (1018, 20, 'Child D of Parent 20'),
30 (1018, 20, 'Child D of Parent 20'), # duplicate key
31 (1019, 30, 'Child C of Parent 30'),
32 (1020, 30, 'Child D of Parent 40'),
33 (1021, 60, 'Child A of Parent 60'), # parent does not exist
34 (1022, 40, 'Child F of Parent 40'),
35 ]
36
37 # retrieve the number of rows in the table
38 cursor.execute("""
39 select count(*)
40 from ChildTable""")
41 count, = cursor.fetchone()
42 print("number of rows in child table:", int(count))
43 print("number of rows to insert:", len(dataToInsert))
44
45 # old method: executemany() with data errors results in stoppage after the
46 # first error takes place; the row count is updated to show how many rows
47 # actually succeeded
48 try:
49 cursor.executemany("insert into ChildTable values (:1, :2, :3)",
50 dataToInsert)
51 except cx_Oracle.DatabaseError as e:
52 error, = e.args
53 print("FAILED with error:", error.message)
54 print("number of rows which succeeded:", cursor.rowcount)
55
56 # demonstrate that the row count is accurate
57 cursor.execute("""
58 select count(*)
59 from ChildTable""")
60 count, = cursor.fetchone()
61 print("number of rows in child table after failed insert:", int(count))
62
63 # roll back so we can perform the same work using the new method
64 connection.rollback()
65
66 # new method: executemany() with batch errors enabled (and array DML row counts
67 # 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)
70
71 # 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)
74
75 # display the errors that have taken place
76 errors = cursor.getbatcherrors()
77 print("number of errors which took place:", len(errors))
78 for error in errors:
79 print("Error", error.message.rstrip(), "at row offset", error.offset)
80
81 # demonstrate that all of the rows without errors have been successfully
82 # inserted
83 cursor.execute("""
84 select count(*)
85 from ChildTable""")
86 count, = cursor.fetchone()
87 print("number of rows in child table after successful insert:", int(count))
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # BindInsert.py
6 #
7 # Demonstrate how to insert a row into a table using bind variables.
8 #------------------------------------------------------------------------------
9
10 from __future__ import print_function
11
12 import cx_Oracle
13 import SampleEnv
14
15 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
16
17 rows = [ (1, "First" ),
18 (2, "Second" ),
19 (3, "Third" ),
20 (4, "Fourth" ),
21 (5, "Fifth" ),
22 (6, "Sixth" ),
23 (7, "Seventh" ) ]
24
25 cursor = connection.cursor()
26 cursor.executemany("insert into mytab(id, data) values (:1, :2)", rows)
27
28 # Don't commit - this lets us run the demo multiple times
29 #connection.commit()
30
31 # Now query the results back
32
33 for row in cursor.execute('select * from mytab'):
34 print(row)
35
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # BindQuery.py
6 #
7 # Demonstrate how to perform a simple query limiting the rows retrieved using
8 # a bind variable. Since the query that is executed is identical, no additional
9 # parsing is required, thereby reducing overhead and increasing performance. It
10 # also permits data to be bound without having to be concerned about escaping
11 # special characters or SQL injection attacks.
12 #------------------------------------------------------------------------------
13
14 from __future__ import print_function
15
16 import cx_Oracle
17 import SampleEnv
18
19 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
20
21 cursor = connection.cursor()
22 sql = 'select * from SampleQueryTab where id = :bvid'
23
24 print("Query results with id = 4")
25 for row in cursor.execute(sql, bvid = 4):
26 print(row)
27 print()
28
29 print("Query results with id = 1")
30 for row in cursor.execute(sql, bvid = 1):
31 print(row)
32 print()
33
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 # CQN.py
11 # This script demonstrates using continuous query notification in Python, a
12 # feature that is available in Oracle 11g and later. Once this script is
13 # running, use another session to insert, update or delete rows from the table
14 # cx_Oracle.TestTempTable and you will see the notification of that change.
15 #
16 # This script requires cx_Oracle 5.3 and higher.
17 #------------------------------------------------------------------------------
18
19 from __future__ import print_function
20
21 import cx_Oracle
22 import SampleEnv
23 import threading
24 import time
25
26 registered = True
27
28 def callback(message):
29 global registered
30 print("Message type:", message.type)
31 if not message.registered:
32 print("Deregistration has taken place...")
33 registered = False
34 return
35 print("Message database name:", message.dbname)
36 print("Message tranasction id:", message.txid)
37 print("Message queries:")
38 for query in message.queries:
39 print("--> Query ID:", query.id)
40 print("--> Query Operation:", query.operation)
41 for table in query.tables:
42 print("--> --> Table Name:", table.name)
43 print("--> --> Table Operation:", table.operation)
44 if table.rows is not None:
45 print("--> --> Table Rows:")
46 for row in table.rows:
47 print("--> --> --> Row RowId:", row.rowid)
48 print("--> --> --> Row Operation:", row.operation)
49 print("-" * 60)
50 print("=" * 60)
51
52 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(), events = True)
53 sub = connection.subscribe(callback = callback, timeout = 1800,
54 qos = cx_Oracle.SUBSCR_QOS_QUERY | cx_Oracle.SUBSCR_QOS_ROWIDS)
55 print("Subscription:", sub)
56 print("--> Connection:", sub.connection)
57 print("--> Callback:", sub.callback)
58 print("--> Namespace:", sub.namespace)
59 print("--> Protocol:", sub.protocol)
60 print("--> Timeout:", sub.timeout)
61 print("--> Operations:", sub.operations)
62 print("--> Rowids?:", bool(sub.qos & cx_Oracle.SUBSCR_QOS_ROWIDS))
63 queryId = sub.registerquery("select * from TestTempTable")
64 print("Registered query:", queryId)
65
66 while registered:
67 print("Waiting for notifications....")
68 time.sleep(5)
69
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 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 # DMLReturningMultipleRows.py
11 # This script demonstrates the use of DML returning with multiple rows being
12 # returned at once.
13 #
14 # This script requires cx_Oracle 6.0 and higher.
15 #------------------------------------------------------------------------------
16
17 from __future__ import print_function
18
19 import cx_Oracle
20 import datetime
21 import SampleEnv
22
23 # truncate table first so that script can be rerun
24 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
25 cursor = connection.cursor()
26 print("Truncating table...")
27 cursor.execute("truncate table TestTempTable")
28
29 # populate table with a few rows
30 for i in range(5):
31 data = (i + 1, "Test String #%d" % (i + 1))
32 print("Adding row", data)
33 cursor.execute("insert into TestTempTable values (:1, :2)", data)
34
35 # now delete them and use DML returning to return the data that was inserted
36 intCol = cursor.var(int)
37 stringCol = cursor.var(str)
38 print("Deleting data with DML returning...")
39 cursor.execute("""
40 delete from TestTempTable
41 returning IntCol, StringCol into :intCol, :stringCol""",
42 intCol = intCol,
43 stringCol = stringCol)
44 print("Data returned:")
45 for intVal, stringVal in zip(intCol.getvalue(), stringCol.getvalue()):
46 print(tuple([intVal, stringVal]))
47
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 # DRCP.py
11 # This script demonstrates the use of Database Resident Connection Pooling
12 # (DRCP) which provides a connection pool in the database server, thereby
13 # reducing the cost of creating and tearing down client connections. The pool
14 # can be started and stopped in the database by issuing the following commands
15 # in SQL*Plus:
16 #
17 # exec dbms_connection_pool.start_pool()
18 # exec dbms_connection_pool.stop_pool()
19 #
20 # Statistics regarding the pool can be acquired from the following query:
21 #
22 # select * from v$cpool_cc_stats;
23 #
24 # There is no difference in how a connection is used once it has been
25 # established.
26 #
27 # This script requires cx_Oracle 5.0 and higher.
28 #------------------------------------------------------------------------------
29
30 from __future__ import print_function
31
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 cursor = conn.cursor()
38 print("Performing query using DRCP...")
39 for row in cursor.execute("select * from TestNumbers order by IntCol"):
40 print(row)
41
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 # DatabaseChangeNotification.py
11 # This script demonstrates using database change notification in Python, a
12 # feature that is available in Oracle 10g Release 2. Once this script is
13 # running, use another session to insert, update or delete rows from the table
14 # cx_Oracle.TestTempTable and you will see the notification of that change.
15 #
16 # This script requires cx_Oracle 5.3 and higher.
17 #------------------------------------------------------------------------------
18
19 from __future__ import print_function
20
21 import cx_Oracle
22 import SampleEnv
23 import threading
24 import time
25
26 registered = True
27
28 def callback(message):
29 global registered
30 print("Message type:", message.type)
31 if not message.registered:
32 print("Deregistration has taken place...")
33 registered = False
34 return
35 print("Message database name:", message.dbname)
36 print("Message tranasction id:", message.txid)
37 print("Message tables:")
38 for table in message.tables:
39 print("--> Table Name:", table.name)
40 print("--> Table Operation:", table.operation)
41 if table.rows is not None:
42 print("--> Table Rows:")
43 for row in table.rows:
44 print("--> --> Row RowId:", row.rowid)
45 print("--> --> Row Operation:", row.operation)
46 print("-" * 60)
47 print("=" * 60)
48
49 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(), events = True)
50 sub = connection.subscribe(callback = callback, timeout = 1800,
51 qos = cx_Oracle.SUBSCR_QOS_ROWIDS)
52 print("Subscription:", sub)
53 print("--> Connection:", sub.connection)
54 print("--> Callback:", sub.callback)
55 print("--> Namespace:", sub.namespace)
56 print("--> Protocol:", sub.protocol)
57 print("--> Timeout:", sub.timeout)
58 print("--> Operations:", sub.operations)
59 print("--> Rowids?:", bool(sub.qos & cx_Oracle.SUBSCR_QOS_ROWIDS))
60 sub.registerquery("select * from TestTempTable")
61
62 while registered:
63 print("Waiting for notifications....")
64 time.sleep(5)
65
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2017, 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 # DatabaseShutdown.py
11 # This script demonstrates shutting down a database using Python. It is only
12 # possible in Oracle 10g Release 2 and higher. The connection used assumes that
13 # the environment variable ORACLE_SID has been set.
14 #
15 # This script requires cx_Oracle 4.3 and higher.
16 #------------------------------------------------------------------------------
17
18 import cx_Oracle
19
20 # need to connect as SYSDBA or SYSOPER
21 connection = cx_Oracle.connect("/", mode = cx_Oracle.SYSDBA)
22
23 # first shutdown() call must specify the mode, if DBSHUTDOWN_ABORT is used,
24 # there is no need for any of the other steps
25 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_IMMEDIATE)
26
27 # now close and dismount the database
28 cursor = connection.cursor()
29 cursor.execute("alter database close normal")
30 cursor.execute("alter database dismount")
31
32 # perform the final shutdown call
33 connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_FINAL)
34
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2017, 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 # DatabaseStartup.py
11 # This script demonstrates starting up a database using Python. It is only
12 # possible in Oracle 10g Release 2 and higher. The connection used assumes that
13 # the environment variable ORACLE_SID has been set.
14 #
15 # This script requires cx_Oracle 4.3 and higher.
16 #------------------------------------------------------------------------------
17
18 import cx_Oracle
19
20 # the connection must be in PRELIM_AUTH mode
21 connection = cx_Oracle.connect("/",
22 mode = cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
23 connection.startup()
24
25 # the following statements must be issued in normal SYSDBA mode
26 connection = cx_Oracle.connect("/", mode = cx_Oracle.SYSDBA)
27 cursor = connection.cursor()
28 cursor.execute("alter database mount")
29 cursor.execute("alter database open")
30
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # DropSamples.py
6 #
7 # Drops the database objects used for the cx_Oracle samples.
8 #------------------------------------------------------------------------------
9
10 from __future__ import print_function
11
12 import cx_Oracle
13 import SampleEnv
14
15 def DropSamples(conn):
16 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())
21
22 if __name__ == "__main__":
23 conn = cx_Oracle.connect(SampleEnv.GetSysdbaConnectString(),
24 mode = cx_Oracle.SYSDBA)
25 DropSamples(conn)
26 print("Done.")
27
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 # Editioning.py
11 # This script demonstrates the use of Edition-Based Redefinition, available
12 # in Oracle# Database 11.2 and higher. See the Oracle documentation on the
13 # subject for additional information. Adjust the contants at the top of the
14 # script for your own database as needed.
15 #
16 # This script requires cx_Oracle 5.3 and higher.
17 #------------------------------------------------------------------------------
18
19 from __future__ import print_function
20
21 import cx_Oracle
22 import SampleEnv
23 import os
24
25 # 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))
30 cursor = connection.cursor()
31 cursor.execute("""
32 create or replace function TestEditions return varchar2 as
33 begin
34 return 'Base Procedure';
35 end;""")
36 result = cursor.callfunc("TestEditions", str)
37 print("Function should return 'Base Procedure', actually returns:",
38 repr(result))
39
40 # 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))
44 cursor.execute("""
45 create or replace function TestEditions return varchar2 as
46 begin
47 return 'Edition 1 Procedure';
48 end;""")
49 result = cursor.callfunc("TestEditions", str)
50 print("Function should return 'Edition 1 Procedure', actually returns:",
51 repr(result))
52
53 # next, change the edition back to the base edition and demonstrate that the
54 # original function is being called
55 cursor.execute("alter session set edition = ORA$BASE")
56 result = cursor.callfunc("TestEditions", str)
57 print("Function should return 'Base Procedure', actually returns:",
58 repr(result))
59
60 # the edition can be set upon connection
61 connection = cx_Oracle.connect(editionConnectString,
62 edition = SampleEnv.GetEditionName().upper())
63 cursor = connection.cursor()
64 result = cursor.callfunc("TestEditions", str)
65 print("Function should return 'Edition 1 Procedure', actually returns:",
66 repr(result))
67
68 # 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))
73 cursor = connection.cursor()
74 result = cursor.callfunc("TestEditions", str)
75 print("Function should return 'Edition 1 Procedure', actually returns:",
76 repr(result))
77
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # GenericRowFactory.py
6 #
7 # Demonstrate the ability to return named tuples for all queries using a
8 # subclassed cursor and row factory.
9 #------------------------------------------------------------------------------
10
11 from __future__ import print_function
12
13 import collections
14 import cx_Oracle
15 import SampleEnv
16
17 class Connection(cx_Oracle.Connection):
18
19 def cursor(self):
20 return Cursor(self)
21
22
23 class Cursor(cx_Oracle.Cursor):
24
25 def execute(self, statement, args = None):
26 prepareNeeded = (self.statement != statement)
27 result = super(Cursor, self).execute(statement, args or [])
28 if prepareNeeded:
29 description = self.description
30 if description:
31 names = [d[0] for d in description]
32 self.rowfactory = collections.namedtuple("GenericQuery", names)
33 return result
34
35
36 # create new subclassed connection and cursor
37 connection = Connection(SampleEnv.GetMainConnectString())
38 cursor = connection.cursor()
39
40 # the names are now available directly for each query executed
41 for row in cursor.execute("select ParentId, Description from ParentTable"):
42 print(row.PARENTID, "->", row.DESCRIPTION)
43 print()
44
45 for row in cursor.execute("select ChildId, Description from ChildTable"):
46 print(row.CHILDID, "->", row.DESCRIPTION)
47 print()
48
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 # ImplicitResults.py
11 # This script demonstrates the use of the 12.1 feature that allows PL/SQL
12 # procedures to return result sets implicitly, without having to explicitly
13 # define them.
14 #
15 # This script requires cx_Oracle 5.3 and higher.
16 #------------------------------------------------------------------------------
17
18 from __future__ import print_function
19
20 import cx_Oracle
21 import SampleEnv
22
23 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
24 cursor = connection.cursor()
25
26 # use PL/SQL block to return two cursors
27 cursor.execute("""
28 declare
29 c1 sys_refcursor;
30 c2 sys_refcursor;
31 begin
32
33 open c1 for
34 select * from TestNumbers;
35
36 dbms_sql.return_result(c1);
37
38 open c2 for
39 select * from TestStrings;
40
41 dbms_sql.return_result(c2);
42
43 end;""")
44
45 # display results
46 for ix, resultSet in enumerate(cursor.getimplicitresults()):
47 print("Result Set #" + str(ix + 1))
48 for row in resultSet:
49 print(row)
50 print()
51
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 # InsertGeometry.py
11 # This script demonstrates the ability to create Oracle objects (this example
12 # uses SDO_GEOMETRY) and insert them into a table.
13 #
14 # This script requires cx_Oracle 5.3 and higher.
15 #------------------------------------------------------------------------------
16
17 from __future__ import print_function
18
19 import cx_Oracle
20 import SampleEnv
21
22 # 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()
28 obj.SDO_GTYPE = 2003
29 obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
30 obj.SDO_ELEM_INFO.extend([1, 1003, 3])
31 obj.SDO_ORDINATES = ordinateTypeObj.newobject()
32 obj.SDO_ORDINATES.extend([1, 1, 5, 7])
33 print("Created object", obj)
34
35 # create table, if necessary
36 cursor = connection.cursor()
37 cursor.execute("""
38 select count(*)
39 from user_tables
40 where table_name = 'TESTGEOMETRY'""")
41 count, = cursor.fetchone()
42 if count == 0:
43 print("Creating table...")
44 cursor.execute("""
45 create table TestGeometry (
46 IntCol number(9) not null,
47 Geometry MDSYS.SDO_GEOMETRY not null
48 )""")
49
50 # remove all existing rows and then add a new one
51 print("Removing any existing rows...")
52 cursor.execute("delete from TestGeometry")
53 print("Adding row to table...")
54 cursor.execute("insert into TestGeometry values (1, :obj)", obj = obj)
55 connection.commit()
56 print("Success!")
57
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # PLSQLCollection.py
6 #
7 # Demonstrate how to get the value of a PL/SQL collection from a stored
8 # procedure.
9 #
10 # This feature is new in cx_Oracle 5.3 and is only available in Oracle
11 # Database 12.1 and higher. The ability to get the collection as a dictionary
12 # is new in cx_Oracle 7.0.
13 #------------------------------------------------------------------------------
14
15 from __future__ import print_function
16
17 import cx_Oracle
18 import SampleEnv
19
20 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
21
22 # create new empty object of the correct type
23 # note the use of a PL/SQL type defined in a package
24 typeObj = connection.gettype("PKG_DEMO.UDT_STRINGLIST")
25 obj = typeObj.newobject()
26
27 # call the stored procedure which will populate the object
28 cursor = connection.cursor()
29 cursor.callproc("pkg_Demo.DemoCollectionOut", (obj,))
30
31 # show the indexes that are used by the collection
32 print("Indexes and values of collection:")
33 ix = obj.first()
34 while ix is not None:
35 print(ix, "->", obj.getelement(ix))
36 ix = obj.next(ix)
37 print()
38
39 # show the values as a simple list
40 print("Values of collection as list:")
41 print(obj.aslist())
42 print()
43
44 # show the values as a simple dictionary
45 print("Values of collection as dictionary:")
46 print(obj.asdict())
47 print()
48
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # PLSQLFunction.py
6 #
7 # Demonstrate how to call a PL/SQL function and get its return value.
8 #------------------------------------------------------------------------------
9
10 from __future__ import print_function
11
12 import cx_Oracle
13 import SampleEnv
14
15 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
16
17 cursor = connection.cursor()
18 res = cursor.callfunc('myfunc', int, ('abc', 2))
19 print(res)
20
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # PLSQLProcedure.py
6 #
7 # Demonstrate how to call a PL/SQL stored procedure and get the results of an
8 # OUT variable.
9 #------------------------------------------------------------------------------
10
11 from __future__ import print_function
12
13 import cx_Oracle
14 import SampleEnv
15
16 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
17
18 cursor = connection.cursor()
19 myvar = cursor.var(int)
20 cursor.callproc('myproc', (123, myvar))
21 print(myvar.getvalue())
22
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # PLSQLRecord.py
6 #
7 # Demonstrate how to bind (in and out) a PL/SQL record.
8 #
9 # This feature is new in cx_Oracle 5.3 and is only available in Oracle
10 # Database 12.1 and higher.
11 #------------------------------------------------------------------------------
12
13 from __future__ import print_function
14
15 import cx_Oracle
16 import SampleEnv
17 import datetime
18
19 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
20
21 # create new object of the correct type
22 # note the use of a PL/SQL record defined in a package
23 # a table record identified by TABLE%ROWTYPE can also be used
24 typeObj = connection.gettype("PKG_DEMO.UDT_DEMORECORD")
25 obj = typeObj.newobject()
26 obj.NUMBERVALUE = 6
27 obj.STRINGVALUE = "Test String"
28 obj.DATEVALUE = datetime.datetime(2016, 5, 28)
29 obj.BOOLEANVALUE = False
30
31 # show the original values
32 print("NUMBERVALUE ->", obj.NUMBERVALUE)
33 print("STRINGVALUE ->", obj.STRINGVALUE)
34 print("DATEVALUE ->", obj.DATEVALUE)
35 print("BOOLEANVALUE ->", obj.BOOLEANVALUE)
36 print()
37
38 # call the stored procedure which will modify the object
39 cursor = connection.cursor()
40 cursor.callproc("pkg_Demo.DemoRecordsInOut", (obj,))
41
42 # show the modified values
43 print("NUMBERVALUE ->", obj.NUMBERVALUE)
44 print("STRINGVALUE ->", obj.STRINGVALUE)
45 print("DATEVALUE ->", obj.DATEVALUE)
46 print("BOOLEANVALUE ->", obj.BOOLEANVALUE)
47 print()
48
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Query.py
6 #
7 # Demonstrate how to perform a query in different ways.
8 #------------------------------------------------------------------------------
9
10 from __future__ import print_function
11
12 import cx_Oracle
13 import SampleEnv
14
15 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
16
17 sql = """
18 select * from SampleQueryTab
19 where id < 6
20 order by id"""
21
22 print("Get all rows via iterator")
23 cursor = connection.cursor()
24 for result in cursor.execute(sql):
25 print(result)
26 print()
27
28 print("Query one row at a time")
29 cursor.execute(sql)
30 row = cursor.fetchone()
31 print(row)
32 row = cursor.fetchone()
33 print(row)
34 print()
35
36 print("Fetch many rows")
37 cursor.execute(sql)
38 res = cursor.fetchmany(numRows=3)
39 print(res)
40
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # QueryArraysize.py
6 #
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.
10 #------------------------------------------------------------------------------
11
12 from __future__ import print_function
13
14 import time
15 import cx_Oracle
16 import SampleEnv
17
18 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
19
20 start = time.time()
21
22 cursor = connection.cursor()
23 cursor.arraysize = 1000
24 cursor.execute('select * from bigtab')
25 res = cursor.fetchall()
26 # print(res) # uncomment to display the query results
27
28 elapsed = (time.time() - start)
29 print("Retrieved", len(res), "rows in", elapsed, "seconds")
30
0 This directory contains samples for cx_Oracle.
1
2 1. The schemas and SQL objects that are referenced in the samples can be
3 created by running the Python script [SetupSamples.py][1]. The script
4 requires SYSDBA privileges and will prompt for these credentials as well as
5 the names of the schemas and edition that will be created, unless a number
6 of environment variables are set as documented in the Python script
7 [SampleEnv.py][2]. Run the script using the following command:
8
9 python SetupSamples.py
10
11 Alternatively, the [SQL script][3] can be run directly via SQL\*Plus, which
12 will always prompt for the names of the schemas and edition that will be
13 created.
14
15 sqlplus sys/syspassword@hostname/servicename @sql/SetupSamples.sql
16
17 2. Run a Python script, for example:
18
19 python Query.py
20
21 3. After running cx_Oracle samples, the schemas and SQL objects can be
22 dropped by running the Python script [DropSamples.py][4]. The script
23 requires SYSDBA privileges and will prompt for these credentials as well as
24 the names of the schemas and edition that will be dropped, unless a number
25 of environment variables are set as documented in the Python script
26 [SampleEnv.py][2]. Run the script using the following command:
27
28 python DropSamples.py
29
30 Alternatively, the [SQL script][5] can be run directly via SQL\*Plus, which
31 will always prompt for the names of the schemas and edition that will be
32 dropped.
33
34 sqlplus sys/syspassword@hostname/servicename @sql/DropSamples.sql
35
36 [1]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/SetupSamples.py
37 [2]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/SampleEnv.py
38 [3]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/sql/SetupSamples.sql
39 [4]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/DropSamples.py
40 [5]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/sql/DropSamples.sql
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # RefCursor.py
6 # Demonstrates the use of REF cursors with cx_Oracle.
7 #------------------------------------------------------------------------------
8
9 from __future__ import print_function
10
11 import cx_Oracle
12 import SampleEnv
13
14 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
15 cursor = connection.cursor()
16
17 refCursor = connection.cursor()
18 cursor.callproc("myrefcursorproc", (2, 6, refCursor))
19 print("Rows between 2 and 6:")
20 for row in refCursor:
21 print(row)
22 print()
23
24 refCursor = connection.cursor()
25 cursor.callproc("myrefcursorproc", (8, 9, refCursor))
26 print("Rows between 8 and 9:")
27 for row in refCursor:
28 print(row)
29 print()
30
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 # ReturnLobsAsStrings.py
11 # Returns all CLOB values as strings and BLOB values as bytes. The
12 # performance of this technique is significantly better than fetching the LOBs
13 # and then reading the contents of the LOBs as it avoids round-trips to the
14 # database. Be aware, however, that this method requires contiguous memory so
15 # is not usable for very large LOBs.
16 #
17 # This script requires cx_Oracle 5.0 and higher.
18 #------------------------------------------------------------------------------
19
20 from __future__ import print_function
21
22 import cx_Oracle
23 import SampleEnv
24
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
33 cursor = connection.cursor()
34
35 # add some data to the tables
36 print("Populating tables with data...")
37 cursor.execute("truncate table TestClobs")
38 cursor.execute("truncate table TestBlobs")
39 longString = ""
40 for i in range(10):
41 char = chr(ord('A') + i)
42 longString += char * 25000
43 # uncomment the line below for cx_Oracle 5.3 and earlier
44 # cursor.setinputsizes(None, cx_Oracle.LONG_STRING)
45 cursor.execute("insert into TestClobs values (:1, :2)",
46 (i + 1, "STRING " + longString))
47 # uncomment the line below for cx_Oracle 5.3 and earlier
48 # cursor.setinputsizes(None, cx_Oracle.LONG_BINARY)
49 cursor.execute("insert into TestBlobs values (:1, :2)",
50 (i + 1, longString.encode("ascii")))
51 connection.commit()
52
53 # fetch the data and show the results
54 print("CLOBS returned as strings")
55 cursor.execute("""
56 select
57 IntCol,
58 ClobCol
59 from TestClobs
60 order by IntCol""")
61 for intCol, value in cursor:
62 print("Row:", intCol, "string of length", len(value))
63 print()
64 print("BLOBS returned as bytes")
65 cursor.execute("""
66 select
67 IntCol,
68 BlobCol
69 from TestBlobs
70 order by IntCol""")
71 for intCol, value in cursor:
72 print("Row:", intCol, "string of length", value and len(value) or 0)
73
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # ReturnNumbersAsDecimals.py
6 # Returns all numbers as decimals by means of an output type handler. This is
7 # needed if the full decimal precision of Oracle numbers is required by the
8 # application. See this article
9 # (http://blog.reverberate.org/2016/02/06/floating-point-demystified-part2.html)
10 # for an explanation of why decimal numbers (like Oracle numbers) cannot be
11 # represented exactly by floating point numbers.
12 #
13 # This script requires cx_Oracle 5.0 and higher.
14 #------------------------------------------------------------------------------
15
16 from __future__ import print_function
17
18 import cx_Oracle
19 import decimal
20 import SampleEnv
21
22 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
23 if defaultType == cx_Oracle.NUMBER:
24 return cursor.var(decimal.Decimal, arraysize = cursor.arraysize)
25
26 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
27 connection.outputtypehandler = OutputTypeHandler
28 cursor = connection.cursor()
29 cursor.execute("select * from TestNumbers")
30 for row in cursor:
31 print("Row:", row)
32
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
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 # RowsAsInstance.py
11 # Returns rows as instances instead of tuples. See the ceDatabase.Row class
12 # in the cx_PyGenLib project (http://cx-pygenlib.sourceforge.net) for a more
13 # advanced example.
14 #
15 # This script requires cx_Oracle 4.3 and higher.
16 #------------------------------------------------------------------------------
17
18 from __future__ import print_function
19
20 import cx_Oracle
21 import SampleEnv
22
23 class Test(object):
24
25 def __init__(self, a, b, c):
26 self.a = a
27 self.b = b
28 self.c = c
29
30 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
31 cursor = connection.cursor()
32
33 # change this to False if you want to create the table yourself using SQL*Plus
34 # and then populate it with the data of your choice
35 if True:
36 cursor.execute("""
37 select count(*)
38 from user_tables
39 where table_name = 'TESTINSTANCES'""")
40 count, = cursor.fetchone()
41 if count:
42 cursor.execute("drop table TestInstances")
43 cursor.execute("""
44 create table TestInstances (
45 a varchar2(60) not null,
46 b number(9) not null,
47 c date not null
48 )""")
49 cursor.execute("insert into TestInstances values ('First', 5, sysdate)")
50 cursor.execute("insert into TestInstances values ('Second', 25, sysdate)")
51 connection.commit()
52
53 # retrieve the data and display it
54 cursor.execute("select * from TestInstances")
55 cursor.rowfactory = Test
56 print("Rows:")
57 for row in cursor:
58 print("a = %s, b = %s, c = %s" % (row.a, row.b, row.c))
59
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
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 # ScrollableCursors.py
11 # This script demonstrates how to use scrollable cursors. These allow moving
12 # forward and backward in the result set but incur additional overhead on the
13 # server to retain this information.
14 #
15 # This script requires cx_Oracle 5.3 and higher.
16 #------------------------------------------------------------------------------
17
18 from __future__ import print_function
19
20 import cx_Oracle
21 import SampleEnv
22
23 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
24
25 # show all of the rows available in the table
26 cursor = connection.cursor()
27 cursor.execute("select * from TestStrings order by IntCol")
28 print("ALL ROWS")
29 for row in cursor:
30 print(row)
31 print()
32
33 # create a scrollable cursor
34 cursor = connection.cursor(scrollable = True)
35
36 # set array size smaller than the default (100) to force scrolling by the
37 # database; otherwise, scrolling occurs directly within the buffers
38 cursor.arraysize = 3
39 cursor.execute("select * from TestStrings order by IntCol")
40
41 # scroll to last row in the result set; the first parameter is not needed and
42 # is ignored)
43 cursor.scroll(mode = "last")
44 print("LAST ROW")
45 print(cursor.fetchone())
46 print()
47
48 # scroll to the first row in the result set; the first parameter not needed and
49 # is ignored
50 cursor.scroll(mode = "first")
51 print("FIRST ROW")
52 print(cursor.fetchone())
53 print()
54
55 # scroll to an absolute row number
56 cursor.scroll(5, mode = "absolute")
57 print("ROW 5")
58 print(cursor.fetchone())
59 print()
60
61 # scroll forward six rows (the mode parameter defaults to relative)
62 cursor.scroll(3)
63 print("SKIP 3 ROWS")
64 print(cursor.fetchone())
65 print()
66
67 # scroll backward four rows (the mode parameter defaults to relative)
68 cursor.scroll(-4)
69 print("SKIP BACK 4 ROWS")
70 print(cursor.fetchone())
71 print()
72
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # SessionCallback.py
6 #
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.
13 #
14 # This script requires cx_Oracle 7.1 and higher.
15 #------------------------------------------------------------------------------
16
17 from __future__ import print_function
18
19 import cx_Oracle
20 import SampleEnv
21
22 # define a dictionary of NLS_DATE_FORMAT formats supported by this sample
23 SUPPORTED_FORMATS = {
24 "SIMPLE" : "'YYYY-MM-DD HH24:MI'",
25 "FULL" : "'YYYY-MM-DD HH24:MI:SS'"
26 }
27
28 # define a dictionary of TIME_ZONE values supported by this sample
29 SUPPORTED_TIME_ZONES = {
30 "UTC" : "'UTC'",
31 "MST" : "'-07:00'"
32 }
33
34 # define a dictionary of keys that are supported by this sample
35 SUPPORTED_KEYS = {
36 "NLS_DATE_FORMAT" : SUPPORTED_FORMATS,
37 "TIME_ZONE" : SUPPORTED_TIME_ZONES
38 }
39
40 # define session callback
41 def InitSession(conn, requestedTag):
42
43 # display the requested and actual tags
44 print("InitSession(): requested tag=%r, actual tag=%r" % \
45 (requestedTag, conn.tag))
46
47 # tags are expected to be in the form "key1=value1;key2=value2"
48 # in this example, they are used to set NLS parameters and the tag is
49 # parsed to validate it
50 if requestedTag is not None:
51 stateParts = []
52 for directive in requestedTag.split(";"):
53 parts = directive.split("=")
54 if len(parts) != 2:
55 raise ValueError("Tag must contain key=value pairs")
56 key, value = parts
57 valueDict = SUPPORTED_KEYS.get(key)
58 if valueDict is None:
59 raise ValueError("Tag only supports keys: %s" % \
60 (", ".join(SUPPORTED_KEYS)))
61 actualValue = valueDict.get(value)
62 if actualValue is None:
63 raise ValueError("Key %s only supports values: %s" % \
64 (key, ", ".join(valueDict)))
65 stateParts.append("%s = %s" % (key, actualValue))
66 sql = "alter session set %s" % " ".join(stateParts)
67 cursor = conn.cursor()
68 cursor.execute(sql)
69
70 # assign the requested tag to the connection so that when the connection
71 # is closed, it will automatically be retagged; note that if the requested
72 # tag is None (no tag was requested) this has no effect
73 conn.tag = requestedTag
74
75
76 # 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
81 # acquire session without specifying a tag; since the session returned is
82 # newly created, the callback will be invoked but since there is no tag
83 # specified, no session state will be changed
84 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()
91
92 # acquire session, specifying a tag; since the session returned has no tag,
93 # the callback will be invoked; session state will be changed and the tag will
94 # be saved when the connection is closed
95 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()
102
103 # acquire session, specifying the same tag; since a session exists in the pool
104 # with this tag, it will be returned and the callback will not be invoked but
105 # the connection will still have the session state defined previously
106 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()
113
114 # acquire session, specifying a different tag; since no session exists in the
115 # pool with this tag, a new session will be returned and the callback will be
116 # invoked; session state will be changed and the tag will be saved when the
117 # connection is closed
118 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()
125
126 # acquire session, specifying a different tag but also specifying that a
127 # session with any tag can be acquired from the pool; a session with one of the
128 # previously set tags will be returned and the callback will be invoked;
129 # session state will be changed and the tag will be saved when the connection
130 # is closed
131 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
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # SessionCallbackPLSQL.py
6 #
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.
12 #
13 # The primary advantage to this approach over the equivalent approach shown in
14 # SessionCallback.py is when DRCP is used, as the callback is invoked on the
15 # server and no round trip is required to set state.
16 #
17 # This script requires cx_Oracle 7.1 and higher.
18 #------------------------------------------------------------------------------
19
20 from __future__ import print_function
21
22 import cx_Oracle
23 import SampleEnv
24
25 # 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")
30
31 # 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()
36
37 # acquire session without specifying a tag; the callback will not be invoked as
38 # a result and no session state will be changed
39 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()
46
47 # acquire session, specifying a tag; since the session returned has no tag,
48 # the callback will be invoked; session state will be changed and the tag will
49 # be saved when the connection is closed
50 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()
57
58 # acquire session, specifying the same tag; since a session exists in the pool
59 # with this tag, it will be returned and the callback will not be invoked but
60 # the connection will still have the session state defined previously
61 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()
68
69 # acquire session, specifying a different tag; since no session exists in the
70 # pool with this tag, a new session will be returned and the callback will be
71 # invoked; session state will be changed and the tag will be saved when the
72 # connection is closed
73 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()
80
81 # acquire session, specifying a different tag but also specifying that a
82 # session with any tag can be acquired from the pool; a session with one of the
83 # previously set tags will be returned and the callback will be invoked;
84 # session state will be changed and the tag will be saved when the connection
85 # is closed
86 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()
93
94 # 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
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # SetupSamples.py
6 #
7 # Creates users and populates their schemas with the tables and packages
8 # necessary for the cx_Oracle samples. An edition is also created for the
9 # demonstration of PL/SQL editioning.
10 #------------------------------------------------------------------------------
11
12 from __future__ import print_function
13
14 import cx_Oracle
15
16 import SampleEnv
17 import DropSamples
18
19 # connect as SYSDBA
20 conn = cx_Oracle.connect(SampleEnv.GetSysdbaConnectString(),
21 mode = cx_Oracle.SYSDBA)
22
23 # drop existing users and editions, if applicable
24 DropSamples.DropSamples(conn)
25
26 # create sample schema and edition
27 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())
34 print("Done.")
35
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # SodaBasic.py
6 # A basic Simple Oracle Document Access (SODA) example.
7 #
8 # This script requires cx_Oracle 7.0 and higher.
9 # Oracle Client must be at 18.3 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 from __future__ import print_function
15
16 import cx_Oracle
17 import SampleEnv
18
19 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
20
21 # The general recommendation for simple SODA usage is to enable autocommit
22 connection.autocommit = True
23
24 # Create the parent object for SODA
25 soda = connection.getSodaDatabase()
26
27 # Create a new SODA collection and index
28 # This will open an existing collection, if the name is already in use.
29 collection = soda.createCollection("mycollection")
30
31 indexSpec = { 'name': 'CITY_IDX',
32 'fields': [ {
33 'path': 'address.city',
34 'datatype': 'string',
35 'order': 'asc' } ] }
36 collection.createIndex(indexSpec)
37
38 # Insert a document.
39 # A system generated key is created by default.
40 content = {'name': 'Matilda', 'address': {'city': 'Melbourne'}}
41 doc = collection.insertOneAndGet(content)
42 key = doc.key
43 print('The key of the new SODA document is: ', key)
44
45 # Fetch the document back
46 doc = collection.find().key(key).getOne() # A SodaDocument
47 content = doc.getContent() # A JavaScript object
48 print('Retrieved SODA document dictionary is:')
49 print(content)
50 content = doc.getContentAsString() # A JSON string
51 print('Retrieved SODA document string is:')
52 print(content)
53
54 # Replace document contents
55 content = {'name': 'Matilda', 'address': {'city': 'Sydney'}}
56 collection.find().key(key).replaceOne(content)
57
58 # Insert some more documents without caring about their keys
59 content = {'name': 'Venkat', 'address': {'city': 'Bengaluru'}}
60 collection.insertOne(content)
61 content = {'name': 'May', 'address': {'city': 'London'}}
62 collection.insertOne(content)
63 content = {'name': 'Sally-Ann', 'address': {'city': 'San Francisco'}}
64 collection.insertOne(content)
65
66 # Find all documents with names like 'Ma%'
67 print("Names matching 'Ma%'")
68 documents = collection.find().filter({'name': {'$like': 'Ma%'}}).getDocuments()
69 for d in documents:
70 content = d.getContent()
71 print(content["name"])
72
73 # Count all documents
74 c = collection.find().count()
75 print('Collection has', c, 'documents')
76
77 # Remove documents with cities containing 'o'
78 print('Removing documents')
79 c = collection.find().filter({'address.city': {'$regex': '.*o.*'}}).remove()
80 print('Dropped', c, 'documents')
81
82 # Count all documents
83 c = collection.find().count()
84 print('Collection has', c, 'documents')
85
86 # Drop the collection
87 if collection.drop():
88 print('Collection was dropped')
89
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # SpatialToGeoPandas.py
6 # GeoPandas is a popular python library for working with geospatial data.
7 # GeoPandas extends the Pandas data analysis library with geospatial support
8 # using the Shapely library for geometry object support.
9 #
10 # See http://geopandas.org, https://pandas.pydata.org,
11 # and https://github.com/Toblerity/Shapely.
12 #
13 # This example shows how to bring geometries from Oracle Spatial (SDO_GEOMETRY
14 # data type) into GeoPandas and perform a simple spatial operation. While the
15 # spatial operation we perform in Python could have been performed in the
16 # Oracle database, this example targets use cases where Python with GeoPandas
17 # is being used to combine and work with geospatial data from numerous
18 # additional sources such as files and web services.
19 #
20 # This script requires cx_Oracle (5.3 and higher) as well as GeoPandas and its
21 # dependencies (see http://geopandas.org/install.html).
22 #------------------------------------------------------------------------------
23
24 from __future__ import print_function
25
26 import SampleEnv
27 import cx_Oracle
28 from shapely.wkb import loads
29 import geopandas as gpd
30
31 # create Oracle connection and cursor objects
32 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
33 cursor = connection.cursor()
34
35 # enable autocommit to avoid the additional round trip to the database to
36 # perform a commit; this should not be used if multiple statements must be
37 # executed for a single transaction
38 connection.autocommit = True
39
40 # define output type handler to fetch LOBs, avoiding the second round trip to
41 # 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
46
47 # drop and create table
48 print("Dropping and creating table...")
49 cursor.execute("""
50 begin
51 execute immediate 'drop table TestStates';
52 exception when others then
53 if sqlcode <> -942 then
54 raise;
55 end if;
56 end;""")
57 cursor.execute("""
58 create table TestStates (
59 state VARCHAR2(30) not null,
60 geometry SDO_GEOMETRY not null
61 )""")
62
63 # 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")
67
68 # define function for creating an SDO_GEOMETRY object
69 def CreateGeometryObj(*ordinates):
70 geometry = typeObj.newobject()
71 geometry.SDO_GTYPE = 2003
72 geometry.SDO_SRID = 8307
73 geometry.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
74 geometry.SDO_ELEM_INFO.extend([1, 1003, 1])
75 geometry.SDO_ORDINATES = ordinateTypeObj.newobject()
76 geometry.SDO_ORDINATES.extend(ordinates)
77 return geometry
78
79 # create SDO_GEOMETRY objects for three adjacent states in the USA
80 geometryNevada = CreateGeometryObj(-114.052025, 37.103989, -114.049797,
81 37.000423, -113.484375, 37, -112.898598, 37.000401,-112.539604,
82 37.000683, -112, 37.000977, -111.412048, 37.001514, -111.133018,
83 37.00079,-110.75, 37.003201, -110.5, 37.004265, -110.469505, 36.998001,
84 -110, 36.997967, -109.044571,36.999088, -109.045143, 37.375,
85 -109.042824, 37.484692, -109.040848, 37.881176, -109.041405,38.153027,
86 -109.041107, 38.1647, -109.059402, 38.275501, -109.059296, 38.5,
87 -109.058868, 38.719906,-109.051765, 39, -109.050095, 39.366699,
88 -109.050697, 39.4977, -109.050499, 39.6605, -109.050156,40.222694,
89 -109.047577, 40.653641, -109.0494, 41.000702, -109.2313, 41.002102,
90 -109.534233,40.998184, -110, 40.997398, -110.047768, 40.997696, -110.5,
91 40.994801, -111.045982, 40.998013,-111.045815, 41.251774, -111.045097,
92 41.579899, -111.045944, 42.001633, -111.506493, 41.999588,-112.108742,
93 41.997677, -112.16317, 41.996784, -112.172562, 41.996643, -112.192184,
94 42.001244,-113, 41.998314, -113.875, 41.988091, -114.040871, 41.993805,
95 -114.038803, 41.884899, -114.041306,41, -114.04586, 40.116997,
96 -114.046295, 39.906101, -114.046898, 39.542801, -114.049026, 38.67741,
97 -114.049339, 38.572968, -114.049095, 38.14864, -114.0476,
98 37.80946,-114.05098, 37.746284, -114.051666, 37.604805, -114.052025,
99 37.103989)
100 geometryWyoming = CreateGeometryObj(-111.045815, 41.251774, -111.045982,
101 40.998013, -110.5, 40.994801, -110.047768, 40.997696, -110, 40.997398,
102 -109.534233, 40.998184, -109.2313, 41.002102, -109.0494, 41.000702,
103 -108.525368, 40.999634, -107.917793, 41.002071, -107.317177, 41.002956,
104 -106.857178, 41.002697, -106.455704, 41.002167, -106.320587, 40.999153,
105 -106.189987, 40.997604, -105.729874, 40.996906, -105.276604, 40.998188,
106 -104.942848, 40.998226, -104.625, 41.00145, -104.052742, 41.001423,
107 -104.051781, 41.39333, -104.052032, 41.564301, -104.052185, 41.697983,
108 -104.052109, 42.001736, -104.052277, 42.611626, -104.052643, 43.000614,
109 -104.054337, 43.47784, -104.054298, 43.503101, -104.055, 43.8535,
110 -104.054108, 44.141102, -104.054001, 44.180401, -104.055458, 44.570877,
111 -104.057205, 44.997444, -104.664658, 44.998631, -105.037872, 45.000359,
112 -105.088867, 45.000462, -105.912819, 45.000957, -105.927612, 44.99366,
113 -106.024239, 44.993591, -106.263, 44.993801, -107.054871, 44.996384,
114 -107.133545, 45.000141, -107.911095, 45.001343, -108.248672, 44.999504,
115 -108.620628, 45.000328, -109.082314, 44.999664, -109.102745, 45.005955,
116 -109.797951, 45.002247, -110.000771, 45.003502, -110.10936, 45.003967,
117 -110.198761, 44.99625, -110.286026, 44.99691, -110.361946, 45.000656,
118 -110.402176, 44.993874, -110.5, 44.992355, -110.704506, 44.99239,
119 -110.784241, 45.003021, -111.05442, 45.001392, -111.054558, 44.666336,
120 -111.048203, 44.474144, -111.046272, 43.983456, -111.044724, 43.501213,
121 -111.043846, 43.3158, -111.043381, 43.02013, -111.042786, 42.719578,
122 -111.045967, 42.513187, -111.045944, 42.001633, -111.045097, 41.579899,
123 -111.045815, 41.251774)
124 geometryColorado = CreateGeometryObj(-109.045143, 37.375, -109.044571,
125 36.999088, -108.378571, 36.999516, -107.481133, 37, -107.420311, 37,
126 -106.876701, 37.00013, -106.869209, 36.992416, -106.475639, 36.993748,
127 -106.006058, 36.995327, -105.717834, 36.995823, -105.220055, 36.995144,
128 -105.154488, 36.995239, -105.028671, 36.992702, -104.407616, 36.993446,
129 -104.007324, 36.996216, -103.085617, 37.000244, -103.001709, 37.000084,
130 -102.986488, 36.998505, -102.759384, 37, -102.69767, 36.995132,
131 -102.041794, 36.993061, -102.041191, 37.389172, -102.04113, 37.644268,
132 -102.041695, 37.738529, -102.043938, 38.262466, -102.044113, 38.268803,
133 -102.04483, 38.615234, -102.044762, 38.697556, -102.046112, 39.047035,
134 -102.046707, 39.133144, -102.049301, 39.568176, -102.049347, 39.574062,
135 -102.051277, 40.00309, -102.051117, 40.34922, -102.051003, 40.440018,
136 -102.050873, 40.697556, -102.050835, 40.749596, -102.051155, 41.002384,
137 -102.620567, 41.002609, -102.652992, 41.002342, -103.382011, 41.00227,
138 -103.574036, 41.001736, -104.052742, 41.001423, -104.625, 41.00145,
139 -104.942848, 40.998226, -105.276604, 40.998188, -105.729874, 40.996906,
140 -106.189987, 40.997604, -106.320587, 40.999153, -106.455704, 41.002167,
141 -106.857178, 41.002697, -107.317177, 41.002956, -107.917793, 41.002071,
142 -108.525368, 40.999634, -109.0494, 41.000702, -109.047577, 40.653641,
143 -109.050156, 40.222694, -109.050499, 39.6605, -109.050697, 39.4977,
144 -109.050095, 39.366699, -109.051765, 39, -109.058868, 38.719906,
145 -109.059296, 38.5, -109.059402, 38.275501, -109.041107, 38.1647,
146 -109.041405, 38.153027, -109.040848, 37.881176, -109.042824, 37.484692,
147 -109.045143, 37.375)
148
149 # Insert rows for test states. If we were analyzing these geometries in Oracle
150 # we would also add Spatial metadata and indexes. However in this example we
151 # are only storing the geometries so that we load them back into Python, so we
152 # will skip the metadata and indexes.
153 print("Adding rows to table...")
154 data = [
155 ('Nevada', geometryNevada),
156 ('Colorado', geometryColorado),
157 ('Wyoming', geometryWyoming)
158 ]
159 cursor.executemany('insert into TestStates values (:state, :obj)', data)
160
161 # We now have test geometries in Oracle Spatial (SDO_GEOMETRY) and will next
162 # bring them back into Python to analyze with GeoPandas. GeoPandas is able to
163 # consume geometries in the Well Known Text (WKT) and Well Known Binary (WKB)
164 # formats. Oracle database includes utility functions to return SDO_GEOMETRY as
165 # both WKT and WKB. Therefore we use that utility function in the query below
166 # to provide results in a format readily consumable by GeoPandas. These utility
167 # functions were introduced in Oracle 10g. We use WKB here; however the same
168 # process applies for WKT.
169 cursor.execute("""
170 SELECT state, sdo_util.to_wkbgeometry(geometry)
171 FROM TestStates""")
172 gdf = gpd.GeoDataFrame(cursor.fetchall(), columns = ['state', 'wkbgeometry'])
173
174 # create GeoSeries to replace the WKB geometry column
175 gdf['geometry'] = gpd.GeoSeries(gdf['wkbgeometry'].apply(lambda x: loads(x)))
176 del gdf['wkbgeometry']
177
178 # display the GeoDataFrame
179 print()
180 print(gdf)
181
182 # perform a basic GeoPandas operation (unary_union)
183 # to combine the 3 adjacent states into 1 geometry
184 print()
185 print("GeoPandas combining the 3 geometries into a single geometry...")
186 print(gdf.unary_union)
187
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Subclassing.py
6 #
7 # Demonstrate how to subclass cx_Oracle connections and cursors in order to
8 # add additional functionality (like logging) or create specialized interfaces
9 # for paticular applications.
10 #------------------------------------------------------------------------------
11
12 from __future__ import print_function
13
14 import cx_Oracle
15 import SampleEnv
16
17 # sample subclassed connection which overrides the constructor (so no
18 # parameters are required) and the cursor() method (so that the subclassed
19 # cursor is returned instead of the default cursor implementation)
20 class Connection(cx_Oracle.Connection):
21
22 def __init__(self):
23 connectString = SampleEnv.GetMainConnectString()
24 print("CONNECT to database")
25 return super(Connection, self).__init__(connectString)
26
27 def cursor(self):
28 return Cursor(self)
29
30
31 # sample subclassed cursor which overrides the execute() and fetchone()
32 # methods in order to perform some simple logging
33 class Cursor(cx_Oracle.Cursor):
34
35 def execute(self, statement, args):
36 print("EXECUTE", statement)
37 print("ARGS:")
38 for argIndex, arg in enumerate(args):
39 print(" ", argIndex + 1, "=>", repr(arg))
40 return super(Cursor, self).execute(statement, args)
41
42 def fetchone(self):
43 print("FETCH ONE")
44 return super(Cursor, self).fetchone()
45
46
47 # create instances of the subclassed connection and cursor
48 connection = Connection()
49 cursor = connection.cursor()
50
51 # demonstrate that the subclassed connection and cursor are being used
52 cursor.execute("select count(*) from ChildTable where ParentId = :1", (30,))
53 count, = cursor.fetchone()
54 print("COUNT:", int(count))
55
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
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 # TransactionGuard.py
11 # This script demonstrates the use of Transaction Guard to verify if a
12 # transaction has completed, ensuring that a duplicate transaction is not
13 # created or attempted if the application chooses to handle the error. This
14 # feature is only available in Oracle Database 12.1. It follows loosely the
15 # OCI sample provided by Oracle in its documentation about OCI and Transaction
16 # Guard.
17 #
18 # Run the following as SYSDBA to set up Transaction Guard
19 #
20 # grant execute on dbms_app_cont to pythondemo;
21 #
22 # declare
23 # t_Params dbms_service.svc_parameter_array;
24 # begin
25 # t_Params('COMMIT_OUTCOME') := 'true';
26 # t_Params('RETENTION_TIMEOUT') := 604800;
27 # dbms_service.create_service('orcl-tg', 'orcl-tg', t_Params);
28 # dbms_service.start_service('orcl-tg');
29 # end;
30 # /
31 #
32 # This script requires cx_Oracle 5.3 and higher.
33 #------------------------------------------------------------------------------
34
35 from __future__ import print_function
36
37 import cx_Oracle
38 import SampleEnv
39 import sys
40
41 # constants
42 CONNECT_STRING = "localhost/orcl-tg"
43
44 # for Python 2.7 we need raw_input
45 try:
46 input = raw_input
47 except NameError:
48 pass
49
50 # 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)
54 connection = pool.acquire()
55 cursor = connection.cursor()
56 cursor.execute("""
57 delete from TestTempTable
58 where IntCol = 1""")
59 cursor.execute("""
60 insert into TestTempTable
61 values (1, null)""")
62 input("Please kill %s session now. Press ENTER when complete." % \
63 SampleEnv.GetMainUser())
64 try:
65 connection.commit() # this should fail
66 sys.exit("Session was not killed. Terminating.")
67 except cx_Oracle.DatabaseError as e:
68 errorObj, = e.args
69 if not errorObj.isrecoverable:
70 sys.exit("Session is not recoverable. Terminating.")
71 ltxid = connection.ltxid
72 if not ltxid:
73 sys.exit("Logical transaction not available. Terminating.")
74 pool.drop(connection)
75
76 # check if previous transaction completed
77 connection = pool.acquire()
78 cursor = connection.cursor()
79 _, committed, completed = cursor.callproc("dbms_app_cont.get_ltxid_outcome",
80 (cx_Oracle.Binary(ltxid), cursor.var(bool), cursor.var(bool)))
81 print("Failed transaction was committed:", committed)
82 print("Failed call was completed:", completed)
83
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 # TypeHandlers.py
11 # This script demonstrates the use of input and output type handlers as well
12 # as variable input and output converters. These methods can be used to extend
13 # cx_Oracle in many ways. This script demonstrates the binding and querying of
14 # SQL objects as Python objects.
15 #
16 # This script requires cx_Oracle 5.0 and higher.
17 #------------------------------------------------------------------------------
18
19
20 from __future__ import print_function
21
22 import cx_Oracle
23 import datetime
24 import SampleEnv
25
26 con = cx_Oracle.connect(SampleEnv.GetMainConnectString())
27 objType = con.gettype("UDT_BUILDING")
28
29 class Building(object):
30
31 def __init__(self, buildingId, description, numFloors, dateBuilt):
32 self.buildingId = buildingId
33 self.description = description
34 self.numFloors = numFloors
35 self.dateBuilt = dateBuilt
36
37 def __repr__(self):
38 return "<Building %s: %s>" % (self.buildingId, self.description)
39
40
41 def BuildingInConverter(value):
42 obj = objType.newobject()
43 obj.BUILDINGID = value.buildingId
44 obj.DESCRIPTION = value.description
45 obj.NUMFLOORS = value.numFloors
46 obj.DATEBUILT = value.dateBuilt
47 return obj
48
49
50 def BuildingOutConverter(obj):
51 return Building(int(obj.BUILDINGID), obj.DESCRIPTION, int(obj.NUMFLOORS),
52 obj.DATEBUILT)
53
54
55 def InputTypeHandler(cursor, value, numElements):
56 if isinstance(value, Building):
57 return cursor.var(cx_Oracle.OBJECT, arraysize = numElements,
58 inconverter = BuildingInConverter, typename = objType.name)
59
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)
64
65 buildings = [
66 Building(1, "The First Building", 5, datetime.date(2007, 5, 18)),
67 Building(2, "The Second Building", 87, datetime.date(2010, 2, 7)),
68 Building(3, "The Third Building", 12, datetime.date(2005, 6, 19)),
69 ]
70
71 cur = con.cursor()
72 cur.inputtypehandler = InputTypeHandler
73 for building in buildings:
74 try:
75 cur.execute("insert into TestBuildings values (:1, :2)",
76 (building.buildingId, building))
77 except cx_Oracle.DatabaseError as e:
78 error, = e.args
79 print("CONTEXT:", error.context)
80 print("MESSAGE:", error.message)
81 raise
82
83 print("NO OUTPUT TYPE HANDLER:")
84 for row in cur.execute("select * from TestBuildings order by BuildingId"):
85 print(row)
86 print()
87
88 cur = con.cursor()
89 cur.outputtypehandler = OutputTypeHandler
90 print("WITH OUTPUT TYPE HANDLER:")
91 for row in cur.execute("select * from TestBuildings order by BuildingId"):
92 print(row)
93 print()
94
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2017, 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 # UniversalRowids.py
11 # This script demonstrates the use of universal rowids. Universal rowids are
12 # used to identify rows in index organized tables.
13 #
14 # This script requires cx_Oracle 6.0 and higher.
15 #------------------------------------------------------------------------------
16
17 from __future__ import print_function
18
19 import cx_Oracle
20 import datetime
21 import SampleEnv
22
23 DATA = [
24 (1, "String #1", datetime.datetime(2017, 4, 4)),
25 (2, "String #2", datetime.datetime(2017, 4, 5)),
26 (3, "A" * 250, datetime.datetime(2017, 4, 6))
27 ]
28
29 # truncate table so sample can be rerun
30 connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
31 cursor = connection.cursor()
32 print("Truncating table...")
33 cursor.execute("truncate table TestUniversalRowids")
34
35 # populate table with a few rows
36 print("Populating table...")
37 for row in DATA:
38 print("Inserting", row)
39 cursor.execute("insert into TestUniversalRowids values (:1, :2, :3)", row)
40 connection.commit()
41
42 # fetch the rowids from the table
43 rowids = [r for r, in cursor.execute("select rowid from TestUniversalRowids")]
44
45 # fetch each of the rows given the rowid
46 for rowid in rowids:
47 print("-" * 79)
48 print("Rowid:", rowid)
49 cursor.execute("""
50 select IntCol, StringCol, DateCol
51 from TestUniversalRowids
52 where rowid = :rid""",
53 rid = rowid)
54 intCol, stringCol, dateCol = cursor.fetchone()
55 print("IntCol:", intCol)
56 print("StringCol:", stringCol)
57 print("DateCol:", dateCol)
58
0 /*-----------------------------------------------------------------------------
1 * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 *---------------------------------------------------------------------------*/
3
4 /*-----------------------------------------------------------------------------
5 * DropSamples.sql
6 * Drops database objects used for cx_Oracle samples.
7 *
8 * Run this like:
9 * sqlplus sys/syspassword@hostname/servicename as sysdba @DropSamples
10 *---------------------------------------------------------------------------*/
11
12 whenever sqlerror exit failure
13
14 -- get parameters
15 set echo off termout on feedback off verify off
16 accept main_user char default pythondemo -
17 prompt "Name of main schema [pythondemo]: "
18 accept edition_user char default pythoneditions -
19 prompt "Name of edition schema [pythoneditions]: "
20 accept edition_name char default python_e1 -
21 prompt "Name of edition [python_e1]: "
22 set feedback on
23
24 -- perform work
25 @@DropSamplesExec.sql
26
27 exit
28
0 /*-----------------------------------------------------------------------------
1 * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 *---------------------------------------------------------------------------*/
3
4 /*-----------------------------------------------------------------------------
5 * DropSamplesExec.sql
6 * This script performs the actual work of dropping the database schemas and
7 * edition used by the cx_Oracle samples. It is called by the DropSamples.sql
8 * and SetupSamples.sql files after acquiring the necessary parameters and
9 * also by the Python script DropSamples.py.
10 *---------------------------------------------------------------------------*/
11
12 begin
13
14 for r in
15 ( select username
16 from dba_users
17 where username in (upper('&main_user'), upper('&edition_user'))
18 ) loop
19 execute immediate 'drop user ' || r.username || ' cascade';
20 end loop;
21
22 for r in
23 ( select edition_name
24 from dba_editions
25 where edition_name in (upper('&edition_name'))
26 ) loop
27 execute immediate 'drop edition ' || r.edition_name || ' cascade';
28 end loop;
29
30 end;
31 /
32
0 /*-----------------------------------------------------------------------------
1 * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 *---------------------------------------------------------------------------*/
3
4 /*-----------------------------------------------------------------------------
5 * SetupSamples.sql
6 * Creates and populates schemas with the database objects used by the
7 * cx_Oracle samples. An edition is also created for the demonstration of
8 * PL/SQL editioning.
9 *
10 * Run this like:
11 * sqlplus sys/syspassword@hostname/servicename as sysdba @SetupSamples
12 *---------------------------------------------------------------------------*/
13
14 whenever sqlerror exit failure
15
16 -- get parameters
17 set echo off termout on feedback off verify off
18 accept main_user char default pythondemo -
19 prompt "Name of main schema [pythondemo]: "
20 accept main_password char prompt "Password for &main_user: " HIDE
21 accept edition_user char default pythoneditions -
22 prompt "Name of edition schema [pythoneditions]: "
23 accept edition_password char prompt "Password for &edition_user: " HIDE
24 accept edition_name char default python_e1 -
25 prompt "Name of edition [python_e1]: "
26 set feedback on
27
28 -- perform work
29 @@DropSamplesExec.sql
30 @@SetupSamplesExec.sql
31
32 exit
33
0 /*-----------------------------------------------------------------------------
1 * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 *---------------------------------------------------------------------------*/
3
4 /*-----------------------------------------------------------------------------
5 * SetupSamplesExec.sql
6 * This script performs the actual work of creating and populating the
7 * schemas with the database objects used by the cx_Oracle samples. An edition
8 * is also created for the demonstration of PL/SQL editioning. It is called by
9 * the SetupSamples.sql file after acquiring the necessary parameters and also
10 * by the Python script SetupSamples.py.
11 *---------------------------------------------------------------------------*/
12
13 alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'
14 /
15 alter session set nls_numeric_characters='.,'
16 /
17
18 create user &main_user identified by &main_password
19 quota unlimited on users
20 default tablespace users
21 /
22
23 grant
24 create session,
25 create table,
26 create procedure,
27 create type,
28 select any dictionary,
29 change notification
30 to &main_user
31 /
32
33 grant execute on dbms_aqadm to &main_user
34 /
35 grant execute on dbms_lock to &main_user
36 /
37
38 begin
39
40 for r in
41 ( select role
42 from dba_roles
43 where role in ('SODA_APP')
44 ) loop
45 execute immediate 'grant ' || r.role || ' to &main_user';
46 end loop;
47
48 end;
49 /
50
51 create user &edition_user identified by &edition_password
52 /
53
54 grant
55 create session,
56 create procedure
57 to &edition_user
58 /
59
60 alter user &edition_user enable editions
61 /
62
63 create edition &edition_name
64 /
65
66 grant use on edition &edition_name to &edition_user
67 /
68
69 -- create types
70
71 create type &main_user..udt_SubObject as object (
72 SubNumberValue number,
73 SubStringValue varchar2(60)
74 );
75 /
76
77 create or replace type &main_user..udt_Building as object (
78 BuildingId number(9),
79 NumFloors number(3),
80 Description varchar2(60),
81 DateBuilt date
82 );
83 /
84
85 create or replace type &main_user..udt_Book as object (
86 Title varchar2(100),
87 Authors varchar2(100),
88 Price number(5,2)
89 );
90 /
91
92 -- create tables
93
94 create table &main_user..TestNumbers (
95 IntCol number(9) not null,
96 NumberCol number(9, 2) not null,
97 FloatCol float not null,
98 UnconstrainedCol number not null,
99 NullableCol number(38)
100 )
101 /
102
103 create table &main_user..TestStrings (
104 IntCol number(9) not null,
105 StringCol varchar2(20) not null,
106 RawCol raw(30) not null,
107 FixedCharCol char(40) not null,
108 NullableCol varchar2(50)
109 )
110 /
111
112 create table &main_user..TestCLOBs (
113 IntCol number(9) not null,
114 CLOBCol clob not null
115 )
116 /
117
118 create table &main_user..TestBLOBs (
119 IntCol number(9) not null,
120 BLOBCol blob not null
121 )
122 /
123
124 create table &main_user..TestTempTable (
125 IntCol number(9) not null,
126 StringCol varchar2(400),
127 constraint TestTempTable_pk primary key (IntCol)
128 )
129 /
130
131 create table &main_user..TestUniversalRowids (
132 IntCol number(9) not null,
133 StringCol varchar2(250) not null,
134 DateCol date not null,
135 constraint TestUniversalRowids_pk primary key (IntCol, StringCol, DateCol)
136 ) organization index
137 /
138
139 create table &main_user..TestBuildings (
140 BuildingId number(9) not null,
141 BuildingObj &main_user..udt_Building not null
142 )
143 /
144
145 create table &main_user..BigTab (
146 mycol varchar2(20)
147 )
148 /
149
150 create table &main_user..SampleQueryTab (
151 id number not null,
152 name varchar2(20) not null
153 )
154 /
155
156 create table &main_user..MyTab (
157 id number,
158 data varchar2(20)
159 )
160 /
161
162 create table &main_user..ParentTable (
163 ParentId number(9) not null,
164 Description varchar2(60) not null,
165 constraint ParentTable_pk primary key (ParentId)
166 )
167 /
168
169 create table &main_user..ChildTable (
170 ChildId number(9) not null,
171 ParentId number(9) not null,
172 Description varchar2(60) not null,
173 constraint ChildTable_pk primary key (ChildId),
174 constraint ChildTable_fk foreign key (ParentId)
175 references &main_user..ParentTable
176 )
177 /
178
179 create table &main_user..Ptab (
180 myid number,
181 mydata varchar(20)
182 )
183 /
184
185 create table &main_user..PlsqlSessionCallbacks (
186 RequestedTag varchar2(250),
187 ActualTag varchar2(250),
188 FixupTimestamp timestamp
189 )
190 /
191
192 -- create queue table and queues for demonstrating advanced queuing
193 begin
194 dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE',
195 '&main_user..UDT_BOOK');
196 dbms_aqadm.create_queue('&main_user..BOOKS', '&main_user..BOOK_QUEUE');
197 dbms_aqadm.start_queue('&main_user..BOOKS');
198 end;
199 /
200
201 -- populate tables
202
203 begin
204 for i in 1..20000 loop
205 insert into &main_user..BigTab (mycol)
206 values (dbms_random.string('A', 20));
207 end loop;
208 end;
209 /
210
211 begin
212 for i in 1..10 loop
213 insert into &main_user..TestNumbers
214 values (i, i + i * 0.25, i + i * .75, i * i * i + i *.5,
215 decode(mod(i, 2), 0, null, power(143, i)));
216 end loop;
217 end;
218 /
219
220 declare
221
222 t_RawValue raw(30);
223
224 function ConvertHexDigit(a_Value number) return varchar2 is
225 begin
226 if a_Value between 0 and 9 then
227 return to_char(a_Value);
228 end if;
229 return chr(ascii('A') + a_Value - 10);
230 end;
231
232 function ConvertToHex(a_Value varchar2) return varchar2 is
233 t_HexValue varchar2(60);
234 t_Digit number;
235 begin
236 for i in 1..length(a_Value) loop
237 t_Digit := ascii(substr(a_Value, i, 1));
238 t_HexValue := t_HexValue ||
239 ConvertHexDigit(trunc(t_Digit / 16)) ||
240 ConvertHexDigit(mod(t_Digit, 16));
241 end loop;
242 return t_HexValue;
243 end;
244
245 begin
246 for i in 1..10 loop
247 t_RawValue := hextoraw(ConvertToHex('Raw ' || to_char(i)));
248 insert into &main_user..TestStrings
249 values (i, 'String ' || to_char(i), t_RawValue,
250 'Fixed Char ' || to_char(i),
251 decode(mod(i, 2), 0, null, 'Nullable ' || to_char(i)));
252 end loop;
253 end;
254 /
255
256 insert into &main_user..ParentTable values (10, 'Parent 10')
257 /
258 insert into &main_user..ParentTable values (20, 'Parent 20')
259 /
260 insert into &main_user..ParentTable values (30, 'Parent 30')
261 /
262 insert into &main_user..ParentTable values (40, 'Parent 40')
263 /
264 insert into &main_user..ParentTable values (50, 'Parent 50')
265 /
266
267 insert into &main_user..ChildTable values (1001, 10, 'Child A of Parent 10')
268 /
269 insert into &main_user..ChildTable values (1002, 20, 'Child A of Parent 20')
270 /
271 insert into &main_user..ChildTable values (1003, 20, 'Child B of Parent 20')
272 /
273 insert into &main_user..ChildTable values (1004, 20, 'Child C of Parent 20')
274 /
275 insert into &main_user..ChildTable values (1005, 30, 'Child A of Parent 30')
276 /
277 insert into &main_user..ChildTable values (1006, 30, 'Child B of Parent 30')
278 /
279 insert into &main_user..ChildTable values (1007, 40, 'Child A of Parent 40')
280 /
281 insert into &main_user..ChildTable values (1008, 40, 'Child B of Parent 40')
282 /
283 insert into &main_user..ChildTable values (1009, 40, 'Child C of Parent 40')
284 /
285 insert into &main_user..ChildTable values (1010, 40, 'Child D of Parent 40')
286 /
287 insert into &main_user..ChildTable values (1011, 40, 'Child E of Parent 40')
288 /
289 insert into &main_user..ChildTable values (1012, 50, 'Child A of Parent 50')
290 /
291 insert into &main_user..ChildTable values (1013, 50, 'Child B of Parent 50')
292 /
293 insert into &main_user..ChildTable values (1014, 50, 'Child C of Parent 50')
294 /
295 insert into &main_user..ChildTable values (1015, 50, 'Child D of Parent 50')
296 /
297
298 insert into &main_user..SampleQueryTab values (1, 'Anthony')
299 /
300 insert into &main_user..SampleQueryTab values (2, 'Barbie')
301 /
302 insert into &main_user..SampleQueryTab values (3, 'Chris')
303 /
304 insert into &main_user..SampleQueryTab values (4, 'Dazza')
305 /
306 insert into &main_user..SampleQueryTab values (5, 'Erin')
307 /
308 insert into &main_user..SampleQueryTab values (6, 'Frankie')
309 /
310 insert into &main_user..SampleQueryTab values (7, 'Gerri')
311 /
312
313 commit
314 /
315
316 --
317 -- For PL/SQL Examples
318 --
319
320 create or replace function &main_user..myfunc (
321 a_Data varchar2,
322 a_Id number
323 ) return number as
324 begin
325 insert into &main_user..ptab (mydata, myid) values (a_Data, a_Id);
326 return (a_Id * 2);
327 end;
328 /
329
330 create or replace procedure &main_user..myproc (
331 a_Value1 number,
332 a_Value2 out number
333 ) as
334 begin
335 a_Value2 := a_Value1 * 2;
336 end;
337 /
338
339 create or replace procedure &main_user..myrefcursorproc (
340 a_StartingValue number,
341 a_EndingValue number,
342 a_RefCursor out sys_refcursor
343 ) as
344 begin
345 open a_RefCursor for
346 select *
347 from TestStrings
348 where IntCol between a_StartingValue and a_EndingValue;
349 end;
350 /
351
352
353 --
354 -- Create package for demoing PL/SQL collections and records.
355 --
356
357 create or replace package &main_user..pkg_Demo as
358
359 type udt_StringList is table of varchar2(100) index by binary_integer;
360
361 type udt_DemoRecord is record (
362 NumberValue number,
363 StringValue varchar2(30),
364 DateValue date,
365 BooleanValue boolean
366 );
367
368 procedure DemoCollectionOut (
369 a_Value out nocopy udt_StringList
370 );
371
372 procedure DemoRecordsInOut (
373 a_Value in out nocopy udt_DemoRecord
374 );
375
376 end;
377 /
378
379 create or replace package body &main_user..pkg_Demo as
380
381 procedure DemoCollectionOut (
382 a_Value out nocopy udt_StringList
383 ) is
384 begin
385 a_Value(-1048576) := 'First element';
386 a_Value(-576) := 'Second element';
387 a_Value(284) := 'Third element';
388 a_Value(8388608) := 'Fourth element';
389 end;
390
391 procedure DemoRecordsInOut (
392 a_Value in out nocopy udt_DemoRecord
393 ) is
394 begin
395 a_Value.NumberValue := a_Value.NumberValue * 2;
396 a_Value.StringValue := a_Value.StringValue || ' (Modified)';
397 a_Value.DateValue := a_Value.DateValue + 5;
398 a_Value.BooleanValue := not a_Value.BooleanValue;
399 end;
400
401 end;
402 /
403
404 --
405 -- Create package for demoing PL/SQL session callback
406 --
407
408 create or replace package &main_user..pkg_SessionCallback as
409
410 procedure TheCallback (
411 a_RequestedTag varchar2,
412 a_ActualTag varchar2
413 );
414
415 end;
416 /
417
418 create or replace package body &main_user..pkg_SessionCallback as
419
420 type udt_Properties is table of varchar2(64) index by varchar2(64);
421
422 procedure LogCall (
423 a_RequestedTag varchar2,
424 a_ActualTag varchar2
425 ) is
426 pragma autonomous_transaction;
427 begin
428 insert into PlsqlSessionCallbacks
429 values (a_RequestedTag, a_ActualTag, systimestamp);
430 commit;
431 end;
432
433 procedure ParseProperty (
434 a_Property varchar2,
435 a_Name out nocopy varchar2,
436 a_Value out nocopy varchar2
437 ) is
438 t_Pos number;
439 begin
440 t_Pos := instr(a_Property, '=');
441 if t_Pos = 0 then
442 raise_application_error(-20000, 'Tag must contain key=value pairs');
443 end if;
444 a_Name := substr(a_Property, 1, t_Pos - 1);
445 a_Value := substr(a_Property, t_Pos + 1);
446 end;
447
448 procedure SetProperty (
449 a_Name varchar2,
450 a_Value varchar2
451 ) is
452 t_ValidValues udt_Properties;
453 begin
454 if a_Name = 'TIME_ZONE' then
455 t_ValidValues('UTC') := 'UTC';
456 t_ValidValues('MST') := '-07:00';
457 elsif a_Name = 'NLS_DATE_FORMAT' then
458 t_ValidValues('SIMPLE') := 'YYYY-MM-DD HH24:MI';
459 t_ValidValues('FULL') := 'YYYY-MM-DD HH24:MI:SS';
460 else
461 raise_application_error(-20000, 'Unsupported session setting');
462 end if;
463 if not t_ValidValues.exists(a_Value) then
464 raise_application_error(-20000, 'Unsupported session setting');
465 end if;
466 execute immediate
467 'ALTER SESSION SET ' || a_Name || '=''' ||
468 t_ValidValues(a_Value) || '''';
469 end;
470
471 procedure ParseTag (
472 a_Tag varchar2,
473 a_Properties out nocopy udt_Properties
474 ) is
475 t_PropertyName varchar2(64);
476 t_PropertyValue varchar2(64);
477 t_StartPos number;
478 t_EndPos number;
479 begin
480 t_StartPos := 1;
481 while t_StartPos < length(a_Tag) loop
482 t_EndPos := instr(a_Tag, ';', t_StartPos);
483 if t_EndPos = 0 then
484 t_EndPos := length(a_Tag) + 1;
485 end if;
486 ParseProperty(substr(a_Tag, t_StartPos, t_EndPos - t_StartPos),
487 t_PropertyName, t_PropertyValue);
488 a_Properties(t_PropertyName) := t_PropertyValue;
489 t_StartPos := t_EndPos + 1;
490 end loop;
491 end;
492
493 procedure TheCallback (
494 a_RequestedTag varchar2,
495 a_ActualTag varchar2
496 ) is
497 t_RequestedProps udt_Properties;
498 t_ActualProps udt_Properties;
499 t_PropertyName varchar2(64);
500 begin
501 LogCall(a_RequestedTag, a_ActualTag);
502 ParseTag(a_RequestedTag, t_RequestedProps);
503 ParseTag(a_ActualTag, t_ActualProps);
504 t_PropertyName := t_RequestedProps.first;
505 while t_PropertyName is not null loop
506 if not t_ActualProps.exists(t_PropertyName) or
507 t_ActualProps(t_PropertyName) !=
508 t_RequestedProps(t_PropertyName) then
509 SetProperty(t_PropertyName, t_RequestedProps(t_PropertyName));
510 end if;
511 t_PropertyName := t_RequestedProps.next(t_PropertyName);
512 end loop;
513 end;
514
515 end;
516 /
517
0 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <title>Python and Oracle Database: Scripting for the Future</title>
4 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
5
6 <link rel="stylesheet" href="resources/base.css" type="text/css"/>
7 <link rel="shortcut icon" type="image/x-icon" href="resources/favicon.ico"/>
8 </head>
9 <body bgcolor="#ffffff" text="#000000">
10
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">
14
15 <h2>Contents</h2>
16
17 <ul>
18 <li><a href="#preface" >Preface</a></li>
19 <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>
22 <ul>
23 <li><a href="#connecting">1. Connecting to Oracle</a>
24 <ul>
25 <li>1.1 Review the connection credentials</li>
26 <li>1.2 Creating a basic connection</li>
27 <li>1.3 Indentation indicates code structure</li>
28 <li>1.4 Executing a query</li>
29 <li>1.5 Closing connections</li>
30 <li>1.6 Checking versions</li>
31 </ul>
32 </li>
33 <li><a href="#pooling">2. Connection Pooling</a>
34 <ul>
35 <li>2.1 Session pooling</li>
36 <li>2.2 Session pool experiments</li>
37 <li>2.3 Creating a DRCP Connection</li>
38 <li>2.4 Session pooling and DRCP</li>
39 <li>2.5 More DRCP investigation</li>
40 </ul>
41 </li>
42 <li><a href="#fetching">3. Fetching Data</a>
43 <ul>
44 <li>3.1 A simple query</li>
45 <li>3.2 Using fetchone()</li>
46 <li>3.3 Using fetchmany()</li>
47 <li>3.4 Scrollable cursors</li>
48 <li>3.5 Tuning with arraysize</li>
49 </ul>
50 </li>
51 <li><a href="#binding">4. Binding Data</a>
52 <ul>
53 <li>4.1 Binding in queries</li>
54 <li>4.2 Binding in inserts</li>
55 <li>4.3 Batcherrors</li>
56 <li>4.4 Binding named objects</li>
57 </ul>
58 </li>
59 <li><a href="#plsql">5. PL/SQL</a>
60 <ul>
61 <li>5.1 PL/SQL functions</li>
62 <li>5.2 PL/SQL procedures</li>
63 </ul>
64 </li>
65 <li><a href="#handlers">6. Type Handlers</a>
66 <ul>
67 <li>6.1 Basic output type handler</li>
68 <li>6.2 Output type handlers and variable converters</li>
69 <li>6.3 Input type handlers</li>
70 </ul>
71 </li>
72 <li> <a href="#lobs">7. LOBs</a>
73 <ul>
74 <li>7.1 Fetching a CLOB using a locator</li>
75 <li>7.2 Fetching a CLOB as a string</li>
76 </ul>
77 </li>
78 <li> <a href="#rowfactory" >8. Rowfactory functions</a>
79 <ul>
80 <li>8.1 Rowfactory for mapping column names</li>
81 </ul>
82 </li>
83 <li><a href="#subclass" >9. Subclassing connections and cursors</a>
84 <ul>
85 <li>9.1 Subclassing connections</li>
86 <li>9.2 Subclassing cursors</li>
87 </ul>
88 </li>
89 <li><a href="#aq" >10. Advanced Queuing</a>
90 <ul>
91 <li>10.1 Message passing with Oracle Advanced Queuing</li>
92 </ul>
93 </li>
94 </ul>
95 <li><a href="#summary" >Summary</a></li>
96 <li><a href="#primer" >Appendix: Python Primer</a></li>
97 <li><a href="#resources" >Resources</a></li>
98 </ul>
99
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
142 <h2><a name="overview">Overview</a></h2>
143
144 <p>This tutorial is an introduction to using Python with Oracle
145 Database. It contains beginner and advanced material. Sections can
146 be done in any order. Choose the content that interests you and
147 your skill level.</p>
148
149 <p>Follow the steps in this document. The <code>tutorial</code>
150 directory has scripts to run and modify. The
151 <code>tutorial/solutions</code> directory has scripts with the
152 suggested code changes.</p>
153
154 <p>Use the Desktop icons to start editors and terminal windows.</p>
155
156 <p>If you are new to Python review the <a href="#primer">Appendix:
157 Python Primer</a> to gain an understanding of the language. </p>
158
159 <h2><a name="lab">Using Python cx_Oracle 7 with Oracle Database</a></h2>
160
161 <p>Python is a popular general purpose dynamic scripting language.
162 The cx_Oracle interface provides Python API to access Oracle
163 Database.</p>
164
165 <ul>
166 <li>
167 <h3><a name="connecting">1. Connecting to Oracle</a></h3>
168 <ul>
169 <li>
170 <h4>1.1 Review the connection credentials</h4>
171 <p>Review <code>db_config.py</code> and <code>db_config.sql</code> in the <code>tutorial</code> directory. These are included in other Python and SQL files in this tutorial:</p>
172 <code>db_config.py</code>
173 <pre>
174 user = "pythonhol"
175 pw = "welcome"
176 dsn = "localhost/orclpdb"
177 </pre>
178 <code>db_config.sql</code>
179 <pre>
180 def user = "pythonhol"
181 def pw = "welcome"
182 def connect_string = "localhost/orclpdb"
183 </pre>
184
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>
186
187 </li>
188
189 <li>
190 <h4>1.2 Creating a basic connection</h4>
191 <p>Review the code contained in <code>connect.py</code>:</p>
192 <pre>
193 import cx_Oracle
194 import db_config
195
196 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
197 print("Database version:", con.version)
198 </pre>
199
200 <p>The cx_Oracle module is imported to provide the API for
201 accessing the Oracle database. Many inbuilt and third party
202 modules can be included in this way in Python scripts.</p>
203
204 <p> The <code>connect()</code> method is passed the username,
205 the password and the connection string that you configured in
206 the db_config.py module. In this case, Oracle's Easy Connect connection
207 string syntax is used. It consists of the hostname of your
208 machine, <code>localhost</code>, and the database service name
209 <code>orclpdb</code>. </p>
210
211 <p>Open a command terminal and change to the <code>tutorial</code> directory:</p>
212
213 <pre><strong>cd tutorial</strong></pre>
214
215 <p>Run the Python script:</p>
216
217 <pre><strong>python connect.py</strong></pre>
218
219 <p>The version number of the database should be displayed. An
220 exception is raised if the connection fails. Adjust the username,
221 password or connect string parameters to invalid values to see the
222 exception.</p>
223
224 <p>cx_Oracle also supports "external authentication", which
225 allows connections without needing usernames and passwords
226 to be embedded in the code. Authentication would then
227 instead be performed by, for example, LDAP.</p>
228
229 </li>
230
231 <li>
232 <h4>1.3 Indentation indicates code structure</h4>
233
234 <p>There are no statement terminators or begin/end keywords
235 or braces to indicate blocks of code.</p>
236
237 <p>Open <code>connect.py</code> in an editor. Indent the
238 print statement with some spaces:</p>
239
240 <pre>
241 import cx_Oracle
242 import db_config
243
244 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
245 print("Database version:", con.version)
246 </pre>
247
248 <p>Save the script and run it again:</p>
249
250 <pre><strong>python connect.py</strong> </pre>
251
252 <p>This raises an exception about the indentation. The
253 number of spaces or tabs must be consistent in each block;
254 otherwise, the Python interpreter will either raise an
255 exception or execute code unexpectedly. </p>
256
257 <p> Python may not always be able to identify accidental
258 from deliberate indentation. <i>Check your indentation is
259 correct before running each example. Make sure to indent
260 all statement blocks equally.</i> <b>Note the sample files
261 use spaces, not tabs.</b> </p>
262
263 </li>
264
265 <li>
266 <h4>1.4 Executing a query</h4>
267
268 <p>Open <code>query.py</code> in an editor. It looks like:</p>
269
270 <pre>
271 import cx_Oracle
272 import db_config
273
274 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
275 </pre>
276
277 <p>Edit the file and add the code shown in bold below:</p>
278
279 <pre>
280 import cx_Oracle
281 import db_config
282
283 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
284
285 <strong>cur = con.cursor()
286 cur.execute("select * from dept order by deptno")
287 res = cur.fetchall()
288 for row in res:
289 print(row)</strong>
290 </pre>
291
292 <p><i>Make sure the <code>print(row)</code> line is indented. This lab uses spaces, not tabs.</i></p>
293
294 <p>The code executes a query and fetches all data.</p>
295
296 <p>Save the file and run it:</p>
297
298 <pre><strong>python query.py</strong></pre>
299
300 <p>In each loop iteration a new row is stored in
301 <code>row</code> as a Python "tuple" and is displayed.</p>
302
303 <p>Fetching data is described further in <a href="#fetching" >section 3</a>. </p>
304 </li>
305
306 <li>
307 <h4>1.5 Closing connections</h4>
308
309 <p>Connections and other resources used by cx_Oracle will
310 automatically be closed at the end of scope. This is a
311 common programming style that takes care of the correct
312 order of resource closure.</p>
313
314 <p>Resources can also be explicitly closed to free up
315 database resources if they are no longer needed. This may
316 be useful in blocks of code that remain active for some
317 time.</p>
318
319 <p>Open <code>query.py</code> in an editor and add calls to
320 close the cursor and connection like:</p>
321
322 <pre>
323 import cx_Oracle
324 import db_config
325
326 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
327
328 cur = con.cursor()
329 cur.execute("select * from dept order by deptno")
330 res = cur.fetchall()
331 for row in res:
332 print(row)
333
334 <strong>cur.close()</strong>
335 <strong>con.close()</strong>
336 </pre>
337
338 <p>Running the script completes without error:</p>
339
340 <pre><strong>python query.py</strong></pre>
341
342 <p>If you swap the order of the two <code>close()</code> calls you will see an error.</p>
343 </li>
344
345 <li>
346 <h4>1.6 Checking versions</h4>
347
348 <p>Review the code contained in <code>versions.py</code>:</p>
349
350 <pre>
351 import cx_Oracle
352 import db_config
353
354 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
355
356 print(cx_Oracle.version)</pre>
357
358 <p>Run the script:</p>
359
360 <pre><strong>python versions.py</strong></pre>
361
362 <p>This gives the version of the cx_Oracle interface.</p>
363
364 <p>Edit the file to print the version of the database, and of the Oracle client libraries used by cx_Oracle:</p>
365
366 <pre>
367 import cx_Oracle
368 import db_config
369
370 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
371
372 print(cx_Oracle.version)
373 <strong>print("Database version:", con.version)
374 print("Client version:", cx_Oracle.clientversion())</strong>
375 </pre>
376
377 <p>When the script is run, it will display:</p>
378
379 <pre>
380 7.0.0
381 Database version: 18.3.0.0.0
382 Client version: (18, 3, 0, 0, 0)
383 </pre>
384
385 <p>Note the client version is a tuple.</p>
386
387 <p>Any cx_Oracle installation can connect to older and newer
388 Oracle Database versions. By checking the Oracle Database
389 and client versions numbers, the application can make use of
390 the best Oracle features available.</p>
391
392 </li>
393
394 </ul>
395 </li>
396
397 <li><h3><a name="pooling">2. Connection Pooling</a></h3>
398
399 <ul>
400 <li> <h4>2.1 Session pooling</h4>
401
402 <p>Review the code contained in <code>connect_pool.py</code>:</p>
403 <pre>
404 import cx_Oracle
405 import threading
406 import db_config
407
408 pool = cx_Oracle.<strong>SessionPool</strong>(db_config.user, db_config.pw, db_config.dsn,
409 min = 2, max = 5, increment = 1, threaded = True)
410
411 def Query():
412 con = pool.<strong>acquire</strong>()
413 cur = con.cursor()
414 for i in range(4):
415 cur.execute("select myseq.nextval from dual")
416 seqval, = cur.fetchone()
417 print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
418
419 thread1 = threading.Thread(name='#1', target=Query)
420 thread1.start()
421
422 thread2 = threading.Thread(name='#2', target=Query)
423 thread2.start()
424
425 thread1.join()
426 thread2.join()
427
428 print("All done!")
429 </pre>
430
431 <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>
438
439 <p>The <code>def Query():</code> line creates a method that
440 is called by each thread. </p>
441
442 <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
445 iterations to query the sequence <code>myseq</code>. At the
446 end of the method, cx_Oracle will automatically close the
447 cursor and release the session back to the pool for
448 reuse.</p>
449
450 <p>The <code>seqval, = cur.fetchone()</code> line fetches a
451 row and puts the single value contained in the result tuple
452 into the variable <code>seqval</code>. Without the comma,
453 the value in <code>seqval</code> would be a tuple like
454 "<code>(1,)</code>".</p>
455
456 <p>Two threads are created, each invoking the
457 <code>Query()</code> method.</p>
458
459 <p>In a command terminal, run:</p>
460
461 <pre><strong>python connect_pool.py</strong></pre>
462
463 <p>The output shows interleaved query results as each thread fetches
464 values independently. The order of interleaving may vary from run to
465 run.</p>
466
467 </li>
468
469 <li>
470 <h4>2.2 Session pool experiments</h4>
471
472
473 <p>Review <code>connect_pool2.py</code>, which has a loop for the number
474 of threads, each iteration invoking the <code>Query()</code> method:</p>
475
476 <pre>
477 import cx_Oracle
478 import threading
479 import db_config
480
481 pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn,
482 min = 2, max = 5, increment = 1, threaded = True)
483
484 def Query():
485 con = pool.acquire()
486 cur = con.cursor()
487 for i in range(4):
488 cur.execute("select myseq.nextval from dual")
489 seqval, = cur.fetchone()
490 print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
491
492 <strong>numberOfThreads = 2
493 threadArray = []
494
495 for i in range(numberOfThreads):
496 thread = threading.Thread(name = '#' + str(i), target = Query)
497 threadArray.append(thread)
498 thread.start()
499
500 for t in threadArray:
501 t.join()</strong>
502
503 print("All done!")
504 </pre>
505
506 <p>In a command terminal, run:</p>
507
508 <pre><strong>python connect_pool2.py</strong></pre>
509
510 <p>Experiment with different values of the pool parameters and
511 <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 =
516 cx_Oracle.SPOOL_ATTRVAL_WAIT</code> to the
517 <code>cx_Oracle.SessionPool()</code> call will prevent the exception
518 from taking place, but will cause the thread to wait until a session
519 is available.</p>
520
521 <p>Pool configurations where <code>min</code> is the same as
522 <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>
525
526 </li>
527
528 <li>
529 <h4>2.3 Creating a DRCP Connection</h4>
530
531 <p>Database Resident Connection Pooling allows multiple Python
532 processes on multiple machines to share a small pool of database
533 server processes.</p>
534
535 <p>Below left is a diagram without DRCP. Every application
536 connection or session has its own 'dedicated' database server
537 process. Application connect and close calls require the expensive
538 create and destroy of those database server processes. To avoid these
539 costs, scripts may hold connections open even when not doing
540 database work: these idle server processes consumes database host
541 resources. Below right is a diagram with DRCP. Scripts can use
542 database servers from a precreated pool of servers and return them
543 when they are not in use. </p>
544
545 <table cellspacing="0" cellpadding="30" border="0" >
546 <tr>
547 <td>
548 <img width="400" src="resources/python_nopool.png" >
549 <p><center><strong>Without DRCP</strong></center></p>
550 </td>
551 <td>
552 <img width="400" src="resources/python_pool.png" >
553 <p><center><strong>With DRCP</strong></center></p>
554 </td>
555 </tr>
556 </table>
557
558 <p>DRCP is useful when the database host machine does not have
559 enough memory to handled the number of database server processes
560 required. However, if database host memory is large enough, then
561 the default, 'dedicated' server process model is generally
562 recommended. If DRCP is enabled, it is best used in conjunction
563 with cx_Oracle session pooling.</p>
564
565 <p>Batch scripts doing long running jobs should generally use
566 dedicated connections. Both dedicated and DRCP servers can be used
567 in the same database for different applications.</p>
568
569 <p>Review the code contained in <code>connect_drcp.py</code>:</p>
570
571 <pre>
572 import cx_Oracle
573 import db_config
574
575 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn + "<strong>:pooled</strong>",
576 cclass=&quot;PYTHONHOL&quot;, purity=cx_Oracle.ATTR_PURITY_SELF)
577 print("Database version:", con.version)
578 </pre>
579
580 <p> This is similar to <code>connect.py</code> but
581 &quot;<code>:pooled</code>&quot; is appended to the connection
582 string, telling the database to use a pooled server. A Connection Class
583 "PYTHONHOL" is also passed into the <code>connect()</code> method to
584 allow grouping of database servers to applications. </p>
585
586 <p> The &quot;purity&quot; of the connection is defined as the
587 <code>ATTR_PURITY_SELF</code> constant, meaning the session state
588 (such as the default date format) might be retained between
589 connection calls, giving performance benefits. Session information
590 will be discarded if a pooled server is later reused by an
591 application with a different connection class name.</p>
592
593 <p>Applications that should never share session information should
594 use a different connection class and/or use
595 <code>ATTR_PURITY_NEW</code> to force creation of a new
596 session. This reduces overall scalability but prevents applications
597 mis-using session information.</p>
598
599 <p>Run <code>connect_drcp.py </code>in a terminal window.</p>
600
601 <pre><strong>python connect_drcp.py</strong></pre>
602
603 <p>The output is simply the version of the database.</p>
604
605 </li>
606
607 <li>
608 <h4>2.4 Session pooling and DRCP</h4>
609
610 <p>DRCP works well with session pooling.</p>
611
612 <p>Edit <code>connect_pool2.py</code>, reset any changed pool options, and modify it to use DRCP:</p>
613 <pre>
614 import cx_Oracle
615 import threading
616
617 pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn <strong>+ ":pooled"</strong>,
618 min = 2, max = 5, increment = 1, threaded = True)
619
620 def Query():
621 con = pool.acquire(<strong>cclass = "PYTHONHOL", purity = cx_Oracle.ATTR_PURITY_SELF</strong>)
622 cur = conn.cursor()
623 for i in range(4):
624 cur.execute("select myseq.nextval from dual")
625 seqval, = cur.fetchone()
626 print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
627
628 numberOfThreads = 2
629 threadArray = []
630
631 for i in range(numberOfThreads):
632 thread = threading.Thread(name = '#' + str(i), target = Query)
633 threadArray.append(thread)
634 thread.start()
635
636 for t in threadArray:
637 t.join()
638
639 print("All done!")
640 </pre>
641
642 <p>The script logic does not need to be changed to benefit from
643 DRCP connection pooling.</p>
644
645 <p>Run the script:</p>
646
647 <pre><strong>python connect_pool2.py</strong></pre>
648
649 <p>If you get the error "ORA-24418: Cannot open further
650 sessions", it is because connection requests are being made
651 while the pool is starting or growing. Add the argument
652 <code>getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT</code> to the
653 <code>cx_Oracle.SessionPool()</code> call so connection
654 requests wait for pooled sessions to be available.</p>
655
656 <p>Open a new a terminal window and invoke SQL*Plus:</p>
657
658 <pre><strong>sqlplus /nolog @drcp_query.sql</strong></pre>
659
660 <p>This shows the number of connection requests made to the pool
661 since the database was started ("NUM_REQUESTS"), how many of those
662 reused a pooled server's session ("NUM_HITS"), and how many had to
663 create new sessions ("NUM_MISSES"). Typically the goal is a low
664 number of misses.</p>
665
666 <p>To see the pool configuration you can query DBA_CPOOL_INFO.</p>
667 </li>
668
669 <li>
670 <h4>2.5 More DRCP investigation</h4>
671
672 <p>To explore the behaviors of session and DRCP pooling futher,
673 you could try changing the purity to
674 <code>cx_Oracle.ATTR_PURITY_NEW</code> to see the effect on the
675 DRCP NUM_MISSES statistic.</p>
676
677 <p>Another experiement is to include the <code>time</code> module at the file
678 top:</p>
679
680 <pre>
681 import time</pre>
682
683 <p>and add calls to <code>time.sleep(1)</code> in the code, for
684 example in the query loop. Then look at the way the threads execute. Use
685 <code>drcp_query.sql</code> to monitor the pool's behavior.</p>
686
687
688 </li>
689 </ul>
690
691 <li><h3><a name="fetching">3. Fetching Data</a> </h3>
692
693 <ul>
694 <li><h4>3.1 A simple query</h4>
695
696 <p>There are a number of functions you can use to query an Oracle
697 database, but the basics of querying are always the same:</p>
698
699 <p>1. Parse the statement for execution.<br />
700 2. Bind data values (optional).<br />
701 3. Execute the statement.<br />
702 4. Fetch the results from the database.</p>
703
704 <p>Review the code contained in <code>query2.py</code>:</p>
705
706 <pre>
707 import cx_Oracle
708 import db_config
709
710 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
711
712 cur = con.cursor()
713 cur.execute("select * from dept order by deptno")
714 for deptno, dname, loc in cur:
715 print("Department number: ", deptno)
716 print("Department name: ", dname)
717 print("Department location:", loc)
718 </pre>
719
720 <p>The <code>cursor()</code> method opens a cursor for statements to use.</p>
721
722 <p>The <code>execute()</code> method parses and executes the statement.</p>
723
724 <p>The loop fetches each row from the cursor and unpacks the returned
725 tuple into the variables <code>deptno</code>, <code>dname</code>,
726 <code>loc</code>, which are then printed.</p>
727
728 <p>Run the script in a terminal window:</p>
729
730 <pre><strong>python query2.py</strong></pre>
731
732 </li>
733
734 <li><h4>3.2 Using fetchone()</h3>
735
736 <p>When the number of rows is large, the <code>fetchall()</code>
737 call may use too much memory.</p>
738
739 <p>Review the code contained in <code>query_one.py</code>:</p>
740
741 <pre>
742 import cx_Oracle
743 import db_config
744
745 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
746 cur = con.cursor()
747
748 cur.execute("select * from dept order by deptno")
749 row = cur.fetchone()
750 print(row)
751
752 row = cur.fetchone()
753 print(row)
754 </pre>
755
756 <p>This uses the <code>fetchone()</code> method to return just a single row as a
757 tuple. When called multiple time, consecutive rows are returned:</p>
758
759 <p>Run the script in a terminal window:</p>
760
761 <pre><strong>python query_one.py</strong></pre>
762
763 <p>The first two rows of the table are printed.</p>
764
765 </li>
766
767 <li><h4>3.3 Using fetchmany()</h4>
768
769 <p>Review the code contained in <code>query_many.py</code>:</p>
770
771 <pre>
772 import cx_Oracle
773 import db_config
774
775 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
776 cur = con.cursor()
777
778 cur.execute("select * from dept order by deptno")
779 res = cur.fetchmany(numRows = 3)
780 print(res)
781 </pre>
782
783 <p>The <code>fetchmany()</code> method returns a list of tuples. By
784 default the number of rows returned is specified by the cursor
785 attribute <code>arraysize</code> (which defaults to 100). Here the
786 <code>numRows</code> parameter specifies that three rows should be
787 returned.</p>
788
789 <p>Run the script in a terminal window:</p>
790
791 <pre><strong>python query_many.py</strong></pre>
792
793 <p>The first three rows of the table are returned as a list
794 (Python's name for an array) of tuples.</p>
795
796 <p>You can access elements of the lists by position indexes. To see this,
797 edit the file and add:</p>
798
799 <pre>
800 <strong>print(res[0])</strong> # first row
801 <strong>print(res[0][1])</strong> # second element of first row
802 </pre>
803
804 </li>
805
806 <li><h4>3.4 Scrollable cursors</h4>
807
808 <p>Scrollable cursors enable the application to move backwards as
809 well as forwards in query results. They can be used to skip rows
810 as well as move to a particular row.</p>
811
812 <p>Review the code contained in <code>query_scroll.py</code>:</p>
813
814 <pre>
815 import cx_Oracle
816 import db_config
817
818 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
819 cur = con.cursor(scrollable = True)
820
821 cur.execute("select * from dept order by deptno")
822
823 cur.scroll(2, mode = "absolute") # go to second row
824 print(cur.fetchone())
825
826 cur.scroll(-1) # go back one row
827 print(cur.fetchone())
828 </pre>
829
830 <p>Run the script in a terminal window:</p>
831
832 <pre><strong>python query_scroll.py</strong></pre>
833
834 <p>Edit <code>query_scroll.py</code> and experiment with different
835 scroll options and orders, such as:</p>
836
837 <pre>cur.scroll(1) # go to next row
838 print(cur.fetchone())
839
840 cur.scroll(mode = "first") # go to first row
841 print(cur.fetchone())
842 </pre>
843
844 <p>Try some scroll options that go beyond the number of rows in
845 the resultset.</p>
846
847 </li>
848
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>
854
855 <p>First, create a table with a large number of rows.
856 Review <code>query_arraysize.sql</code>:</p>
857
858 <pre>
859 create table bigtab (mycol varchar2(20));
860 begin
861 for i in 1..20000
862 loop
863 insert into bigtab (mycol) values (dbms_random.string('A',20));
864 end loop;
865 end;
866 /
867 show errors
868
869 commit;
870 </pre>
871
872 <p>In a terminal window run the script as:</p>
873
874 <pre><strong>sqlplus /nolog @query_arraysize.sql</strong></pre>
875
876
877 <p>Review the code contained in <code>query_arraysize.py</code>:</p>
878
879 <pre>
880 import cx_Oracle
881 import time
882 import db_config
883
884 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
885
886 start = time.time()
887
888 cur = con.cursor()
889 cur.arraysize = 10
890 cur.execute("select * from bigtab")
891 res = cur.fetchall()
892 # print(res) # uncomment to display the query results
893
894 elapsed = (time.time() - start)
895 print(elapsed, "seconds")
896 </pre>
897
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>
907
908 <p>In a terminal window, run:</p>
909
910 <pre><strong>python query_arraysize.py</strong></pre>
911
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>
916
917 <pre>cur.arraysize = <strong>2000</strong></pre>
918
919 <p>Rerun the script to compare the performance of different
920 arraysize settings.</p>
921
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>
931
932 <p>If you know a query only returns a few records,
933 decrease the arraysize from the default to reduce memory
934 usage.</p>
935
936 </ul>
937
938 </li>
939
940 <li><h3><a name="binding">4. Binding Data</a></h3>
941
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>
946
947 <ul>
948
949 <li><h4>4.1 Binding in queries</h3>
950
951 <p>Review the code contained in <code>bind_query.py</code>:</p>
952
953 <pre>
954 import cx_Oracle
955 import db_config
956
957 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
958 cur = con.cursor()
959
960 cur.prepare("select * from dept where deptno = :id order by deptno")
961
962 cur.execute(None, id = 20)
963 res = cur.fetchall()
964 print(res)
965
966 cur.execute(None, id = 10)
967 res = cur.fetchall()
968 print(res)
969 </pre>
970
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>
985
986 <p>From a terminal window, run:</p>
987
988 <pre><strong>python bind_query.py</strong></pre>
989
990 <p>The output shows the details for the two departments.</p>
991
992 </li>
993 <li><h4>4.2 Binding in inserts</h3>
994
995 <p>Review the code in <code>bind_insert.sql </code> creating a table
996 for inserting data:</p>
997
998 <pre>
999 create table mytab (id number, data varchar2(20), constraint my_pk primary key (id));
1000 </pre>
1001
1002 <p>Run the script as:</p>
1003
1004 <pre><strong>sqlplus /nolog @bind_insert.sql</strong></pre>
1005
1006 <p>Review the code contained in <code>bind_insert.py</code>:</p>
1007
1008 <pre>
1009 import cx_Oracle
1010 import db_config
1011
1012 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1013 cur = con.cursor()
1014
1015 rows = [ (1, &quot;First&quot; ), (2, &quot;Second&quot; ),
1016 (3, &quot;Third&quot; ), (4, &quot;Fourth&quot; ),
1017 (5, &quot;Fifth&quot; ), (6, &quot;Sixth&quot; ),
1018 (7, &quot;Seventh&quot; ) ]
1019
1020 cur.executemany(&quot;insert into mytab(id, data) values (:1, :2)", rows)
1021
1022 # Now query the results back
1023
1024 cur2 = con.cursor()
1025 cur2.execute('select * from mytab')
1026 res = cur2.fetchall()
1027 print(res)
1028 </pre>
1029
1030 <p>The '<code>rows</code>' array contains the data to be inserted.</p>
1031
1032 <p>The <code>executemany()</code> call inserts all rows. This
1033 calls allows "array binding", which is an efficient way to
1034 insert multiple records.</p>
1035
1036 <p>The final part of the script queries the results back and displays them as a list of tuples.</p>
1037
1038 <p>From a terminal window, run:</p>
1039
1040 <pre><strong>python bind_insert.py</strong></pre>
1041
1042 <p>The new results are automatically rolled back at the end of
1043 the script so re-running it will always show the same number of
1044 rows in the table.</p>
1045
1046 </li>
1047
1048 <li><h4>4.3 Batcherrors</h3>
1049
1050 <p>The Batcherrors features allows invalid data to be identified
1051 while allowing valid data to be inserted.</p>
1052
1053 <p>Edit the data values in <code>bind_insert.py</code> and
1054 create a row with a duplicate key:</p>
1055
1056 <pre>
1057 rows = [ (1, &quot;First&quot; ), (2, &quot;Second&quot; ),
1058 (3, &quot;Third&quot; ), (4, &quot;Fourth&quot; ),
1059 (5, &quot;Fifth&quot; ), (6, &quot;Sixth&quot; ),
1060 <strong>(6, &quot;Duplicate&quot; ),</strong>
1061 (7, &quot;Seventh&quot; ) ]
1062
1063 </pre>
1064
1065 <p>From a terminal window, run:</p>
1066
1067 <pre><strong>python bind_insert.py</strong></pre>
1068
1069 <p>The duplicate generates the error "ORA-00001: unique
1070 constraint (PYTHONHOL.MY_PK) violated". The data is rolled back
1071 and the query returns no rows.</p>
1072
1073 <p>Edit the file again and enable <code>batcherrors</code> like:</p>
1074
1075 <pre>
1076 import cx_Oracle
1077 import db_config
1078
1079 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1080 cur = con.cursor()
1081
1082 rows = [ (1, &quot;First&quot; ), (2, &quot;Second&quot; ),
1083 (3, &quot;Third&quot; ), (4, &quot;Fourth&quot; ),
1084 (5, &quot;Fifth&quot; ), (6, &quot;Sixth&quot; ),
1085 <strong>(6, &quot;Duplicate&quot; ),</strong>
1086 (7, &quot;Seventh&quot; ) ]
1087
1088 cur.executemany("insert into mytab(id, data) values (:1, :2)", rows<strong>, batcherrors = True</strong>)
1089
1090 <strong>for error in cur.getbatcherrors():
1091 print("Error", error.message.rstrip(), "at row offset", error.offset)</strong>
1092
1093 # Now query the results back
1094
1095 cur2 = con.cursor()
1096 cur2.execute('select * from mytab')
1097 res = cur2.fetchall()
1098 print(res)
1099 </pre>
1100
1101 <p>Run the file:</p>
1102
1103 <pre><strong>python bind_insert.py</strong></pre>
1104
1105 <p>The new code shows the offending duplicate row: "ORA-00001:
1106 unique constraint (PYTHONHOL.MY_PK) violated at row offset 6".
1107 This indicates the 6th data value (counting from 0) had a
1108 problem.</p>
1109
1110 <p>The other data gets inserted and is queried back.</p>
1111
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>
1113
1114 <pre>con.commit()</pre>
1115
1116 <p>To force a rollback in cx_Oracle, use:</p>
1117
1118 <pre>con.rollback()</pre>
1119
1120 </li>
1121
1122 <li><h4>4.4 Binding named objects</h3>
1123
1124 <p>cx_Oracle can fetch and bind named object types such as Oracle's
1125 Spatial Data Objects (SDO).</p>
1126
1127 <p>In a terminal window, start SQL*Plus using the lab credentials and connection string, such as:</p>
1128
1129 <pre>
1130 sqlplus pythonhol/welcome@localhost/orclpdb
1131 </pre>
1132
1133 <p>Use the SQL*Plus DESCRIBE command to look at the SDO definition:</p>
1134
1135 <pre>
1136 desc MDSYS.SDO_GEOMETRY
1137 </pre>
1138
1139 <p>It contains various attributes and methods. The top level description is:</p>
1140
1141 <pre>
1142 Name Null? Type
1143 ----------------------------------------- -------- ----------------------------
1144 SDO_GTYPE NUMBER
1145 SDO_SRID NUMBER
1146 SDO_POINT MDSYS.SDO_POINT_TYPE
1147 SDO_ELEM_INFO MDSYS.SDO_ELEM_INFO_ARRAY
1148 SDO_ORDINATES MDSYS.SDO_ORDINATE_ARRAY
1149 </pre>
1150
1151 <p>Review the code contained in <code>bind_sdo.py</code>:</p>
1152
1153 <pre>
1154 import cx_Oracle
1155 import db_config
1156
1157 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1158 cur = con.cursor()
1159
1160 # Create table
1161 cur.execute("""begin
1162 execute immediate 'drop table testgeometry';
1163 exception when others then
1164 if sqlcode <> -942 then
1165 raise;
1166 end if;
1167 end;""")
1168 cur.execute("""create table testgeometry (
1169 id number(9) not null,
1170 geometry MDSYS.SDO_GEOMETRY not null)""")
1171
1172 # Create and populate Oracle objects
1173 typeObj = con.<strong>gettype</strong>("MDSYS.SDO_GEOMETRY")
1174 elementInfoTypeObj = con.<strong>gettype</strong>("MDSYS.SDO_ELEM_INFO_ARRAY")
1175 ordinateTypeObj = con.<strong>gettype</strong>("MDSYS.SDO_ORDINATE_ARRAY")
1176 obj = typeObj.<strong>newobject()</strong>
1177 obj.SDO_GTYPE = 2003
1178 obj.SDO_ELEM_INFO = elementInfoTypeObj.<strong>newobject()</strong>
1179 obj.SDO_ELEM_INFO.<strong>extend</strong>([1, 1003, 3])
1180 obj.SDO_ORDINATES = ordinateTypeObj.<strong>newobject()</strong>
1181 obj.SDO_ORDINATES.<strong>extend</strong>([1, 1, 5, 7])
1182 print("Created object", obj)
1183
1184 # Add a new row
1185 print("Adding row to table...")
1186 cur.execute("insert into testgeometry values (1, :objbv)", objbv = obj)
1187 print("Row added!")
1188
1189 # Query the row
1190 print("Querying row just inserted...")
1191 cur.execute("select id, geometry from testgeometry");
1192 for row in cur:
1193 print(row)
1194 </pre>
1195
1196 <p>This uses <code>gettype()</code> to get the database types of the
1197 SDO and its object attributes. The <code>newobject()</code> calls
1198 create Python representations of those objects. The python object
1199 atributes are then set. Oracle VARRAY types such as
1200 SDO_ELEM_INFO_ARRAY are set with <code>extend()</code>.</p>
1201
1202 <p>Run the file:</p>
1203
1204 <pre><strong>python bind_sdo.py</strong></pre>
1205
1206 <p>The new SDO is shown as an object, similar to:</p>
1207
1208 <pre>(1, &lt;cx_Oracle.Object MDSYS.SDO_GEOMETRY at 0x104a76230&gt;)</pre>
1209
1210 <p>To show the attribute values, edit the the query code section at
1211 the end of the file. Add a new method that traverses the object. The
1212 file below the existing comment "<code># (Change below here)</code>")
1213 should look like:</p>
1214
1215 <pre>
1216 # (Change below here)
1217
1218 # Define a function to dump the contents of an Oracle object
1219 def dumpobject(obj, prefix = " "):
1220 if obj.type.iscollection:
1221 print(prefix, "[")
1222 for value in obj.aslist():
1223 if isinstance(value, cx_Oracle.Object):
1224 dumpobject(value, prefix + " ")
1225 else:
1226 print(prefix + " ", repr(value))
1227 print(prefix, "]")
1228 else:
1229 print(prefix, "{")
1230 for attr in obj.type.attributes:
1231 value = getattr(obj, attr.name)
1232 if isinstance(value, cx_Oracle.Object):
1233 print(prefix + " " + attr.name + " :")
1234 dumpobject(value, prefix + " ")
1235 else:
1236 print(prefix + " " + attr.name + " :", repr(value))
1237 print(prefix, "}")
1238
1239 # Query the row
1240 print("Querying row just inserted...")
1241 cur.execute("select id, geometry from testgeometry")
1242 for id, obj in cur:
1243 print("Id: ", id)
1244 dumpobject(obj)
1245 </pre>
1246
1247 <p>Run the file again:</p>
1248
1249 <pre><strong>python bind_sdo.py</strong></pre>
1250
1251 <p>This shows</p>
1252 <pre>
1253 Querying row just inserted...
1254 Id: 1
1255 {
1256 SDO_GTYPE : 2003
1257 SDO_SRID : None
1258 SDO_POINT : None
1259 SDO_ELEM_INFO :
1260 [
1261 1
1262 1003
1263 3
1264 ]
1265 SDO_ORDINATES :
1266 [
1267 1
1268 1
1269 5
1270 7
1271 ]
1272 }
1273 </pre>
1274
1275 <p>To explore further, try setting the SDO attribute SDO_POINT, which
1276 is of type SDO_POINT_TYPE.</p>
1277
1278 <p>The <code>gettype()</code> and <code>newobject()</code> methods can
1279 also be used to bind PL/SQL Records and Collections.</p>
1280
1281 </li>
1282 </ul>
1283
1284 </li>
1285
1286 <li><h3><a name="plsql">5. PL/SQL</a></h3>
1287
1288 <p>PL/SQL is Oracle's procedural language extension to SQL. PL/SQL
1289 procedures and functions are stored and run in the database. Using
1290 PL/SQL lets all database applications reuse logic, no matter how the
1291 application accesses the database. Many data-related operations can
1292 be performed in PL/SQL faster than extracting the data into a
1293 program (for example, Python) and then processing it.</p>
1294
1295 <ul>
1296 <li><h4>5.1 PL/SQL functions</h3>
1297
1298 <p>Review <code>plsql_func.sql</code> which creates a PL/SQL
1299 stored function <code>myfunc()</code> to insert a row into a new
1300 table named <b>ptab</b> and return double the inserted
1301 value:</p>
1302
1303 <pre>
1304 create table ptab (mydata varchar(20), myid number);
1305
1306 create or replace function myfunc(d_p in varchar2, i_p in number) return number as
1307 begin
1308 insert into ptab (mydata, myid) values (d_p, i_p);
1309 return (i_p * 2);
1310 end;
1311 /
1312 </pre>
1313
1314 <p>Run the script using: </p>
1315
1316 <pre><strong>sqlplus /nolog @plsql_func.sql</strong></pre>
1317
1318 <p>Review the code contained in <code>plsql_func.py</code>:</p>
1319
1320 <pre>
1321 import cx_Oracle
1322 import db_config
1323
1324 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1325 cur = con.cursor()
1326
1327 res = cur.callfunc('myfunc', int, ('abc', 2))
1328 print(res)
1329 </pre>
1330
1331 <p>This uses <code>callfunc()</code> to execute the function.
1332 The second parameter is the type of the returned value. It should be one
1333 of the types supported by cx_Oracle or one of the type constants defined
1334 by cx_Oracle (such as cx_Oracle.NUMBER). The two PL/SQL function
1335 parameters are passed as a tuple, binding them to the function parameter
1336 arguments.</p>
1337
1338 <p>From a terminal window, run:</p>
1339
1340 <pre><strong>python plsql_func.py</strong></pre>
1341
1342 <p>The output is a result of the PL/SQL function calculation.</p>
1343
1344 </li>
1345
1346 <li><h4>5.2 PL/SQL procedures</h3>
1347
1348 <p>Review <code>plsql_proc.sql</code> which creates a PL/SQL procedure
1349 <code>myproc()</code> to accept two parameters. The second parameter
1350 contains an OUT return value. </p>
1351
1352 <pre>
1353 create or replace procedure myproc(v1_p in number, v2_p out number) as
1354 begin
1355 v2_p := v1_p * 2;
1356 end;
1357 /
1358 </pre>
1359
1360 <p>Run the script with:</p>
1361
1362 <pre><strong>sqlplus /nolog @plsql_proc.sql</strong></pre>
1363
1364 <p>Review the code contained in <code>plsql_proc.py</code>:</p>
1365
1366 <pre>
1367 import cx_Oracle
1368 import db_config
1369
1370 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1371 cur = con.cursor()
1372
1373 myvar = cur.var(int)
1374 cur.callproc('myproc', (123, myvar))
1375 print(myvar.getvalue())
1376 </pre>
1377
1378 <p>This creates an integer variable <code>myvar</code> to hold
1379 the value returned by the PL/SQL OUT parameter. The input number
1380 123 and the output variable name are bound to the procedure call
1381 parameters using a tuple.</p>
1382
1383 <p>To call the PL/SQL procedure, the <code>callproc()</code>
1384 method is used.</p>
1385
1386 <p>In a terminal window, run:</p>
1387
1388 <pre><strong>python plsql_proc.py</strong></pre>
1389
1390 <p>The <code>getvalue()</code> method displays the returned
1391 value.</p>
1392 </li>
1393 </ul>
1394 </li>
1395
1396 <li><h3><a name="handlers">6. Type Handlers</a></h3>
1397
1398 <ul>
1399 <li>
1400 <h4>6.1 Basic output type handler</h4>
1401
1402 <p>Output type handlers enable applications to change how data
1403 is fetched from the database. For example, numbers can be
1404 returned as strings or decimal objects. LOBs can be returned as
1405 string or bytes.</p>
1406
1407 <p>A type handler is enabled by setting the
1408 <code>outputtypehandler</code> attribute on either a cursor or
1409 the connection. If set on a cursor it only affects queries executed
1410 by that cursor. If set on a connection it affects all queries executed
1411 on cursors created by that connection.</p>
1412
1413 <p>Review the code contained in <code>type_output.py</code>:</p>
1414
1415 <pre>
1416 import cx_Oracle
1417 import db_config
1418
1419 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1420 cur = con.cursor()
1421
1422 print("Standard output...")
1423 for row in cur.execute("select * from dept"):
1424 print(row)
1425 </pre>
1426
1427 <p>In a terminal window, run:</p>
1428
1429 <pre><strong>python type_output.py</strong></pre>
1430
1431 <p>This shows the department number represented as digits like
1432 <code>10</code>.</p>
1433
1434 <p>Add an output type handler to the bottom of the file:</p>
1435
1436 <pre>
1437 <strong>def ReturnNumbersAsStrings(cursor, name, defaultType, size, precision, scale):
1438 if defaultType == cx_Oracle.NUMBER:
1439 return cursor.var(str, 9, cursor.arraysize)
1440
1441 print("Output type handler output...")
1442 cur = con.cursor()
1443 cur.outputtypehandler = ReturnNumbersAsStrings
1444 for row in cur.execute("select * from dept"):
1445 print(row)</strong>
1446 </pre>
1447
1448 <p>This type handler converts any number columns to strings with
1449 maxium size 9.</p>
1450
1451 <p>Run the script again:</p>
1452
1453 <pre><strong>python type_output.py</strong></pre>
1454
1455 <p>The new output shows the department numbers are now strings
1456 within quotes like <code>'10'</code>.</p>
1457
1458 </li>
1459
1460 <li><h4>6.2 Output type handlers and variable converters</h4>
1461
1462 <p>When numbers are fetched from the database, the conversion
1463 from Oracle's decimal representation to Python's binary format
1464 may need careful handling. To avoid unexpected issues, the
1465 general recommendation is to do number operations in SQL or
1466 PL/SQL, or to use the decimal module in Python.</p>
1467
1468 <p>Output type handlers can be combined with variable converters
1469 to change how data is fetched.</p>
1470
1471 <p>Review <code>type_converter.py</code>:</p>
1472
1473 <pre>
1474 import cx_Oracle
1475 import db_config
1476
1477 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1478 cur = con.cursor()
1479
1480 for value, in cur.execute("select 0.1 from dual"):
1481 print("Value:", value, "* 3 =", value * 3)
1482 </pre>
1483
1484 <p>Run the file:</p>
1485
1486 <pre><strong>python type_converter.py</strong></pre>
1487
1488 <p>The output is like:</p>
1489
1490 <pre>Value: 0.1 * 3 = 0.30000000000000004</pre>
1491
1492 <p>Edit the file and add a type handler that uses a Python
1493 decimal converter:</p>
1494
1495 <pre>
1496 import cx_Oracle
1497 <strong>import decimal</strong>
1498 import db_config
1499
1500 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1501 cur = con.cursor()
1502
1503 <strong>def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale):
1504 if defaultType == cx_Oracle.NUMBER:
1505 return cursor.var(str, 9, cursor.arraysize, outconverter = decimal.Decimal)
1506
1507 cur.outputtypehandler = ReturnNumbersAsDecimal</strong>
1508
1509 for value, in cur.execute("select 0.1 from dual"):
1510 print("Value:", value, "* 3 =", value * 3)
1511 </pre>
1512
1513 <p>The Python <code>decimal.Decimal</code> converter gets called
1514 with the string representation of the Oracle number. The output
1515 from <code>decimal.Decimal</code> is returned in the output
1516 tuple. </p>
1517
1518 <p>Run the file again:</p>
1519
1520 <pre><strong>python type_converter.py</strong></pre>
1521
1522 <p>Output is like:</p>
1523
1524 <pre>Value: 0.1 * 3 = 0.3</pre>
1525
1526 <p>Although the code demonstrates the use of outconverter, in this
1527 particular case, the variable can be created simply by using the
1528 following code to replace the outputtypehandler function defined
1529 above:</p>
1530
1531 <pre>
1532 def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale):
1533 if defaultType == cx_Oracle.NUMBER:
1534 return cursor.var(decimal.Decimal, arraysize = cursor.arraysize)
1535 </pre>
1536
1537 </li>
1538
1539 <li><h4>6.3 Input type handlers</h4>
1540
1541 <p>Input type handlers enable applications to change how data is
1542 bound to statements, or to enable new types to be bound directly
1543 without having to be converted individually.</p>
1544
1545 <p>Review <code>type_input.py</code>, which is similar to the
1546 final <code>bind_sdo.py</code> from section 4.4, with the
1547 addition of a new class and converter (shown in bold):</p>
1548
1549 <pre>
1550 import cx_Oracle
1551 import db_config
1552
1553 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1554 cur = con.cursor()
1555
1556 # Create table
1557 cur.execute("""begin
1558 execute immediate 'drop table testgeometry';
1559 exception when others then
1560 if sqlcode <> -942 then
1561 raise;
1562 end if;
1563 end;""")
1564 cur.execute("""create table testgeometry (
1565 id number(9) not null,
1566 geometry MDSYS.SDO_GEOMETRY not null)""")
1567
1568 <strong># Create a Python class for an SDO
1569 class mySDO(object):
1570
1571 def __init__(self, gtype, elemInfo, ordinates):
1572 self.gtype = gtype
1573 self.elemInfo = elemInfo
1574 self.ordinates = ordinates
1575
1576 # Get Oracle type information
1577 objType = con.gettype("MDSYS.SDO_GEOMETRY")
1578 elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
1579 ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY")
1580
1581 # Convert a Python object to MDSYS.SDO_GEOMETRY
1582 def SDOInConverter(value):
1583 obj = objType.newobject()
1584 obj.SDO_GTYPE = value.gtype
1585 obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
1586 obj.SDO_ELEM_INFO.extend(value.elemInfo)
1587 obj.SDO_ORDINATES = ordinateTypeObj.newobject()
1588 obj.SDO_ORDINATES.extend(value.ordinates)
1589 return obj
1590
1591 def SDOInputTypeHandler(cursor, value, numElements):
1592 if isinstance(value, mySDO):
1593 return cursor.var(cx_Oracle.OBJECT, arraysize = numElements,
1594 inconverter = SDOInConverter, typename = objType.name)
1595
1596 sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7]) # Python object
1597 cur.inputtypehandler = SDOInputTypeHandler
1598 cur.execute("insert into testgeometry values (:1, :2)", (1, sdo))</strong>
1599
1600 # Define a function to dump the contents of an Oracle object
1601 def dumpobject(obj, prefix = " "):
1602 if obj.type.iscollection:
1603 print(prefix, "[")
1604 for value in obj.aslist():
1605 if isinstance(value, cx_Oracle.Object):
1606 dumpobject(value, prefix + " ")
1607 else:
1608 print(prefix + " ", repr(value))
1609 print(prefix, "]")
1610 else:
1611 print(prefix, "{")
1612 for attr in obj.type.attributes:
1613 value = getattr(obj, attr.name)
1614 if isinstance(value, cx_Oracle.Object):
1615 print(prefix + " " + attr.name + " :")
1616 dumpobject(value, prefix + " ")
1617 else:
1618 print(prefix + " " + attr.name + " :", repr(value))
1619 print(prefix, "}")
1620
1621 # Query the row
1622 print("Querying row just inserted...")
1623 cur.execute("select id, geometry from testgeometry")
1624 for (id, obj) in cur:
1625 print("Id: ", id)
1626 dumpobject(obj)
1627 </pre>
1628
1629 <p>In the new file, a Python class <code>mySDO</code> is defined,
1630 which has attributes corresponding to each Oracle MDSYS.SDO_GEOMETRY
1631 attribute.
1632
1633 The <code>mySDO</code> class is used lower in the code to create a
1634 Python instance:</p>
1635
1636 <pre>
1637 sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7])</pre>
1638
1639 <p>which is then directly bound into the INSERT statement like:</p>
1640
1641 <pre>cur.execute("insert into testgeometry values (:1, :2)", (1, sdo))</pre>
1642
1643 <p>The mapping between Python and Oracle objects is handled in
1644 <code>SDOInConverter</code> which uses the cx_Oracle
1645 <code>newobject()</code> and <code>extend()</code> methods to create
1646 an Oracle object from the Python object values. The
1647 <code>SDOInConverter</code> method is called by the input type handler
1648 <code>SDOInputTypeHandler</code> whenever an instance of
1649 <code>mySDO</code> is inserted with the cursor.</p>
1650
1651 <p>To confirm the behavior, run the file:</p>
1652
1653 <pre><strong>python type_input.py</strong></pre>
1654
1655 </li>
1656
1657 </ul>
1658
1659 </li>
1660
1661 <li><h3><a name="lobs">7. LOBs</a></h3>
1662
1663 <ul>
1664 <li>
1665 <h4>7.1 Fetching a CLOB using a locator</h4>
1666
1667 <p>Review the code contained in <code>clob.py</code>:</p>
1668
1669 <pre>
1670 import cx_Oracle
1671 import db_config
1672
1673 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1674 cur = con.cursor()
1675
1676 print("Inserting data...")
1677 cur.execute("truncate table testclobs")
1678 longString = ""
1679 for i in range(5):
1680 char = chr(ord('A') + i)
1681 longString += char * 250
1682 cur.execute("insert into testclobs values (:1, :2)",
1683 (i + 1, "String data " + longString + ' End of string'))
1684 con.commit()
1685
1686 print("Querying data...")
1687 cur.prepare("select * from testclobs where id = :id")
1688 cur.execute(None, {'id': 1})
1689 (id, clob) = cur.fetchone()
1690 print("CLOB length:", clob.size())
1691 clobdata = clob.read()
1692 print("CLOB data:", clobdata)
1693 </pre>
1694
1695 <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
1698 <code>read()</code>.</p>
1699
1700 <p>To see the output, run the file:</p>
1701
1702 <pre><strong>python clob.py</strong></pre>
1703
1704 <p>Edit the file and experiment reading chunks of data by giving
1705 start character position and length, such as
1706 <code>clob.read(1,10)</code></p>
1707
1708 </li>
1709
1710 <li>
1711 <h4>7.2 Fetching a CLOB as a string</h4>
1712
1713 <p>For CLOBs small enough to fit in the application memory, it
1714 is much faster to fetch them directly as strings.</p>
1715
1716 <p>Review the code contained in <code>clob_string.py</code>.
1717 The differences from <code>clob.py</code> are shown in bold:</p>
1718
1719 <pre>
1720 import cx_Oracle
1721 import db_config
1722
1723 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1724 cur = con.cursor()
1725
1726 print("Inserting data...")
1727 cur.execute("truncate table testclobs")
1728 longString = ""
1729 for i in range(5):
1730 char = chr(ord('A') + i)
1731 longString += char * 250
1732 cur.execute("insert into testclobs values (:1, :2)",
1733 (i + 1, "String data " + longString + ' End of string'))
1734 con.commit()
1735
1736 <strong>def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
1737 if defaultType == cx_Oracle.CLOB:
1738 return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize)
1739
1740 con.outputtypehandler = OutputTypeHandler</strong>
1741
1742 print("Querying data...")
1743 cur.prepare("select * from testclobs where id = :id")
1744 cur.execute(None, {'id': 1})
1745 <strong>(id, clobdata) = cur.fetchone()
1746 print("CLOB length:", len(clobdata))
1747 print("CLOB data:", clobdata)</strong>
1748 </pre>
1749
1750 <p>The OutputTypeHandler causes cx_Oracle to fetch the CLOB as a
1751 string. Standard Python string functions such as
1752 <code>len()</code> can be used on the result.</p>
1753
1754 <p>The output is the same as for <code>clob.py</code>. To
1755 check, run the file:</p>
1756
1757 <pre><strong>python clob_string.py</strong></pre>
1758
1759 </li>
1760 </ul>
1761
1762 </li>
1763
1764 <li><h3><a name="rowfactory">8. Rowfactory functions</a></h3>
1765
1766 <p>Rowfactory functions enable queries to return objects other than
1767 tuples. They can be used to provide names for the various columns
1768 or to return custom objects.</p>
1769
1770 <ul>
1771 <li><h4>8.1 Rowfactory for mapping column names</h4>
1772
1773 <p>Review the code contained in <code>rowfactory.py</code>:</p>
1774
1775 <pre>
1776 import collections
1777 import cx_Oracle
1778 import db_config
1779
1780 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1781 cur = con.cursor()
1782
1783 cur.execute("select deptno, dname from dept")
1784 rows = cur.fetchall()
1785
1786 print('Array indexes:')
1787 for row in rows:
1788 print(row[0], "->", row[1])
1789
1790 print('Loop target variables:')
1791 for c1, c2 in rows:
1792 print(c1, "->", c2)
1793 </pre>
1794
1795 <p>This shows two methods of accessing result set items from a data
1796 row. The first uses array indexes like <code>row[0]</code>. The
1797 second uses loop target variables which take the values of each row
1798 tuple.</p>
1799
1800 <p>Run the file:</p>
1801
1802 <pre><strong>python rowfactory.py</strong></pre>
1803
1804 <p>Both access methods gives the same results.</p>
1805
1806 <p>To use a rowfactory function, edit <code>rowfactory.py</code> and
1807 add this code at the bottom:</p>
1808
1809 <pre>
1810 <strong>print('Rowfactory:')
1811 cur.execute("select deptno, dname from dept")
1812 cur.rowfactory = collections.namedtuple("MyClass", ["DeptNumber", "DeptName"])
1813
1814 rows = cur.fetchall()
1815 for row in rows:
1816 print(row.DeptNumber, "->", row.DeptName)
1817 </strong></pre>
1818
1819 <p>This uses the Python factory function
1820 <code>namedtuple()</code> to create a subclass of tuple that allows
1821 access to the elements via indexes or the given field names.</p>
1822
1823 <p>The <code>print()</code> function shows the use of the new
1824 named tuple fields. This coding style can help reduce coding
1825 errors.</p>
1826
1827 <p>Run the script again:</p>
1828
1829 <pre><strong>python rowfactory.py</strong></pre>
1830
1831
1832 <p>The output results are the same.</p>
1833
1834 </li>
1835 </ul>
1836
1837 </li>
1838
1839 <li><h3><a name="subclass">9. Subclassing connections and cursors</a></h3>
1840
1841 <p>Subclassing enables application to "hook" connection and cursor
1842 creation. This can be used to alter or log connection and execution
1843 parameters, and to extend cx_Oracle functionality. </p>
1844
1845 <ul>
1846 <li><h4>9.1 Subclassing connections</h4>
1847
1848 <p>Review the code contained in <code>subclass.py</code>:</p>
1849
1850 <pre>
1851 import cx_Oracle
1852 import db_config
1853
1854 class MyConnection(cx_Oracle.Connection):
1855
1856 def __init__(self):
1857 print("Connecting to database")
1858 return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn)
1859
1860 con = MyConnection()
1861 cur = con.cursor()
1862
1863 cur.execute("select count(*) from emp where deptno = :bv", (10,))
1864 count, = cur.fetchone()
1865 print("Number of rows:", count)
1866 </pre>
1867
1868 <p>This creates a new class "MyConnection" that inherits from the
1869 cx_Oracle Connection class. The <code>__init__</code> method is
1870 invoked when an instance of the new class is created. It prints a
1871 message and calls the base class, passing the connection
1872 credentials.</p>
1873
1874 <p>In the "normal" application, the application code:</p>
1875
1876 <pre>con = MyConnection()</pre>
1877
1878 <p>does not need to supply any credentials, as they are embedded in the
1879 custom subclass. All the cx_Oracle methods such as <code>cursor()</code> are
1880 available, as shown by the query.</p>
1881
1882 <p>Run the file:</p>
1883
1884 <pre><strong>python subclass.py</strong></pre>
1885
1886 <p>The query executes successfully.</p>
1887
1888 </li>
1889
1890 <li><h4>9.2 Subclassing cursors</h4>
1891
1892 <p>Edit <code>subclass.py</code> and extend the
1893 <code>cursor()</code> method with a new MyCursor class:</p>
1894
1895 <pre>
1896 import cx_Oracle
1897 import db_config
1898
1899 class MyConnection(cx_Oracle.Connection):
1900
1901 def __init__(self):
1902 print("Connecting to database")
1903 return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn)
1904
1905 <strong> def cursor(self):
1906 return MyCursor(self)
1907
1908 class MyCursor(cx_Oracle.Cursor):
1909
1910 def execute(self, statement, args):
1911 print("Executing:", statement)
1912 print("Arguments:")
1913 for argIndex, arg in enumerate(args):
1914 print(" Bind", argIndex + 1, "has value", repr(arg))
1915 return super(MyCursor, self).execute(statement, args)
1916
1917 def fetchone(self):
1918 print("Fetchone()")
1919 return super(MyCursor, self).fetchone()</strong>
1920
1921 con = MyConnection()
1922 cur = con.cursor()
1923
1924 cur.execute("select count(*) from emp where deptno = :bv", (10,))
1925 count, = cur.fetchone()
1926 print("Number of rows:", count)
1927 </pre>
1928
1929 <p>When the application gets a cursor from the
1930 <code>MyConnection</code> class, the new <code>cursor()</code> method
1931 returns an instance of our new <code>MyCursor</code> class.</p>
1932
1933 <p>The "application" query code remains unchanged. The new
1934 <code>execute()</code> and <code>fetchone()</code> methods of the
1935 <code>MyCursor</code> class get invoked. They do some logging and
1936 invoke the parent methods to do the actual statement execution.</p>
1937
1938 <p>To confirm this, run the file again:</p>
1939
1940 <pre><strong>python subclass.py</strong></pre>
1941
1942 </li>
1943
1944 </ul>
1945 </li>
1946
1947 <li><h3><a name="aq">10. Advanced Queuing</a></h3>
1948 <ul>
1949 <li><h4>10.1 Message passing with Oracle Advanced Queuing</h4></li>
1950
1951 <p>Review <code>aq.py</code>:</p>
1952
1953 <pre>
1954 import cx_Oracle
1955 import decimal
1956 import db_config
1957
1958 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
1959 cur = con.cursor()
1960
1961 BOOK_TYPE_NAME = "UDT_BOOK"
1962 QUEUE_NAME = "BOOKS"
1963 QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
1964
1965 # Cleanup
1966 cur.execute(
1967 """begin
1968 dbms_aqadm.stop_queue('""" + QUEUE_NAME + """');
1969 dbms_aqadm.drop_queue('""" + QUEUE_NAME + """');
1970 dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """');
1971 execute immediate 'drop type """ + BOOK_TYPE_NAME + """';
1972 exception when others then
1973 if sqlcode <> -24010 then
1974 raise;
1975 end if;
1976 end;""")
1977
1978 # Create type
1979 print("Creating books type UDT_BOOK...")
1980 cur.execute("""
1981 create type %s as object (
1982 title varchar2(100),
1983 authors varchar2(100),
1984 price number(5,2)
1985 );""" % BOOK_TYPE_NAME)
1986
1987 # Create queue table and queue and start the queue
1988 print("Creating queue table...")
1989 cur.callproc("dbms_aqadm.create_queue_table",
1990 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
1991 cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME))
1992 cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,))
1993
1994 # 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()
2010
2011 # 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()
2018 </pre>
2019
2020 <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>
2022
2023 <p>Run the file:</p>
2024
2025 <pre><strong>python aq.py</strong></pre>
2026
2027 <p>The output shows messages being queued and dequeued.</p>
2028
2029 <p>To experiment, split the code into three files: one to create and
2030 start the queue, and two other files to queue and dequeue messages.
2031 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
2041 </ul>
2042 </li>
2043
2044 </ol>
2045
2046 <h2><a name="summary">Summary</a></h2>
2047 <p>In this tutorial, you have learned how to: </p>
2048 <ul>
2049 <li>Create connections</li>
2050 <li>Use sessions pooling and Database Resident Connection Pooling</li>
2051 <li>Execute queries and fetch data</li>
2052 <li>Use bind variables</li>
2053 <li>Use PL/SQL stored functions and procedures</li>
2054 <li>Extend cx_Oracle classes</li>
2055 <li>Use Oracle Advanced Queuing</li>
2056 </ul>
2057
2058
2059 <h2><a name="primer">Appendix: Python Primer</a></h2>
2060
2061 <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>
2064
2065 <h4>Running Python</h4>
2066
2067 <p> You can either:</p>
2068
2069 <ul>
2070
2071 <li><p>Create a file of Python commands, such as
2072 <code>myfile.py</code>. This can be run with:</p>
2073 <pre><strong>python myfile.py</strong></pre></li>
2074
2075 <li><p>Alternatively run the Python interpreter by executing the
2076 <code>python</code> command in a terminal, and then interactively
2077 enter commands. Use <strong>Ctrl-D</strong> to exit back to the
2078 operating system prompt.</p></li>
2079
2080 </ul>
2081
2082 <p>When you run scripts, Python automatically creates bytecode
2083 versions of them in a folder called <code>__pycache__</code>.
2084 These improve performance of scripts that are run multiple times.
2085 They are automatically recreated if the source file changes.</p>
2086
2087 <h4>Indentation</h4>
2088
2089 <p> Whitespace indentation is significant in Python. When copying
2090 examples, use the same column alignment as shown. The samples in
2091 this lab use spaces, not tabs. </p>
2092
2093 <p>The following indentation prints 'done' once after the loop has
2094 completed:</p>
2095
2096 <pre>
2097 for i in range(5):
2098 print(i)
2099 print('done')
2100 </pre>
2101
2102 <p>But this indentation prints 'done' in each iteration:</p>
2103
2104 <pre>
2105 for i in range(5):
2106 print(i)
2107 print('done')
2108 </pre>
2109
2110 <h4>Strings</h4>
2111
2112 <p> Python strings can be enclosed in
2113 single or double quotes:</p>
2114
2115 <pre>'A string constant'
2116 &quot;another constant&quot;</pre>
2117 <p>Multi line strings use a triple-quote syntax:</p>
2118 <pre>&quot;&quot;&quot;
2119 SELECT *
2120 FROM EMP
2121 &quot;&quot;&quot;</pre>
2122
2123 <h4>Variables</h4>
2124
2125 <p> Variables do not need types declared:</p>
2126 <pre>count = 1
2127 ename = 'Arnie'</pre>
2128
2129 <h4>Comments</h4>
2130
2131 <p> Comments are either single line:</p>
2132 <pre># a short comment</pre>
2133 <p>They can be multi-line using the triple-quote token to create a string that does nothing:</p>
2134 <pre>&quot;&quot;&quot;
2135 a longer
2136 comment
2137 &quot;&quot;&quot;
2138 </pre>
2139
2140 <h4>Printing</h4>
2141
2142 <p> Strings and variables can be displayed with a <code>print()</code> function:</p>
2143 <pre>print('Hello, World!')
2144 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>
2151
2152 <h4>Data Structures</h4>
2153
2154 <p>Associative arrays are called 'dictionaries':</p>
2155 <pre>a2 = {'PI':3.1415, 'E':2.7182}</pre>
2156 <p>Ordered arrays are called 'lists':</p>
2157 <pre>a3 = [101, 4, 67]</pre>
2158 <p>Lists can be accessed via indexes.</p>
2159 <pre>
2160 print(a3[0])
2161 print(a3[-1])
2162 print(a3[1:3])
2163 </pre>
2164
2165 <p>Tuples are like lists but cannot be changed once they are
2166 created. They are created with parentheses:</p>
2167
2168 <pre>a4 = (3, 7, 10)</pre>
2169
2170 <p>Individual values in a tuple can be assigned to variables like:</p>
2171
2172 <pre>v1, v2, v3 = a4</pre>
2173
2174 <p>Now the variable v1 contains 3, the variable v2 contains 7 and the variable v3 contains 10.</p>
2175
2176 <p>The value in a single entry tuple like "<code>(13,)</code>"can be
2177 assigned to a variable by putting a comma after the variable name
2178 like:</p>
2179
2180 <pre>v1, = (13,)</pre>
2181
2182 <p>If the assignment is:</p>
2183
2184 <pre>v1 = (13,)</pre>
2185
2186 <p>then <code>v1</code> will contain the whole tuple "<code>(13,)</code>"</p>
2187
2188 <h4>Objects</h4>
2189
2190 <p>Everything in Python is an object. As an example, given the of the
2191 list <code>a3</code> above, the <code>append()</code> method can be
2192 used to add a value to the list.</p>
2193
2194 <pre>a3.append(23)</pre>
2195 <p>Now <code>a3</code> contains <code>[101, 4, 67, 23]</code></p>
2196
2197 <h4>Flow Control</h4>
2198
2199 <p> Code flow can be controlled with tests and loops. The
2200 <code>if</code>/<code>elif</code>/<code>else</code> statements look
2201 like:</p>
2202
2203 <pre>
2204 if sal &gt; 900000:
2205 print('Salary is way too big')
2206 elif sal &gt; 500000:
2207 print('Salary is huge')
2208 else:
2209 print('Salary might be OK')
2210 </pre>
2211
2212 <p>This also shows how the clauses are delimited with colons, and each
2213 sub block of code is indented.</p>
2214
2215 <h4>Loops</h4>
2216
2217 <p>A traditional loop is:</p>
2218 <pre>for i in range(10):
2219 print(i)</pre>
2220
2221 <p>This prints the numbers from 0 to 9. The value of <code>i</code>
2222 is incremented in each iteration. </p>
2223
2224 <p>The '<code>for</code>' command can also be used to iterate over
2225 lists and tuples:</p>
2226
2227 <pre>
2228 a5 = ['Aa', 'Bb', 'Cc']
2229 for v in a5:
2230 print(v)
2231 </pre>
2232
2233 <p>This sets <code>v</code> to each element of the list
2234 <code>a5</code> in turn.</p>
2235
2236 <h4>Functions</h4>
2237
2238 <p> A function may be defined as:</p>
2239
2240 <pre>
2241 def myfunc(p1, p2):
2242 &quot;Function documentation: add two numbers&quot;
2243 print(p1, p2)
2244 return p1 + p2</pre>
2245
2246 <p>Functions may or may not return values. This function could be called using:</p>
2247
2248 <pre>v3 = myfunc(1, 3)</pre>
2249
2250 <p>Function calls must appear after their function definition.</p>
2251
2252 <p>Functions are also objects and have attributes. The inbuilt
2253 <code>__doc__</code> attribute can be used to find the function
2254 description:</p>
2255
2256 <pre>print(myfunc.__doc__)</pre>
2257
2258 <h4>Modules</h4>
2259
2260 <p> Sub-files can be included in Python scripts with an import statement.</p>
2261 <pre>import os
2262 import sys</pre>
2263 <p>Many predefined modules exist, such as the os and the sys modules.</p>
2264
2265
2266 <h2><a name="resources">Resources</a></h2>
2267
2268 <ul>
2269 <li><a href="https://docs.python.org/3/" >Python 3 Documentation</a></li>
2270 <li><a href="http://cx-oracle.readthedocs.io/en/latest/index.html" >Python cx_Oracle Documentation</a></li>
2271 <li><a href="https://github.com/oracle/python-cx_Oracle" >Python cx_Oracle Source Code Repository</a></li>
2272 </ul>
2273
2274 <div class="footer"></div>
2275 <table border="0" cellpadding="10" cellspacing="0" width="100%">
2276 <tbody><tr>
2277 <td align="right" width="54%">Copyright &copy; 2017, Oracle and/or its affiliates. All rights reserved</td>
2278 </tr>
2279 <tr><td colspan="2"></td></tr>
2280 </tbody>
2281 </table>
2282
2283
2284 </div>
2285 </body>
2286 </html>
0 #------------------------------------------------------------------------------
1 # aq.py (Section 10.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import decimal
12 import db_config
13
14 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
15 cur = con.cursor()
16
17 BOOK_TYPE_NAME = "UDT_BOOK"
18 QUEUE_NAME = "BOOKS"
19 QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
20
21 # Cleanup
22 cur.execute(
23 """begin
24 dbms_aqadm.stop_queue('""" + QUEUE_NAME + """');
25 dbms_aqadm.drop_queue('""" + QUEUE_NAME + """');
26 dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """');
27 execute immediate 'drop type """ + BOOK_TYPE_NAME + """';
28 exception when others then
29 if sqlcode <> -24010 then
30 raise;
31 end if;
32 end;""")
33
34 # Create type
35 print("Creating books type UDT_BOOK...")
36 cur.execute("""
37 create type %s as object (
38 title varchar2(100),
39 authors varchar2(100),
40 price number(5,2)
41 );""" % BOOK_TYPE_NAME)
42
43 # Create queue table and queue and start the queue
44 print("Creating queue table...")
45 cur.callproc("dbms_aqadm.create_queue_table",
46 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
47 cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME))
48 cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,))
49
50 # 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()
66
67 # 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()
0 #------------------------------------------------------------------------------
1 # bind_insert.py (Section 4.2 and 4.3)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 rows = [ (1, "First" ), (2, "Second" ),
17 (3, "Third" ), (4, "Fourth" ),
18 (5, "Fifth" ), (6, "Sixth" ),
19 (7, "Seventh" ) ]
20
21 cur.executemany("insert into mytab(id, data) values (:1, :2)", rows)
22
23 # Now query the results back
24
25 cur2 = con.cursor()
26 cur2.execute('select * from mytab')
27 res = cur2.fetchall()
28 print(res)
0 -------------------------------------------------------------------------------
1 -- bind_insert.sql (Section 4.2)
2 -------------------------------------------------------------------------------
3
4 /*-----------------------------------------------------------------------------
5 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
6 *---------------------------------------------------------------------------*/
7
8 set echo on
9
10 @@db_config.sql
11 connect &user/&pw@&connect_string
12
13 begin
14 execute immediate 'drop table mytab';
15 exception
16 when others then
17 if sqlcode not in (-00942) then
18 raise;
19 end if;
20 end;
21 /
22
23 create table mytab (id number, data varchar2(20), constraint my_pk primary key (id));
24
25 exit
0 #------------------------------------------------------------------------------
1 # bind_query.py (Section 4.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 cur.prepare("select * from dept where deptno = :id order by deptno")
17
18 cur.execute(None, id = 20)
19 res = cur.fetchall()
20 print(res)
21
22 cur.execute(None, id = 10)
23 res = cur.fetchall()
24 print(res)
0 #------------------------------------------------------------------------------
1 # bind_sdo.py (Section 4.4)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 # Create table
17 cur.execute("""begin
18 execute immediate 'drop table testgeometry';
19 exception when others then
20 if sqlcode <> -942 then
21 raise;
22 end if;
23 end;""")
24 cur.execute("""create table testgeometry (
25 id number(9) not null,
26 geometry MDSYS.SDO_GEOMETRY not null)""")
27
28 # Create and populate Oracle objects
29 typeObj = con.gettype("MDSYS.SDO_GEOMETRY")
30 elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
31 ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY")
32 obj = typeObj.newobject()
33 obj.SDO_GTYPE = 2003
34 obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
35 obj.SDO_ELEM_INFO.extend([1, 1003, 3])
36 obj.SDO_ORDINATES = ordinateTypeObj.newobject()
37 obj.SDO_ORDINATES.extend([1, 1, 5, 7])
38 print("Created object", obj)
39
40 # Add a new row
41 print("Adding row to table...")
42 cur.execute("insert into testgeometry values (1, :obj)", obj = obj)
43 print("Row added!")
44
45 # (Change below here)
46
47 # Query the row
48 print("Querying row just inserted...")
49 cur.execute("select id, geometry from testgeometry");
50 for row in cur:
51 print(row)
0 #------------------------------------------------------------------------------
1 # clob.py (Section 7.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 print("Inserting data...")
17 cur.execute("truncate table testclobs")
18 longString = ""
19 for i in range(5):
20 char = chr(ord('A') + i)
21 longString += char * 250
22 cur.execute("insert into testclobs values (:1, :2)",
23 (i + 1, "String data " + longString + ' End of string'))
24 con.commit()
25
26 print("Querying data...")
27 cur.prepare("select * from testclobs where id = :id")
28 cur.execute(None, {'id': 1})
29 (id, clob) = cur.fetchone()
30 print("CLOB length:", clob.size())
31 clobdata = clob.read()
32 print("CLOB data:", clobdata)
0 #------------------------------------------------------------------------------
1 # clob_string.py (Section 7.2)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 print("Inserting data...")
17 cur.execute("truncate table testclobs")
18 longString = ""
19 for i in range(5):
20 char = chr(ord('A') + i)
21 longString += char * 250
22 cur.execute("insert into testclobs values (:1, :2)",
23 (i + 1, "String data " + longString + ' End of string'))
24 con.commit()
25
26 def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
27 if defaultType == cx_Oracle.CLOB:
28 return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize)
29
30 con.outputtypehandler = OutputTypeHandler
31
32 print("Querying data...")
33 cur.prepare("select * from testclobs where id = :id")
34 cur.execute(None, {'id': 1})
35 (id, clobdata) = cur.fetchone()
36 print("CLOB length:", len(clobdata))
37 print("CLOB data:", clobdata)
0 #------------------------------------------------------------------------------
1 # connect.py (Section 1.2 and 1.3)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 print("Database version:", con.version)
0 #------------------------------------------------------------------------------
1 # connect_drcp.py (Section 2.3 and 2.5)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn + ":pooled",
14 cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF)
15 print("Database version:", con.version)
0 #------------------------------------------------------------------------------
1 # connect_pool.py (Section 2.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import threading
12 import db_config
13
14 pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn,
15 min = 2, max = 5, increment = 1, threaded = True)
16
17 def Query():
18 con = pool.acquire()
19 cur = con.cursor()
20 for i in range(4):
21 cur.execute("select myseq.nextval from dual")
22 seqval, = cur.fetchone()
23 print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
24
25 thread1 = threading.Thread(name='#1', target=Query)
26 thread1.start()
27
28 thread2 = threading.Thread(name='#2', target=Query)
29 thread2.start()
30
31 thread1.join()
32 thread2.join()
33
34 print("All done!")
0 #------------------------------------------------------------------------------
1 # connect_pool2.py (Section 2.2 and 2.4)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import threading
12 import db_config
13
14 pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn,
15 min = 2, max = 5, increment = 1, threaded = True)
16
17 def Query():
18 con = pool.acquire()
19 cur = con.cursor()
20 for i in range(4):
21 cur.execute("select myseq.nextval from dual")
22 seqval, = cur.fetchone()
23 print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
24
25 numberOfThreads = 2
26 threadArray = []
27
28 for i in range(numberOfThreads):
29 thread = threading.Thread(name = '#' + str(i), target = Query)
30 threadArray.append(thread)
31 thread.start()
32
33 for t in threadArray:
34 t.join()
35
36 print("All done!")
0 user = "pythonhol"
1 pw = "welcome"
2 dsn = "localhost/orclpdb"
0 def user = "pythonhol"
1 def pw = "welcome"
2 def connect_string = "localhost/orclpdb"
0 -------------------------------------------------------------------------------
1 -- drcp_query.sql (Section 2.4 and 2.5)
2 -------------------------------------------------------------------------------
3
4 /*-----------------------------------------------------------------------------
5 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
6 *---------------------------------------------------------------------------*/
7
8 set echo on
9
10 -- Connect to the CDB to see pool statistics
11 connect sys/oracle@localhost/orcl as sysdba
12
13 col cclass_name format a30
14
15 -- Some DRCP pool statistics
16 select cclass_name, num_requests, num_hits, num_misses from v$cpool_cc_stats;
17
18 exit
0 #------------------------------------------------------------------------------
1 # plsql_func.py (Section 5.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 res = cur.callfunc('myfunc', int, ('abc', 2))
17 print(res)
0 -------------------------------------------------------------------------------
1 -- plsql_func.sql (Section 5.1)
2 -------------------------------------------------------------------------------
3
4 /*-----------------------------------------------------------------------------
5 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
6 *---------------------------------------------------------------------------*/
7
8 set echo on
9
10 @@db_config.sql
11 connect &user/&pw@&connect_string
12
13 begin
14 execute immediate 'drop table ptab';
15 exception
16 when others then
17 if sqlcode not in (-00942) then
18 raise;
19 end if;
20 end;
21 /
22
23 create table ptab (mydata varchar(20), myid number);
24
25 create or replace function myfunc(d_p in varchar2, i_p in number) return number as
26 begin
27 insert into ptab (mydata, myid) values (d_p, i_p);
28 return (i_p * 2);
29 end;
30 /
31 show errors
32
33 exit
0 #------------------------------------------------------------------------------
1 # plsql_proc.py (Section 5.2)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 myvar = cur.var(int)
17 cur.callproc('myproc', (123, myvar))
18 print(myvar.getvalue())
0 -------------------------------------------------------------------------------
1 -- plsql_proc.sql (Section 5.2)
2 -------------------------------------------------------------------------------
3
4 /*-----------------------------------------------------------------------------
5 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
6 *---------------------------------------------------------------------------*/
7
8 set echo on
9
10 @@db_config.sql
11 connect &user/&pw@&connect_string
12
13 create or replace procedure myproc(v1_p in number, v2_p out number) as
14 begin
15 v2_p := v1_p * 2;
16 end;
17 /
18 show errors
19
20 exit
0 #------------------------------------------------------------------------------
1 # query.py (Section 1.4 and 1.5)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
0 #------------------------------------------------------------------------------
1 # query2.py (Section 3.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14
15 cur = con.cursor()
16 cur.execute('select * from dept order by deptno')
17 for deptno, dname, loc in cur:
18 print("Department number: ", deptno)
19 print("Department name: ", dname)
20 print("Department location:", loc)
0 #------------------------------------------------------------------------------
1 # query_arraysize.py (Section 3.5)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import time
12 import db_config
13
14 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
15
16 start = time.time()
17
18 cur = con.cursor()
19 cur.arraysize = 10
20 cur.execute("select * from bigtab")
21 res = cur.fetchall()
22 # print(res) # uncomment to display the query results
23
24 elapsed = (time.time() - start)
25 print(elapsed, "seconds")
0 -------------------------------------------------------------------------------
1 -- query_arraysize.sql (Section 3.5)
2 -------------------------------------------------------------------------------
3
4 /*-----------------------------------------------------------------------------
5 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
6 *---------------------------------------------------------------------------*/
7
8 set echo on
9
10 @@db_config.sql
11 connect &user/&pw@&connect_string
12
13 begin
14 execute immediate 'drop table bigtab';
15 exception
16 when others then
17 if sqlcode not in (-00942) then
18 raise;
19 end if;
20 end;
21 /
22
23 create table bigtab (mycol varchar2(20));
24 begin
25 for i in 1..20000
26 loop
27 insert into bigtab (mycol) values (dbms_random.string('A',20));
28 end loop;
29 end;
30 /
31 show errors
32
33 commit;
34
35 exit
0 #------------------------------------------------------------------------------
1 # query_many.py (Section 3.3)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 cur.execute("select * from dept order by deptno")
17 res = cur.fetchmany(numRows = 3)
18 print(res)
0 #------------------------------------------------------------------------------
1 # query_one.py (Section 3.2)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 cur.execute("select * from dept order by deptno")
17 row = cur.fetchone()
18 print(row)
19
20 row = cur.fetchone()
21 print(row)
0 #------------------------------------------------------------------------------
1 # query_scroll.py (Section 3.4)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor(scrollable = True)
15
16 cur.execute("select * from dept order by deptno")
17
18 cur.scroll(2, mode = "absolute") # go to second row
19 print(cur.fetchone())
20
21 cur.scroll(-1) # go back one row
22 print(cur.fetchone())
0 body {
1 font-family: -apple-system, BlinkMacSystemFont,
2 "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji",
3 "Segoe UI Emoji", "Segoe UI Symbol";
4 font-size: 16px;
5 line-height: 1.5;
6 color: #333;
7 background-color: #fff;
8 }
9
10 #container {
11 border: 2px solid #078311;
12 min-height: 103%;
13 }
14
15 h1 {
16 background-color: #078311;
17 color: white;
18 font-size: 2.45em;
19 margin: 0;
20 padding: .3em;
21 }
22
23 h2 {
24 color: #078311;
25 margin-bottom: .5em;
26 }
27
28 hr {
29 color: #078311;
30 }
31
32 #menu {
33 background-color: #00C029;
34 padding: .5em;
35 margin-bottom: .5em;
36 }
37
38 #menu a {
39 text-decoration: none;
40 color: blue
41 }
42
43 #menu a:hover {
44 text-decoration: underline
45 }
46
47 #menu a:visited {
48 color: blue
49 }
50
51 #content {
52 margin: 1em;
53 }
54
55 .logo {
56 float: right;
57 }
58
59 pre {
60 background: #E0E0E0;
61 }
0 #------------------------------------------------------------------------------
1 # rowfactory.py (Section 8.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import collections
11 import cx_Oracle
12 import db_config
13
14 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
15 cur = con.cursor()
16
17 cur.execute("select deptno, dname from dept")
18 res = cur.fetchall()
19
20 print('Array indexes:')
21 for row in res:
22 print(row[0], "->", row[1])
23
24 print('Loop target variables:')
25 for c1, c2 in res:
26 print(c1, "->", c2)
0 #------------------------------------------------------------------------------
1 # aq-dequeue.py (Section 10.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import decimal
12 import db_config
13
14 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
15 cur = con.cursor()
16
17 BOOK_TYPE_NAME = "UDT_BOOK"
18 QUEUE_NAME = "BOOKS"
19 QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
20
21 # 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()
26 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()
0 #------------------------------------------------------------------------------
1 # aq-enqueue.py (Section 10.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import decimal
12 import db_config
13
14 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
15 cur = con.cursor()
16
17 BOOK_TYPE_NAME = "UDT_BOOK"
18 QUEUE_NAME = "BOOKS"
19 QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
20
21 # Enqueue a few messages
22 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()
0 #------------------------------------------------------------------------------
1 # aq-queuestart.py (Section 10.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import decimal
12 import db_config
13
14 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
15 cur = con.cursor()
16
17 BOOK_TYPE_NAME = "UDT_BOOK"
18 QUEUE_NAME = "BOOKS"
19 QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
20
21 # Cleanup
22 cur.execute(
23 """begin
24 dbms_aqadm.stop_queue('""" + QUEUE_NAME + """');
25 dbms_aqadm.drop_queue('""" + QUEUE_NAME + """');
26 dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """');
27 execute immediate 'drop type """ + BOOK_TYPE_NAME + """';
28 exception when others then
29 if sqlcode <> -24010 then
30 raise;
31 end if;
32 end;""")
33
34 # Create type
35 print("Creating books type UDT_BOOK...")
36 cur.execute("""
37 create type %s as object (
38 title varchar2(100),
39 authors varchar2(100),
40 price number(5,2)
41 );""" % BOOK_TYPE_NAME)
42
43 # Create queue table and queue and start the queue
44 print("Creating queue table...")
45 cur.callproc("dbms_aqadm.create_queue_table",
46 (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
47 cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME))
48 cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,))
0 #------------------------------------------------------------------------------
1 # bind_insert.py (Section 4.3)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 rows = [ (1, "First" ), (2, "Second" ),
17 (3, "Third" ), (4, "Fourth" ),
18 (5, "Fifth" ), (6, "Sixth" ),
19 (6, "Duplicate" ),
20 (7, "Seventh" ) ]
21
22 cur.executemany("insert into mytab(id, data) values (:1, :2)", rows, batcherrors = True)
23
24 for error in cur.getbatcherrors():
25 print("Error", error.message.rstrip(), "at row offset", error.offset)
26
27 # Now query the results back
28
29 cur2 = con.cursor()
30 cur2.execute('select * from mytab')
31 res = cur2.fetchall()
32 print(res)
0 #------------------------------------------------------------------------------
1 # bind_sdo.py (Section 4.4)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 # Create table
17 cur.execute("""begin
18 execute immediate 'drop table testgeometry';
19 exception when others then
20 if sqlcode <> -942 then
21 raise;
22 end if;
23 end;""")
24 cur.execute("""create table testgeometry (
25 id number(9) not null,
26 geometry MDSYS.SDO_GEOMETRY not null)""")
27
28 # Create and populate Oracle objects
29 typeObj = con.gettype("MDSYS.SDO_GEOMETRY")
30 elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
31 ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY")
32 obj = typeObj.newobject()
33 obj.SDO_GTYPE = 2003
34 obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
35 obj.SDO_ELEM_INFO.extend([1, 1003, 3])
36 obj.SDO_ORDINATES = ordinateTypeObj.newobject()
37 obj.SDO_ORDINATES.extend([1, 1, 5, 7])
38
39 pointTypeObj = con.gettype("MDSYS.SDO_POINT_TYPE")
40 obj.SDO_POINT = pointTypeObj.newobject()
41 obj.SDO_POINT.X = 1
42 obj.SDO_POINT.Y = 2
43 obj.SDO_POINT.Z = 3
44
45 print("Created object", obj)
46
47 # Add a new row
48 print("Adding row to table...")
49 cur.execute("insert into testgeometry values (1, :objbv)", objbv = obj)
50 print("Row added!")
51
52 # (Change below here)
53
54 # Define a function to dump the contents of an Oracle object
55 def dumpobject(obj, prefix = " "):
56 if obj.type.iscollection:
57 print(prefix, "[")
58 for value in obj.aslist():
59 if isinstance(value, cx_Oracle.Object):
60 dumpobject(value, prefix + " ")
61 else:
62 print(prefix + " ", repr(value))
63 print(prefix, "]")
64 else:
65 print(prefix, "{")
66 for attr in obj.type.attributes:
67 value = getattr(obj, attr.name)
68 if isinstance(value, cx_Oracle.Object):
69 print(prefix + " " + attr.name + " :")
70 dumpobject(value, prefix + " ")
71 else:
72 print(prefix + " " + attr.name + " :", repr(value))
73 print(prefix, "}")
74
75 # Query the row
76 print("Querying row just inserted...")
77 cur.execute("select id, geometry from testgeometry")
78 for (id, obj) in cur:
79 print("Id: ", id)
80 dumpobject(obj)
0 #------------------------------------------------------------------------------
1 # connect_pool2.py (Section 2.5)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import threading
12 import time
13 import db_config
14
15 pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn + ":pooled",
16 min = 2, max = 5, increment = 1, threaded = True,
17 getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT)
18
19 def Query():
20 con = pool.acquire(cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF)
21 #con = pool.acquire(cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_NEW)
22 cur = con.cursor()
23 for i in range(4):
24 cur.execute("select myseq.nextval from dual")
25 seqval, = cur.fetchone()
26 print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
27 #time.sleep(1)
28
29 numberOfThreads = 5
30 threadArray = []
31
32 for i in range(numberOfThreads):
33 thread = threading.Thread(name='#'+str(i), target=Query)
34 threadArray.append(thread)
35 #time.sleep(4)
36 thread.start()
37
38 for t in threadArray:
39 t.join()
40
41 print("All done!")
0 import os
1
2 dirName = os.path.dirname(os.path.dirname(__file__))
3 exec(open(os.path.join(dirName, "db_config.py"), "r").read())
0 #------------------------------------------------------------------------------
1 # query.py (Section 1.5)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14
15 cur = con.cursor()
16 cur.execute("select * from dept order by deptno")
17 res = cur.fetchall()
18 for row in res:
19 print(row)
20
21 cur.close()
22 con.close()
0 #------------------------------------------------------------------------------
1 # query.py (Section 1.4 and 1.5)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14
15 cur = con.cursor()
16 cur.execute("select * from dept order by deptno")
17 res = cur.fetchall()
18 for row in res:
19 print(row)
0 #------------------------------------------------------------------------------
1 # query_many.py (Section 3.3)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 cur.execute("select * from dept order by deptno")
17 res = cur.fetchmany(numRows=3)
18 print(res)
19
20 print(res[0]) # first row
21 print(res[0][1]) # second element of first row
0 #------------------------------------------------------------------------------
1 # query_scroll.py (Section 3.4)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor(scrollable = True)
15
16 cur.execute("select * from dept order by deptno")
17
18 cur.scroll(2, mode = "absolute") # go to second row
19 print(cur.fetchone())
20
21 cur.scroll(-1) # go back one row
22 print(cur.fetchone())
23
24 cur.scroll(1) # go to next row
25 print(cur.fetchone())
26
27 cur.scroll(mode = "first") # go to first row
28 print(cur.fetchone())
0 #------------------------------------------------------------------------------
1 # rowfactory.py (Section 8.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import collections
11 import cx_Oracle
12 import db_config
13
14 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
15
16 cur = con.cursor()
17
18 cur.execute("select deptno, dname from dept")
19 res = cur.fetchall()
20
21 print('Array indexes:')
22 for row in res:
23 print(row[0], "->", row[1])
24
25 print('Loop target variables:')
26 for c1, c2 in res:
27 print(c1, "->", c2)
28
29 print('Rowfactory:')
30 cur.execute("select deptno, dname from dept")
31 cur.rowfactory = collections.namedtuple("MyClass", ["DeptNumber", "DeptName"])
32
33 res = cur.fetchall()
34 for row in res:
35 print(row.DeptNumber, "->", row.DeptName)
0 #------------------------------------------------------------------------------
1 # subclass.py (Section 9.2)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 class MyConnection(cx_Oracle.Connection):
14
15 def __init__(self):
16 print("Connecting to database")
17 return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn)
18
19 def cursor(self):
20 return MyCursor(self)
21
22 class MyCursor(cx_Oracle.Cursor):
23
24 def execute(self, statement, args):
25 print("Executing:", statement)
26 print("Arguments:")
27 for argIndex, arg in enumerate(args):
28 print(" Bind", argIndex + 1, "has value", repr(arg))
29 return super(MyCursor, self).execute(statement, args)
30
31 def fetchone(self):
32 print("Fetchone()")
33 return super(MyCursor, self).fetchone()
34
35 con = MyConnection()
36 cur = con.cursor()
37
38 cur.execute("select count(*) from emp where deptno = :bv", (10,))
39 count, = cur.fetchone()
40 print("Number of rows:", count)
0 #------------------------------------------------------------------------------
1 # type_converter.py (Section 6.2)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import decimal
12 import db_config
13
14 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
15 cur = con.cursor()
16
17 def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale):
18 if defaultType == cx_Oracle.NUMBER:
19 return cursor.var(str, 9, cursor.arraysize, outconverter = decimal.Decimal)
20
21 cur.outputtypehandler = ReturnNumbersAsDecimal
22
23 for value, in cur.execute("select 0.1 from dual"):
24 print("Value:", value, "* 3 =", value * 3)
0 #------------------------------------------------------------------------------
1 # type_output.py (Section 6.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14
15 cur = con.cursor()
16
17 print("Standard output...")
18 for row in cur.execute("select * from dept"):
19 print(row)
20
21 def ReturnNumbersAsStrings(cursor, name, defaultType, size, precision, scale):
22 if defaultType == cx_Oracle.NUMBER:
23 return cursor.var(str, 9, cursor.arraysize)
24
25 print("Output type handler output...")
26 cur = con.cursor()
27 cur.outputtypehandler = ReturnNumbersAsStrings
28 for row in cur.execute("select * from dept"):
29 print(row)
0 #------------------------------------------------------------------------------
1 # versions.py (Section 1.6)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14
15 print(cx_Oracle.version)
16 print("Database version:", con.version)
17 print("Client version:", cx_Oracle.clientversion())
0 /*-----------------------------------------------------------------------------
1 * DropSamples.sql
2 * Drops database objects used for cx_Oracle HOL samples.
3 *
4 * Run this like:
5 * sqlplus / as sysdba @DropSamples <user>
6 *
7 * Note that the script SampleEnv.sql should be modified if you would like to
8 * use something other than the default schemas and passwords.
9 *---------------------------------------------------------------------------*/
10
11 /*-----------------------------------------------------------------------------
12 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
13 *---------------------------------------------------------------------------*/
14
15 whenever sqlerror exit failure
16
17 -- setup environment
18 @@SampleEnv.sql
19
20 begin
21 dbms_aqadm.stop_queue('BOOKS');
22 dbms_aqadm.drop_queue('BOOKS');
23 dbms_aqadm.drop_queue_table('BOOK_QUEUE_TABLE');
24 exception when others then
25 if sqlcode <> -24010 then
26 raise;
27 end if;
28 end;
29 /
30
31 begin
32
33 for r in
34 ( select username
35 from dba_users
36 where username in (upper('&main_user'))
37 ) loop
38 execute immediate 'drop user ' || r.username || ' cascade';
39 end loop;
40 end;
41 /
0 /*-----------------------------------------------------------------------------
1 * SampleEnv.sql
2 * Sets up configuration for the SetupSamples.sql and DropSamples.sql
3 * scripts. Change the values below if you would like to use something other
4 * than the default values. Note that the environment variables noted below
5 * will also need to be set, or the Python script SampleEnv.py will need to be
6 * changed if non-default values are used.
7 *---------------------------------------------------------------------------*/
8
9 /*-----------------------------------------------------------------------------
10 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
11 *---------------------------------------------------------------------------*/
12
13 set echo off termout on feedback off verify off
14
15 define main_user = "pythonhol" -- $CX_ORACLE_SAMPLES_MAIN_USER
16 define main_password = "welcome" -- $CX_ORACLE_SAMPLES_MAIN_PASSWORD
17
18 prompt ************************************************************************
19 prompt CONFIGURATION
20 prompt ************************************************************************
21 prompt Main Schema: &main_user
22 prompt
23
24 set echo on verify on feedback on
0 /*-----------------------------------------------------------------------------
1 * SetupSamples.sql
2 * Creates users and populates their schemas with the tables and packages
3 * necessary for the cx_Oracle HOL samples.
4 *
5 * Run this like:
6 * sqlplus / as sysdba @SetupSamples
7 *
8 * Note that the script SampleEnv.sql should be modified if you would like to
9 * use something other than the default configuration.
10 *---------------------------------------------------------------------------*/
11
12 /*-----------------------------------------------------------------------------
13 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
14 *---------------------------------------------------------------------------*/
15
16 whenever sqlerror exit failure
17
18 -- drop existing user
19 @@DropSamples.sql
20
21 alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
22 alter session set nls_numeric_characters='.,';
23
24 create user &main_user identified by &main_password
25 quota unlimited on users
26 default tablespace users;
27
28 grant
29 create session,
30 create table,
31 create procedure,
32 create type,
33 select any dictionary,
34 change notification
35 to &main_user;
36
37 grant execute on dbms_aqadm to &main_user;
38 grant execute on dbms_lock to &main_user;
39
40 create table &main_user..testclobs (
41 id number not null,
42 myclob clob not null
43 );
44
45 -- Sequence for connect_pool.py
46
47 create sequence &main_user..myseq;
48
49 -- EMP/DEPT tables
50
51 CREATE TABLE &main_user..EMP
52 (EMPNO NUMBER(4) NOT NULL,
53 ENAME VARCHAR2(10),
54 JOB VARCHAR2(9),
55 MGR NUMBER(4),
56 HIREDATE DATE,
57 SAL NUMBER(7, 2),
58 COMM NUMBER(7, 2),
59 DEPTNO NUMBER(2));
60
61 INSERT INTO &main_user..EMP VALUES
62 (7369, 'SMITH', 'CLERK', 7902,
63 TO_DATE('17-DEC-1980', 'DD-MON-YYYY'), 800, NULL, 20);
64 INSERT INTO &main_user..EMP VALUES
65 (7499, 'ALLEN', 'SALESMAN', 7698,
66 TO_DATE('20-FEB-1981', 'DD-MON-YYYY'), 1600, 300, 30);
67 INSERT INTO &main_user..EMP VALUES
68 (7521, 'WARD', 'SALESMAN', 7698,
69 TO_DATE('22-FEB-1981', 'DD-MON-YYYY'), 1250, 500, 30);
70 INSERT INTO &main_user..EMP VALUES
71 (7566, 'JONES', 'MANAGER', 7839,
72 TO_DATE('2-APR-1981', 'DD-MON-YYYY'), 2975, NULL, 20);
73 INSERT INTO &main_user..EMP VALUES
74 (7654, 'MARTIN', 'SALESMAN', 7698,
75 TO_DATE('28-SEP-1981', 'DD-MON-YYYY'), 1250, 1400, 30);
76 INSERT INTO &main_user..EMP VALUES
77 (7698, 'BLAKE', 'MANAGER', 7839,
78 TO_DATE('1-MAY-1981', 'DD-MON-YYYY'), 2850, NULL, 30);
79 INSERT INTO &main_user..EMP VALUES
80 (7782, 'CLARK', 'MANAGER', 7839,
81 TO_DATE('9-JUN-1981', 'DD-MON-YYYY'), 2450, NULL, 10);
82 INSERT INTO &main_user..EMP VALUES
83 (7788, 'SCOTT', 'ANALYST', 7566,
84 TO_DATE('09-DEC-1982', 'DD-MON-YYYY'), 3000, NULL, 20);
85 INSERT INTO &main_user..EMP VALUES
86 (7839, 'KING', 'PRESIDENT', NULL,
87 TO_DATE('17-NOV-1981', 'DD-MON-YYYY'), 5000, NULL, 10);
88 INSERT INTO &main_user..EMP VALUES
89 (7844, 'TURNER', 'SALESMAN', 7698,
90 TO_DATE('8-SEP-1981', 'DD-MON-YYYY'), 1500, 0, 30);
91 INSERT INTO &main_user..EMP VALUES
92 (7876, 'ADAMS', 'CLERK', 7788,
93 TO_DATE('12-JAN-1983', 'DD-MON-YYYY'), 1100, NULL, 20);
94 INSERT INTO &main_user..EMP VALUES
95 (7900, 'JAMES', 'CLERK', 7698,
96 TO_DATE('3-DEC-1981', 'DD-MON-YYYY'), 950, NULL, 30);
97 INSERT INTO &main_user..EMP VALUES
98 (7902, 'FORD', 'ANALYST', 7566,
99 TO_DATE('3-DEC-1981', 'DD-MON-YYYY'), 3000, NULL, 20);
100 INSERT INTO &main_user..EMP VALUES
101 (7934, 'MILLER', 'CLERK', 7782,
102 TO_DATE('23-JAN-1982', 'DD-MON-YYYY'), 1300, NULL, 10);
103
104 CREATE TABLE &main_user..DEPT
105 (DEPTNO NUMBER(2),
106 DNAME VARCHAR2(14),
107 LOC VARCHAR2(13) );
108
109 INSERT INTO &main_user..DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK');
110 INSERT INTO &main_user..DEPT VALUES (20, 'RESEARCH', 'DALLAS');
111 INSERT INTO &main_user..DEPT VALUES (30, 'SALES', 'CHICAGO');
112 INSERT INTO &main_user..DEPT VALUES (40, 'OPERATIONS', 'BOSTON');
113
114 CREATE TABLE &main_user..BONUS
115 (ENAME VARCHAR2(10),
116 JOB VARCHAR2(9),
117 SAL NUMBER,
118 COMM NUMBER);
119
120 CREATE TABLE &main_user..SALGRADE
121 (GRADE NUMBER,
122 LOSAL NUMBER,
123 HISAL NUMBER);
124
125 INSERT INTO &main_user..SALGRADE VALUES (1, 700, 1200);
126 INSERT INTO &main_user..SALGRADE VALUES (2, 1201, 1400);
127 INSERT INTO &main_user..SALGRADE VALUES (3, 1401, 2000);
128 INSERT INTO &main_user..SALGRADE VALUES (4, 2001, 3000);
129 INSERT INTO &main_user..SALGRADE VALUES (5, 3001, 9999);
130
131 COMMIT;
132
133 exit
0 #------------------------------------------------------------------------------
1 # subclass.py (Section 9.1 and 9.2)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 class MyConnection(cx_Oracle.Connection):
14
15 def __init__(self):
16 print("Connecting to database")
17 return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn)
18
19 con = MyConnection()
20 cur = con.cursor()
21
22 cur.execute("select count(*) from emp where deptno = :bv", (10,))
23 count, = cur.fetchone()
24 print("Number of rows:", count)
0 #------------------------------------------------------------------------------
1 # type_converter.py (Section 6.2)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 for value, in cur.execute("select 0.1 from dual"):
17 print("Value:", value, "* 3 =", value * 3)
0 #------------------------------------------------------------------------------
1 # type_input.py (Section 6.3)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 # Create table
17 cur.execute("""begin
18 execute immediate 'drop table testgeometry';
19 exception when others then
20 if sqlcode <> -942 then
21 raise;
22 end if;
23 end;""")
24 cur.execute("""create table testgeometry (
25 id number(9) not null,
26 geometry MDSYS.SDO_GEOMETRY not null)""")
27
28 # Create a Python class for an SDO
29 class mySDO(object):
30
31 def __init__(self, gtype, elemInfo, ordinates):
32 self.gtype = gtype
33 self.elemInfo = elemInfo
34 self.ordinates = ordinates
35
36 # Get Oracle type information
37 objType = con.gettype("MDSYS.SDO_GEOMETRY")
38 elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
39 ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY")
40
41 # Convert a Python object to MDSYS.SDO_GEOMETRY
42 def SDOInConverter(value):
43 obj = objType.newobject()
44 obj.SDO_GTYPE = value.gtype
45 obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
46 obj.SDO_ELEM_INFO.extend(value.elemInfo)
47 obj.SDO_ORDINATES = ordinateTypeObj.newobject()
48 obj.SDO_ORDINATES.extend(value.ordinates)
49 return obj
50
51 def SDOInputTypeHandler(cursor, value, numElements):
52 if isinstance(value, mySDO):
53 return cursor.var(cx_Oracle.OBJECT, arraysize = numElements,
54 inconverter = SDOInConverter, typename = objType.name)
55
56 sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7]) # Python object
57 cur.inputtypehandler = SDOInputTypeHandler
58 cur.execute("insert into testgeometry values (:1, :2)", (1, sdo))
59
60 # Define a function to dump the contents of an Oracle object
61 def dumpobject(obj, prefix = " "):
62 if obj.type.iscollection:
63 print(prefix, "[")
64 for value in obj.aslist():
65 if isinstance(value, cx_Oracle.Object):
66 dumpobject(value, prefix + " ")
67 else:
68 print(prefix + " ", repr(value))
69 print(prefix, "]")
70 else:
71 print(prefix, "{")
72 for attr in obj.type.attributes:
73 value = getattr(obj, attr.name)
74 if isinstance(value, cx_Oracle.Object):
75 print(prefix + " " + attr.name + " :")
76 dumpobject(value, prefix + " ")
77 else:
78 print(prefix + " " + attr.name + " :", repr(value))
79 print(prefix, "}")
80
81 # Query the row
82 print("Querying row just inserted...")
83 cur.execute("select id, geometry from testgeometry")
84 for (id, obj) in cur:
85 print("Id: ", id)
86 dumpobject(obj)
0 #------------------------------------------------------------------------------
1 # type_output.py (Section 6.1)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14 cur = con.cursor()
15
16 print("Standard output...")
17 for row in cur.execute("select * from dept"):
18 print(row)
0 #------------------------------------------------------------------------------
1 # versions.py (Section 1.6)
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
6 #------------------------------------------------------------------------------
7
8 from __future__ import print_function
9
10 import cx_Oracle
11 import db_config
12
13 con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
14
15 print(cx_Oracle.version)
0 """Distutils script for cx_Oracle.
1
2 Windows platforms:
3 python setup.py build --compiler=mingw32 install
4
5 Unix platforms
6 python setup.py build install
7
8 """
9
10 import distutils.core
11 import os
12 import sys
13
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
20
21 # define build constants
22 BUILD_VERSION = "7.1.0"
23
24 # setup extra link and compile args
25 extraLinkArgs = []
26 extraCompileArgs = []
27 if sys.platform == "aix4":
28 extraCompileArgs.append("-qcpluscmt")
29 elif sys.platform == "aix5":
30 extraCompileArgs.append("-DAIX5")
31 elif sys.platform == "cygwin":
32 extraLinkArgs.append("-Wl,--enable-runtime-pseudo-reloc")
33 elif sys.platform == "darwin":
34 extraLinkArgs.append("-shared-libgcc")
35
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
68 # define cx_Oracle sources
69 sourceDir = "src"
70 sources = [os.path.join(sourceDir, n) \
71 for n in sorted(os.listdir(sourceDir)) if n.endswith(".c")]
72 depends = ["src/cxoModule.h"]
73
74
75 # define ODPI-C sources, libraries and include directories; if the environment
76 # variables ODPIC_INC_DIR and ODPIC_LIB_DIR are both set, assume these
77 # locations contain a compiled installation of ODPI-C; otherwise, use the
78 # source of ODPI-C found in the odpi subdirectory
79 dpiIncludeDir = os.environ.get("ODPIC_INC_DIR")
80 dpiLibDir = os.environ.get("ODPIC_LIB_DIR")
81 if dpiIncludeDir and dpiLibDir:
82 dpiSources = []
83 includeDirs = [dpiIncludeDir]
84 libraries = ["odpic"]
85 libraryDirs = [dpiLibDir]
86 else:
87 includeDirs = ["odpi/include", "odpi/src"]
88 dpiSourceDir = os.path.join("odpi", "src")
89 dpiSources = [os.path.join(dpiSourceDir, n) \
90 for n in sorted(os.listdir(dpiSourceDir)) if n.endswith(".c")]
91 depends.extend(["odpi/include/dpi.h", "odpi/src/dpiImpl.h",
92 "odpi/src/dpiErrorMessages.h"])
93 libraries = []
94 libraryDirs = []
95
96 # setup the extension
97 extension = Extension(
98 name = "cx_Oracle",
99 include_dirs = includeDirs,
100 extra_compile_args = extraCompileArgs,
101 define_macros = [("CXO_BUILD_VERSION", BUILD_VERSION)],
102 extra_link_args = extraLinkArgs,
103 sources = sources + dpiSources,
104 depends = depends,
105 libraries = libraries,
106 library_dirs = libraryDirs)
107
108 # perform the setup
109 setup(
110 name = "cx_Oracle",
111 version = BUILD_VERSION,
112 description = "Python interface to Oracle",
113 cmdclass = dict(test = test),
114 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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoBuffer.c
11 // Defines buffer structure and routines for populating it. These are used
12 // to translate Python objects into the buffers needed for Oracle, including
13 // Unicode or buffer objects.
14 //-----------------------------------------------------------------------------
15
16 #include "cxoModule.h"
17
18 //-----------------------------------------------------------------------------
19 // cxoBuffer_fromObject()
20 // Populate the string buffer from a unicode object.
21 //-----------------------------------------------------------------------------
22 int cxoBuffer_fromObject(cxoBuffer *buf, PyObject *obj, const char *encoding)
23 {
24 cxoBuffer_init(buf);
25 if (!obj || obj == Py_None)
26 return 0;
27 if (PyUnicode_Check(obj)) {
28 buf->obj = PyUnicode_AsEncodedString(obj, encoding, NULL);
29 if (!buf->obj)
30 return -1;
31 buf->ptr = PyBytes_AS_STRING(buf->obj);
32 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
36 buf->numCharacters = (uint32_t) PyUnicode_GET_LENGTH(obj);
37 #endif
38 } else if (PyBytes_Check(obj)) {
39 Py_INCREF(obj);
40 buf->obj = obj;
41 buf->ptr = PyBytes_AS_STRING(buf->obj);
42 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
52 } else {
53 #if PY_MAJOR_VERSION >= 3
54 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
59 return -1;
60 }
61 return 0;
62 }
63
64
65 //-----------------------------------------------------------------------------
66 // cxoBuffer_init()
67 // Initialize the buffer with an empty string. Returns 0 as a convenience to
68 // the caller.
69 //-----------------------------------------------------------------------------
70 int cxoBuffer_init(cxoBuffer *buf)
71 {
72 buf->ptr = NULL;
73 buf->size = 0;
74 buf->numCharacters = 0;
75 buf->obj = NULL;
76 return 0;
77 }
78
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 // cxoConnection.c
11 // Definition of the Python type Connection.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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[] = {
76 { "cursor", (PyCFunction) cxoConnection_newCursor,
77 METH_VARARGS | METH_KEYWORDS },
78 { "commit", (PyCFunction) cxoConnection_commit, METH_NOARGS },
79 { "rollback", (PyCFunction) cxoConnection_rollback, METH_NOARGS },
80 { "begin", (PyCFunction) cxoConnection_begin, METH_VARARGS },
81 { "prepare", (PyCFunction) cxoConnection_prepare, METH_NOARGS },
82 { "close", (PyCFunction) cxoConnection_close, METH_NOARGS },
83 { "cancel", (PyCFunction) cxoConnection_cancel, METH_NOARGS },
84 { "__enter__", (PyCFunction) cxoConnection_contextManagerEnter,
85 METH_NOARGS },
86 { "__exit__", (PyCFunction) cxoConnection_contextManagerExit,
87 METH_VARARGS },
88 { "ping", (PyCFunction) cxoConnection_ping, METH_NOARGS },
89 { "shutdown", (PyCFunction) cxoConnection_shutdown,
90 METH_VARARGS | METH_KEYWORDS},
91 { "startup", (PyCFunction) cxoConnection_startup,
92 METH_VARARGS | METH_KEYWORDS},
93 { "subscribe", (PyCFunction) cxoConnection_subscribe,
94 METH_VARARGS | METH_KEYWORDS},
95 { "unsubscribe", (PyCFunction) cxoConnection_unsubscribe,
96 METH_VARARGS | METH_KEYWORDS},
97 { "changepassword", (PyCFunction) cxoConnection_changePassword,
98 METH_VARARGS },
99 { "gettype", (PyCFunction) cxoConnection_getType, METH_O },
100 { "deqoptions", (PyCFunction) cxoConnection_newDequeueOptions,
101 METH_NOARGS },
102 { "enqoptions", (PyCFunction) cxoConnection_newEnqueueOptions,
103 METH_NOARGS },
104 { "msgproperties", (PyCFunction) cxoConnection_newMessageProperties,
105 METH_NOARGS },
106 { "deq", (PyCFunction) cxoConnection_dequeue,
107 METH_VARARGS | METH_KEYWORDS },
108 { "enq", (PyCFunction) cxoConnection_enqueue,
109 METH_VARARGS | METH_KEYWORDS },
110 { "createlob", (PyCFunction) cxoConnection_createLob, METH_O },
111 { "getSodaDatabase", (PyCFunction) cxoConnection_getSodaDatabase,
112 METH_NOARGS },
113 { NULL }
114 };
115
116
117 //-----------------------------------------------------------------------------
118 // declaration of members for Python type "Connection"
119 //-----------------------------------------------------------------------------
120 static PyMemberDef cxoConnectionMembers[] = {
121 { "username", T_OBJECT, offsetof(cxoConnection, username), READONLY },
122 { "dsn", T_OBJECT, offsetof(cxoConnection, dsn), READONLY },
123 { "tnsentry", T_OBJECT, offsetof(cxoConnection, dsn), READONLY },
124 { "tag", T_OBJECT, offsetof(cxoConnection, tag), 0 },
125 { "autocommit", T_INT, offsetof(cxoConnection, autocommit), 0 },
126 { "inputtypehandler", T_OBJECT,
127 offsetof(cxoConnection, inputTypeHandler), 0 },
128 { "outputtypehandler", T_OBJECT,
129 offsetof(cxoConnection, outputTypeHandler), 0 },
130 { NULL }
131 };
132
133
134 //-----------------------------------------------------------------------------
135 // declaration of calculated members for Python type "Connection"
136 //-----------------------------------------------------------------------------
137 static PyGetSetDef cxoConnectionCalcMembers[] = {
138 { "version", (getter) cxoConnection_getVersion, 0, 0, 0 },
139 { "encoding", (getter) cxoConnection_getEncoding, 0, 0, 0 },
140 { "nencoding", (getter) cxoConnection_getNationalEncoding, 0, 0, 0 },
141 { "callTimeout", (getter) cxoConnection_getCallTimeout,
142 (setter) cxoConnection_setCallTimeout, 0, 0 },
143 { "maxBytesPerCharacter", (getter) cxoConnection_getMaxBytesPerCharacter,
144 0, 0, 0 },
145 { "stmtcachesize", (getter) cxoConnection_getStmtCacheSize,
146 (setter) cxoConnection_setStmtCacheSize, 0, 0 },
147 { "module", 0, (setter) cxoConnection_setModule, 0, 0 },
148 { "action", 0, (setter) cxoConnection_setAction, 0, 0 },
149 { "clientinfo", 0, (setter) cxoConnection_setClientInfo, 0, 0 },
150 { "client_identifier", 0, (setter) cxoConnection_setClientIdentifier, 0,
151 0 },
152 { "current_schema", (getter) cxoConnection_getCurrentSchema,
153 (setter) cxoConnection_setCurrentSchema, 0, 0 },
154 { "external_name", (getter) cxoConnection_getExternalName,
155 (setter) cxoConnection_setExternalName, 0, 0 },
156 { "internal_name", (getter) cxoConnection_getInternalName,
157 (setter) cxoConnection_setInternalName, 0, 0 },
158 { "dbop", 0, (setter) cxoConnection_setDbOp, 0, 0 },
159 { "edition", (getter) cxoConnection_getEdition, 0, 0, 0 },
160 { "ltxid", (getter) cxoConnection_getLTXID, 0, 0, 0 },
161 { "handle", (getter) cxoConnection_getHandle, 0, 0, 0 },
162 { "Error", (getter) cxoConnection_getException, NULL, NULL,
163 &cxoErrorException },
164 { "Warning", (getter) cxoConnection_getException, NULL, NULL,
165 &cxoWarningException },
166 { "InterfaceError", (getter) cxoConnection_getException, NULL, NULL,
167 &cxoInterfaceErrorException },
168 { "DatabaseError", (getter) cxoConnection_getException, NULL, NULL,
169 &cxoDatabaseErrorException },
170 { "InternalError", (getter) cxoConnection_getException, NULL, NULL,
171 &cxoInternalErrorException },
172 { "OperationalError", (getter) cxoConnection_getException, NULL, NULL,
173 &cxoOperationalErrorException },
174 { "ProgrammingError", (getter) cxoConnection_getException, NULL, NULL,
175 &cxoProgrammingErrorException },
176 { "IntegrityError", (getter) cxoConnection_getException, NULL, NULL,
177 &cxoIntegrityErrorException },
178 { "DataError", (getter) cxoConnection_getException, NULL, NULL,
179 &cxoDataErrorException },
180 { "NotSupportedError", (getter) cxoConnection_getException, NULL, NULL,
181 &cxoNotSupportedErrorException },
182 { NULL }
183 };
184
185
186 //-----------------------------------------------------------------------------
187 // declaration of Python type "Connection"
188 //-----------------------------------------------------------------------------
189 PyTypeObject cxoPyTypeConnection = {
190 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
232 };
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
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 // cxoCursor.c
11 // Definition of the Python type Cursor.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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[] = {
56 { "execute", (PyCFunction) cxoCursor_execute,
57 METH_VARARGS | METH_KEYWORDS },
58 { "fetchall", (PyCFunction) cxoCursor_fetchAll, METH_NOARGS },
59 { "fetchone", (PyCFunction) cxoCursor_fetchOne, METH_NOARGS },
60 { "fetchmany", (PyCFunction) cxoCursor_fetchMany,
61 METH_VARARGS | METH_KEYWORDS },
62 { "fetchraw", (PyCFunction) cxoCursor_fetchRaw,
63 METH_VARARGS | METH_KEYWORDS },
64 { "prepare", (PyCFunction) cxoCursor_prepare, METH_VARARGS },
65 { "parse", (PyCFunction) cxoCursor_parse, METH_O },
66 { "setinputsizes", (PyCFunction) cxoCursor_setInputSizes,
67 METH_VARARGS | METH_KEYWORDS },
68 { "executemany", (PyCFunction) cxoCursor_executeMany,
69 METH_VARARGS | METH_KEYWORDS },
70 { "callproc", (PyCFunction) cxoCursor_callProc,
71 METH_VARARGS | METH_KEYWORDS },
72 { "callfunc", (PyCFunction) cxoCursor_callFunc,
73 METH_VARARGS | METH_KEYWORDS },
74 { "executemanyprepared", (PyCFunction) cxoCursor_executeManyPrepared,
75 METH_VARARGS },
76 { "setoutputsize", (PyCFunction) cxoCursor_setOutputSize, METH_VARARGS },
77 { "scroll", (PyCFunction) cxoCursor_scroll, METH_VARARGS | METH_KEYWORDS },
78 { "var", (PyCFunction) cxoCursor_var, METH_VARARGS | METH_KEYWORDS },
79 { "arrayvar", (PyCFunction) cxoCursor_arrayVar, METH_VARARGS },
80 { "bindnames", (PyCFunction) cxoCursor_bindNames, METH_NOARGS },
81 { "close", (PyCFunction) cxoCursor_close, METH_NOARGS },
82 { "getbatcherrors", (PyCFunction) cxoCursor_getBatchErrors, METH_NOARGS },
83 { "getarraydmlrowcounts", (PyCFunction) cxoCursor_getArrayDMLRowCounts,
84 METH_NOARGS },
85 { "getimplicitresults", (PyCFunction) cxoCursor_getImplicitResults,
86 METH_NOARGS },
87 { "__enter__", (PyCFunction) cxoCursor_contextManagerEnter, METH_NOARGS },
88 { "__exit__", (PyCFunction) cxoCursor_contextManagerExit, METH_VARARGS },
89 { NULL, NULL }
90 };
91
92
93 //-----------------------------------------------------------------------------
94 // declaration of members for Python type "Cursor"
95 //-----------------------------------------------------------------------------
96 static PyMemberDef cxoCursorMembers[] = {
97 { "arraysize", T_UINT, offsetof(cxoCursor, arraySize), 0 },
98 { "bindarraysize", T_UINT, offsetof(cxoCursor, bindArraySize), 0 },
99 { "rowcount", T_ULONGLONG, offsetof(cxoCursor, rowCount), READONLY },
100 { "statement", T_OBJECT, offsetof(cxoCursor, statement), READONLY },
101 { "connection", T_OBJECT_EX, offsetof(cxoCursor, connection), READONLY },
102 { "rowfactory", T_OBJECT, offsetof(cxoCursor, rowFactory), 0 },
103 { "bindvars", T_OBJECT, offsetof(cxoCursor, bindVariables), READONLY },
104 { "fetchvars", T_OBJECT, offsetof(cxoCursor, fetchVariables), READONLY },
105 { "inputtypehandler", T_OBJECT, offsetof(cxoCursor, inputTypeHandler),
106 0 },
107 { "outputtypehandler", T_OBJECT, offsetof(cxoCursor, outputTypeHandler),
108 0 },
109 { "scrollable", T_BOOL, offsetof(cxoCursor, isScrollable), 0 },
110 { NULL }
111 };
112
113
114 //-----------------------------------------------------------------------------
115 // declaration of calculated members for Python type "Connection"
116 //-----------------------------------------------------------------------------
117 static PyGetSetDef cxoCursorCalcMembers[] = {
118 { "description", (getter) cxoCursor_getDescription, 0, 0, 0 },
119 { NULL }
120 };
121
122
123 //-----------------------------------------------------------------------------
124 // declaration of Python type "Cursor"
125 //-----------------------------------------------------------------------------
126 PyTypeObject cxoPyTypeCursor = {
127 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
169 };
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) 2016, 2018, 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 // cxoDeqOptions.c
11 // Implements the dequeue options objects used in Advanced Queuing.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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"
43 //-----------------------------------------------------------------------------
44 static PyGetSetDef cxoDeqOptionsCalcMembers[] = {
45 { "condition", (getter) cxoDeqOptions_getCondition,
46 (setter) cxoDeqOptions_setCondition, 0, 0 },
47 { "consumername", (getter) cxoDeqOptions_getConsumerName,
48 (setter) cxoDeqOptions_setConsumerName, 0, 0 },
49 { "correlation", (getter) cxoDeqOptions_getCorrelation,
50 (setter) cxoDeqOptions_setCorrelation, 0, 0 },
51 { "deliverymode", 0, (setter) cxoDeqOptions_setDeliveryMode, 0, 0 },
52 { "mode", (getter) cxoDeqOptions_getMode, (setter) cxoDeqOptions_setMode,
53 0, 0 },
54 { "msgid", (getter) cxoDeqOptions_getMsgId,
55 (setter) cxoDeqOptions_setMsgId, 0, 0 },
56 { "navigation", (getter) cxoDeqOptions_getNavigation,
57 (setter) cxoDeqOptions_setNavigation, 0, 0 },
58 { "transformation", (getter) cxoDeqOptions_getTransformation,
59 (setter) cxoDeqOptions_setTransformation, 0, 0 },
60 { "visibility", (getter) cxoDeqOptions_getVisibility,
61 (setter) cxoDeqOptions_setVisibility, 0, 0 },
62 { "wait", (getter) cxoDeqOptions_getWait, (setter) cxoDeqOptions_setWait,
63 0, 0 },
64 { NULL }
65 };
66
67
68 //-----------------------------------------------------------------------------
69 // Python type declarations
70 //-----------------------------------------------------------------------------
71 PyTypeObject cxoPyTypeDeqOptions = {
72 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
113 };
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoEnqOptions.c
11 // Implements the enqueue options objects used in Advanced Queuing.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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 //-----------------------------------------------------------------------------
89 // cxoEnqOptions_new()
90 // Create a new enqueue options object.
91 //-----------------------------------------------------------------------------
92 cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection)
93 {
94 cxoEnqOptions *self;
95
96 self = (cxoEnqOptions*)
97 cxoPyTypeEnqOptions.tp_alloc(&cxoPyTypeEnqOptions, 0);
98 if (!self)
99 return NULL;
100 if (dpiConn_newEnqOptions(connection->handle, &self->handle) < 0) {
101 Py_DECREF(self);
102 cxoError_raiseAndReturnNull();
103 return NULL;
104 }
105 self->encoding = connection->encodingInfo.encoding;
106
107 return self;
108 }
109
110
111 //-----------------------------------------------------------------------------
112 // cxoEnqOptions_free()
113 // Free the memory associated with the enqueue options object.
114 //-----------------------------------------------------------------------------
115 static void cxoEnqOptions_free(cxoEnqOptions *self)
116 {
117 if (self->handle) {
118 dpiEnqOptions_release(self->handle);
119 self->handle = NULL;
120 }
121 Py_TYPE(self)->tp_free((PyObject*) self);
122 }
123
124
125 //-----------------------------------------------------------------------------
126 // cxoEnqOptions_getTransformation()
127 // Get the value of the transformation option.
128 //-----------------------------------------------------------------------------
129 static PyObject *cxoEnqOptions_getTransformation(cxoEnqOptions *self,
130 void *unused)
131 {
132 uint32_t valueLength;
133 const char *value;
134
135 if (dpiEnqOptions_getTransformation(self->handle, &value,
136 &valueLength) < 0)
137 return cxoError_raiseAndReturnNull();
138 if (!value)
139 Py_RETURN_NONE;
140 return cxoPyString_fromEncodedString(value, valueLength, self->encoding,
141 NULL);
142 }
143
144
145 //-----------------------------------------------------------------------------
146 // cxoEnqOptions_getVisibility()
147 // Get the value of the visibility option.
148 //-----------------------------------------------------------------------------
149 static PyObject *cxoEnqOptions_getVisibility(cxoEnqOptions *self, void *unused)
150 {
151 dpiVisibility value;
152
153 if (dpiEnqOptions_getVisibility(self->handle, &value) < 0)
154 return cxoError_raiseAndReturnNull();
155 return PyInt_FromLong(value);
156 }
157
158
159 //-----------------------------------------------------------------------------
160 // cxoEnqOptions_setDeliveryMode()
161 // Set the value of the delivery mode option.
162 //-----------------------------------------------------------------------------
163 static int cxoEnqOptions_setDeliveryMode(cxoEnqOptions *self, PyObject *valueObj,
164 void *unused)
165 {
166 dpiMessageDeliveryMode value;
167
168 value = PyInt_AsLong(valueObj);
169 if (PyErr_Occurred())
170 return -1;
171 if (dpiEnqOptions_setDeliveryMode(self->handle, value) < 0)
172 return cxoError_raiseAndReturnInt();
173 return 0;
174 }
175
176
177 //-----------------------------------------------------------------------------
178 // cxoEnqOptions_setTransformation()
179 // Set the value of the transformation option.
180 //-----------------------------------------------------------------------------
181 static int cxoEnqOptions_setTransformation(cxoEnqOptions *self,
182 PyObject *valueObj, void *unused)
183 {
184 cxoBuffer buffer;
185 int status;
186
187 if (cxoBuffer_fromObject(&buffer, valueObj, self->encoding) < 0)
188 return -1;
189 status = dpiEnqOptions_setTransformation(self->handle, buffer.ptr,
190 buffer.size);
191 cxoBuffer_clear(&buffer);
192 if (status < 0)
193 return cxoError_raiseAndReturnInt();
194 return 0;
195 }
196
197
198 //-----------------------------------------------------------------------------
199 // cxoEnqOptions_setVisibility()
200 // Set the value of the visibility option.
201 //-----------------------------------------------------------------------------
202 static int cxoEnqOptions_setVisibility(cxoEnqOptions *self,
203 PyObject *valueObj, void *unused)
204 {
205 dpiVisibility value;
206
207 value = PyInt_AsLong(valueObj);
208 if (PyErr_Occurred())
209 return -1;
210 if (dpiEnqOptions_setVisibility(self->handle, value) < 0)
211 return cxoError_raiseAndReturnInt();
212 return 0;
213 }
214
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoError.c
11 // Error handling.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
17 // forward declarations
18 //-----------------------------------------------------------------------------
19 static void cxoError_free(cxoError *error);
20 static PyObject *cxoError_str(cxoError *error);
21 static PyObject *cxoError_new(PyTypeObject *type, PyObject *args,
22 PyObject *keywordArgs);
23 static PyObject *cxoError_reduce(cxoError*);
24
25
26 //-----------------------------------------------------------------------------
27 // declaration of methods
28 //-----------------------------------------------------------------------------
29 static PyMethodDef cxoErrorMethods[] = {
30 { "__reduce__", (PyCFunction) cxoError_reduce, METH_NOARGS },
31 { NULL, NULL }
32 };
33
34
35 //-----------------------------------------------------------------------------
36 // declaration of members
37 //-----------------------------------------------------------------------------
38 static PyMemberDef cxoErrorMembers[] = {
39 { "code", T_LONG, offsetof(cxoError, code), READONLY },
40 { "offset", T_UINT, offsetof(cxoError, offset), READONLY },
41 { "message", T_OBJECT, offsetof(cxoError, message), READONLY },
42 { "context", T_OBJECT, offsetof(cxoError, context), READONLY },
43 { "isrecoverable", T_BOOL, offsetof(cxoError, isRecoverable), READONLY },
44 { NULL }
45 };
46
47
48 //-----------------------------------------------------------------------------
49 // declaration of Python type
50 //-----------------------------------------------------------------------------
51 PyTypeObject cxoPyTypeError = {
52 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
93 };
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoFuture.c
6 // Defines the object used for managing behavior changes. This object permits
7 // setting any attribute to any value but only tracks certain values.
8 //-----------------------------------------------------------------------------
9
10 #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
46
47 //-----------------------------------------------------------------------------
48 // cxoFuture_free()
49 // Free the future object and reset global.
50 //-----------------------------------------------------------------------------
51 static void cxoFuture_free(cxoFuture *obj)
52 {
53 Py_TYPE(obj)->tp_free((PyObject*) obj);
54 cxoFutureObj = NULL;
55 }
56
57
58 //-----------------------------------------------------------------------------
59 // cxoFuture_getAttr()
60 // Retrieve an attribute on an object.
61 //-----------------------------------------------------------------------------
62 static PyObject *cxoFuture_getAttr(cxoFuture *obj, PyObject *nameObject)
63 {
64 Py_RETURN_NONE;
65 }
66
67
68 //-----------------------------------------------------------------------------
69 // cxoFuture_setAttr()
70 // Set an attribute on an object.
71 //-----------------------------------------------------------------------------
72 static int cxoFuture_setAttr(cxoFuture *obj, PyObject *nameObject,
73 PyObject *value)
74 {
75 return 0;
76 }
77
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoLob.c
11 // Defines the routines for handling LOB values.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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"
37 //-----------------------------------------------------------------------------
38 static PyMethodDef cxoLobMethods[] = {
39 { "size", (PyCFunction) cxoLob_size, METH_NOARGS },
40 { "open", (PyCFunction) cxoLob_open, METH_NOARGS },
41 { "close", (PyCFunction) cxoLob_close, METH_NOARGS },
42 { "read", (PyCFunction) cxoLob_read, METH_VARARGS | METH_KEYWORDS },
43 { "write", (PyCFunction) cxoLob_write, METH_VARARGS | METH_KEYWORDS },
44 { "trim", (PyCFunction) cxoLob_trim, METH_VARARGS | METH_KEYWORDS },
45 { "getchunksize", (PyCFunction) cxoLob_getChunkSize, METH_NOARGS },
46 { "isopen", (PyCFunction) cxoLob_isOpen, METH_NOARGS },
47 { "getfilename", (PyCFunction) cxoLob_getFileName, METH_NOARGS },
48 { "setfilename", (PyCFunction) cxoLob_setFileName, METH_VARARGS },
49 { "fileexists", (PyCFunction) cxoLob_fileExists, METH_NOARGS },
50 { "__reduce__", (PyCFunction) cxoLob_reduce, METH_NOARGS },
51 { NULL, NULL }
52 };
53
54
55 //-----------------------------------------------------------------------------
56 // Python type declaration
57 //-----------------------------------------------------------------------------
58 PyTypeObject cxoPyTypeLob = {
59 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
100 };
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // Licensed under BSD license (see LICENSE.txt).
9 //-----------------------------------------------------------------------------
10
11 //-----------------------------------------------------------------------------
12 // cxoModule.c
13 // Implementation of cx_Oracle module.
14 //-----------------------------------------------------------------------------
15
16 #include "cxoModule.h"
17
18 // define macro for adding integer constants
19 #define CXO_ADD_INT_CONSTANT(name, value) \
20 if (PyModule_AddIntConstant(module, name, value) < 0) \
21 return NULL;
22
23 // define macro for adding type objects
24 #define CXO_ADD_TYPE_OBJECT(name, type) \
25 Py_INCREF(type); \
26 if (PyModule_AddObject(module, name, (PyObject*) type) < 0) \
27 return NULL;
28
29 // define macro for and making types ready
30 #define CXO_MAKE_TYPE_READY(type) \
31 if (PyType_Ready(type) < 0) \
32 return NULL;
33
34
35 //-----------------------------------------------------------------------------
36 // Globals
37 //-----------------------------------------------------------------------------
38 PyObject *cxoWarningException = NULL;
39 PyObject *cxoErrorException = NULL;
40 PyObject *cxoInterfaceErrorException = NULL;
41 PyObject *cxoDatabaseErrorException = NULL;
42 PyObject *cxoDataErrorException = NULL;
43 PyObject *cxoOperationalErrorException = NULL;
44 PyObject *cxoIntegrityErrorException = NULL;
45 PyObject *cxoInternalErrorException = NULL;
46 PyObject *cxoProgrammingErrorException = NULL;
47 PyObject *cxoNotSupportedErrorException = NULL;
48 PyObject *cxoJsonDumpFunction = NULL;
49 PyObject *cxoJsonLoadFunction = NULL;
50 cxoFuture *cxoFutureObj = NULL;
51 dpiContext *cxoDpiContext = NULL;
52 dpiVersionInfo cxoClientVersionInfo;
53
54 //-----------------------------------------------------------------------------
55 // cxoModule_setException()
56 // Create an exception and set it in the provided dictionary.
57 //-----------------------------------------------------------------------------
58 static int cxoModule_setException(PyObject *module, PyObject **exception,
59 char *name, PyObject *baseException)
60 {
61 char buffer[100];
62
63 sprintf(buffer, "cx_Oracle.%s", name);
64 *exception = PyErr_NewException(buffer, baseException, NULL);
65 if (!*exception)
66 return -1;
67 return PyModule_AddObject(module, name, *exception);
68 }
69
70
71 //-----------------------------------------------------------------------------
72 // cxoModule_makeDSN()
73 // Make a data source name given the host port and SID.
74 //-----------------------------------------------------------------------------
75 static PyObject* cxoModule_makeDSN(PyObject* self, PyObject* args,
76 PyObject* keywordArgs)
77 {
78 static unsigned int numConnectDataArgs = 5;
79 static char *keywordList[] = { "host", "port", "sid", "service_name",
80 "region", "sharding_key", "super_sharding_key", NULL };
81 PyObject *result, *connectData, *hostObj, *portObj;
82 char connectDataFormat[72], *sourcePtr, *targetPtr;
83 PyObject *connectDataArgs[5], *formatArgsArray;
84 unsigned int i;
85
86 // parse arguments
87 for (i = 0; i < numConnectDataArgs; i++)
88 connectDataArgs[i] = NULL;
89 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|OOOOO",
90 keywordList, &hostObj, &portObj, &connectDataArgs[0],
91 &connectDataArgs[1], &connectDataArgs[2], &connectDataArgs[3],
92 &connectDataArgs[4]))
93 return NULL;
94
95 // create list for connect data format arguments
96 formatArgsArray = PyList_New(0);
97 if (!formatArgsArray)
98 return NULL;
99
100 // process each of the connect data arguments
101 // build up a format string and a list of format arguments
102 targetPtr = connectDataFormat;
103 *targetPtr = '\0';
104 for (i = 0; i < numConnectDataArgs; i++) {
105 if (connectDataArgs[i]) {
106 if (PyList_Append(formatArgsArray, connectDataArgs[i]) < 0) {
107 Py_DECREF(formatArgsArray);
108 return NULL;
109 }
110 sourcePtr = keywordList[i + 2];
111 *targetPtr++ = '(';
112 while (*sourcePtr)
113 *targetPtr++ = toupper(*sourcePtr++);
114 *targetPtr++ = '=';
115 *targetPtr++ = '%';
116 *targetPtr++ = 's';
117 *targetPtr++ = ')';
118 *targetPtr = '\0';
119 }
120 }
121
122 // determine connect data
123 connectData = cxoUtils_formatString(connectDataFormat,
124 PyList_AsTuple(formatArgsArray));
125 Py_DECREF(formatArgsArray);
126 if (!connectData)
127 return NULL;
128
129 // perform overall format
130 result = cxoUtils_formatString("(DESCRIPTION=(ADDRESS="
131 "(PROTOCOL=TCP)(HOST=%s)(PORT=%s))(CONNECT_DATA=%s))",
132 PyTuple_Pack(3, hostObj, portObj, connectData));
133 Py_DECREF(connectData);
134 return result;
135 }
136
137
138 //-----------------------------------------------------------------------------
139 // cxoModule_clientVersion()
140 // Return the version of the Oracle client being used as a 5-tuple.
141 //-----------------------------------------------------------------------------
142 static PyObject* cxoModule_clientVersion(PyObject* self, PyObject* args)
143 {
144 if (cxoUtils_initializeDPI() < 0)
145 return NULL;
146 return Py_BuildValue("(iiiii)", cxoClientVersionInfo.versionNum,
147 cxoClientVersionInfo.releaseNum, cxoClientVersionInfo.updateNum,
148 cxoClientVersionInfo.portReleaseNum,
149 cxoClientVersionInfo.portUpdateNum);
150 }
151
152
153 //-----------------------------------------------------------------------------
154 // cxoModule_time()
155 // Returns a time value suitable for binding.
156 //-----------------------------------------------------------------------------
157 static PyObject* cxoModule_time(PyObject* self, PyObject* args)
158 {
159 return cxoError_raiseFromString(cxoNotSupportedErrorException,
160 "Oracle does not support time only variables");
161 }
162
163
164 //-----------------------------------------------------------------------------
165 // cxoModule_timeFromTicks()
166 // Returns a time value suitable for binding.
167 //-----------------------------------------------------------------------------
168 static PyObject* cxoModule_timeFromTicks(PyObject* self, PyObject* args)
169 {
170 return cxoError_raiseFromString(cxoNotSupportedErrorException,
171 "Oracle does not support time only variables");
172 }
173
174
175 //-----------------------------------------------------------------------------
176 // cxoModule_dateFromTicks()
177 // Returns a date value suitable for binding.
178 //-----------------------------------------------------------------------------
179 static PyObject* cxoModule_dateFromTicks(PyObject* self, PyObject* args)
180 {
181 return cxoTransform_dateFromTicks(args);
182 }
183
184
185 //-----------------------------------------------------------------------------
186 // cxoModule_timestampFromTicks()
187 // Returns a date value suitable for binding.
188 //-----------------------------------------------------------------------------
189 static PyObject* cxoModule_timestampFromTicks(PyObject* self, PyObject* args)
190 {
191 return cxoTransform_timestampFromTicks(args);
192 }
193
194
195 //-----------------------------------------------------------------------------
196 // Declaration of methods supported by this module
197 //-----------------------------------------------------------------------------
198 static PyMethodDef cxoModuleMethods[] = {
199 { "makedsn", (PyCFunction) cxoModule_makeDSN,
200 METH_VARARGS | METH_KEYWORDS },
201 { "Time", (PyCFunction) cxoModule_time, METH_VARARGS },
202 { "DateFromTicks", (PyCFunction) cxoModule_dateFromTicks, METH_VARARGS },
203 { "TimeFromTicks", (PyCFunction) cxoModule_timeFromTicks, METH_VARARGS },
204 { "TimestampFromTicks", (PyCFunction) cxoModule_timestampFromTicks,
205 METH_VARARGS },
206 { "clientversion", (PyCFunction) cxoModule_clientVersion, METH_NOARGS },
207 { NULL }
208 };
209
210
211 #if PY_MAJOR_VERSION >= 3
212 //-----------------------------------------------------------------------------
213 // Declaration of module definition for Python 3.x.
214 //-----------------------------------------------------------------------------
215 static struct PyModuleDef cxoModuleDef = {
216 PyModuleDef_HEAD_INIT,
217 "cx_Oracle",
218 NULL,
219 -1,
220 cxoModuleMethods, // methods
221 NULL, // m_reload
222 NULL, // traverse
223 NULL, // clear
224 NULL // free
225 };
226 #endif
227
228
229 //-----------------------------------------------------------------------------
230 // cxoModule_initialize()
231 // Initialization routine for the module.
232 //-----------------------------------------------------------------------------
233 static PyObject *cxoModule_initialize(void)
234 {
235 PyObject *module;
236
237 #ifdef WITH_THREAD
238 PyEval_InitThreads();
239 #endif
240
241 // initialize transforms
242 if (cxoTransform_init() < 0)
243 return NULL;
244
245 // 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);
251 CXO_MAKE_TYPE_READY(&cxoPyTypeConnection);
252 CXO_MAKE_TYPE_READY(&cxoPyTypeCursor);
253 CXO_MAKE_TYPE_READY(&cxoPyTypeCursorVar);
254 CXO_MAKE_TYPE_READY(&cxoPyTypeDateTimeVar);
255 CXO_MAKE_TYPE_READY(&cxoPyTypeDeqOptions);
256 CXO_MAKE_TYPE_READY(&cxoPyTypeEnqOptions);
257 CXO_MAKE_TYPE_READY(&cxoPyTypeError);
258 CXO_MAKE_TYPE_READY(&cxoPyTypeFixedCharVar);
259 CXO_MAKE_TYPE_READY(&cxoPyTypeFixedNcharVar);
260 CXO_MAKE_TYPE_READY(&cxoPyTypeFuture);
261 CXO_MAKE_TYPE_READY(&cxoPyTypeIntervalVar);
262 CXO_MAKE_TYPE_READY(&cxoPyTypeLob);
263 CXO_MAKE_TYPE_READY(&cxoPyTypeLongBinaryVar);
264 CXO_MAKE_TYPE_READY(&cxoPyTypeLongStringVar);
265 CXO_MAKE_TYPE_READY(&cxoPyTypeMsgProps);
266 CXO_MAKE_TYPE_READY(&cxoPyTypeMessage);
267 CXO_MAKE_TYPE_READY(&cxoPyTypeMessageQuery);
268 CXO_MAKE_TYPE_READY(&cxoPyTypeMessageRow);
269 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);
275 CXO_MAKE_TYPE_READY(&cxoPyTypeObjectAttr);
276 CXO_MAKE_TYPE_READY(&cxoPyTypeObject);
277 CXO_MAKE_TYPE_READY(&cxoPyTypeObjectType);
278 CXO_MAKE_TYPE_READY(&cxoPyTypeObjectVar);
279 CXO_MAKE_TYPE_READY(&cxoPyTypeRowidVar);
280 CXO_MAKE_TYPE_READY(&cxoPyTypeSessionPool);
281 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaCollection);
282 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDatabase);
283 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDoc);
284 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDocCursor);
285 CXO_MAKE_TYPE_READY(&cxoPyTypeSodaOperation);
286 CXO_MAKE_TYPE_READY(&cxoPyTypeStringVar);
287 CXO_MAKE_TYPE_READY(&cxoPyTypeSubscr);
288 CXO_MAKE_TYPE_READY(&cxoPyTypeTimestampVar);
289
290 // initialize module and retrieve the dictionary
291 #if PY_MAJOR_VERSION >= 3
292 module = PyModule_Create(&cxoModuleDef);
293 #else
294 module = Py_InitModule("cx_Oracle", cxoModuleMethods);
295 #endif
296 if (!module)
297 return NULL;
298
299 // create exception object and add it to the dictionary
300 if (cxoModule_setException(module, &cxoWarningException,
301 "Warning", CXO_BASE_EXCEPTION) < 0)
302 return NULL;
303 if (cxoModule_setException(module, &cxoErrorException,
304 "Error", CXO_BASE_EXCEPTION) < 0)
305 return NULL;
306 if (cxoModule_setException(module, &cxoInterfaceErrorException,
307 "InterfaceError", cxoErrorException) < 0)
308 return NULL;
309 if (cxoModule_setException(module, &cxoDatabaseErrorException,
310 "DatabaseError", cxoErrorException) < 0)
311 return NULL;
312 if (cxoModule_setException(module, &cxoDataErrorException,
313 "DataError", cxoDatabaseErrorException) < 0)
314 return NULL;
315 if (cxoModule_setException(module, &cxoOperationalErrorException,
316 "OperationalError", cxoDatabaseErrorException) < 0)
317 return NULL;
318 if (cxoModule_setException(module, &cxoIntegrityErrorException,
319 "IntegrityError", cxoDatabaseErrorException) < 0)
320 return NULL;
321 if (cxoModule_setException(module, &cxoInternalErrorException,
322 "InternalError", cxoDatabaseErrorException) < 0)
323 return NULL;
324 if (cxoModule_setException(module, &cxoProgrammingErrorException,
325 "ProgrammingError", cxoDatabaseErrorException) < 0)
326 return NULL;
327 if (cxoModule_setException(module, &cxoNotSupportedErrorException,
328 "NotSupportedError", cxoDatabaseErrorException) < 0)
329 return NULL;
330
331 // set up the types that are available
332 #if PY_MAJOR_VERSION >= 3
333 CXO_ADD_TYPE_OBJECT("Binary", &PyBytes_Type)
334 #else
335 CXO_ADD_TYPE_OBJECT("Binary", &PyBuffer_Type)
336 #endif
337 CXO_ADD_TYPE_OBJECT("Connection", &cxoPyTypeConnection)
338 CXO_ADD_TYPE_OBJECT("Cursor", &cxoPyTypeCursor)
339 CXO_ADD_TYPE_OBJECT("Timestamp", cxoPyTypeDateTime)
340 CXO_ADD_TYPE_OBJECT("Date", cxoPyTypeDate)
341 CXO_ADD_TYPE_OBJECT("SessionPool", &cxoPyTypeSessionPool)
342 CXO_ADD_TYPE_OBJECT("_Error", &cxoPyTypeError)
343 CXO_ADD_TYPE_OBJECT("Object", &cxoPyTypeObject)
344 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)
348 CXO_ADD_TYPE_OBJECT("SodaCollection", &cxoPyTypeSodaCollection)
349 CXO_ADD_TYPE_OBJECT("SodaDatabase", &cxoPyTypeSodaDatabase)
350 CXO_ADD_TYPE_OBJECT("SodaDoc", &cxoPyTypeSodaDoc)
351 CXO_ADD_TYPE_OBJECT("SodaDocCursor", &cxoPyTypeSodaDocCursor)
352 CXO_ADD_TYPE_OBJECT("SodaOperation", &cxoPyTypeSodaOperation)
353
354 // the name "connect" is required by the DB API
355 CXO_ADD_TYPE_OBJECT("connect", &cxoPyTypeConnection)
356
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)
380
381 // create constants required by Python DB API 2.0
382 if (PyModule_AddStringConstant(module, "apilevel", "2.0") < 0)
383 return NULL;
384 if (PyModule_AddIntConstant(module, "threadsafety", 2) < 0)
385 return NULL;
386 if (PyModule_AddStringConstant(module, "paramstyle", "named") < 0)
387 return NULL;
388
389 // add version and build time for easier support
390 if (PyModule_AddStringConstant(module, "version",
391 CXO_BUILD_VERSION_STRING) < 0)
392 return NULL;
393 if (PyModule_AddStringConstant(module, "__version__",
394 CXO_BUILD_VERSION_STRING) < 0)
395 return NULL;
396 if (PyModule_AddStringConstant(module, "buildtime",
397 __DATE__ " " __TIME__) < 0)
398 return NULL;
399
400 // create and initialize future object
401 cxoFutureObj = (cxoFuture*) cxoPyTypeFuture.tp_alloc(&cxoPyTypeFuture, 0);
402 if (!cxoFutureObj)
403 return NULL;
404 if (PyModule_AddObject(module, "__future__", (PyObject*) cxoFutureObj) < 0)
405 return NULL;
406
407 // add constants for authorization modes
408 CXO_ADD_INT_CONSTANT("SYSASM", DPI_MODE_AUTH_SYSASM)
409 CXO_ADD_INT_CONSTANT("SYSBKP", DPI_MODE_AUTH_SYSBKP)
410 CXO_ADD_INT_CONSTANT("SYSDBA", DPI_MODE_AUTH_SYSDBA)
411 CXO_ADD_INT_CONSTANT("SYSDGD", DPI_MODE_AUTH_SYSDGD)
412 CXO_ADD_INT_CONSTANT("SYSKMT", DPI_MODE_AUTH_SYSKMT)
413 CXO_ADD_INT_CONSTANT("SYSOPER", DPI_MODE_AUTH_SYSOPER)
414 CXO_ADD_INT_CONSTANT("SYSRAC", DPI_MODE_AUTH_SYSRAC)
415 CXO_ADD_INT_CONSTANT("PRELIM_AUTH", DPI_MODE_AUTH_PRELIM)
416
417 // add constants for session pool get modes
418 CXO_ADD_INT_CONSTANT("SPOOL_ATTRVAL_WAIT", DPI_MODE_POOL_GET_WAIT)
419 CXO_ADD_INT_CONSTANT("SPOOL_ATTRVAL_NOWAIT", DPI_MODE_POOL_GET_NOWAIT)
420 CXO_ADD_INT_CONSTANT("SPOOL_ATTRVAL_FORCEGET", DPI_MODE_POOL_GET_FORCEGET)
421 CXO_ADD_INT_CONSTANT("SPOOL_ATTRVAL_TIMEDWAIT",
422 DPI_MODE_POOL_GET_TIMEDWAIT)
423
424 // add constants for database shutdown modes
425 CXO_ADD_INT_CONSTANT("DBSHUTDOWN_ABORT", DPI_MODE_SHUTDOWN_ABORT)
426 CXO_ADD_INT_CONSTANT("DBSHUTDOWN_FINAL", DPI_MODE_SHUTDOWN_FINAL)
427 CXO_ADD_INT_CONSTANT("DBSHUTDOWN_IMMEDIATE", DPI_MODE_SHUTDOWN_IMMEDIATE)
428 CXO_ADD_INT_CONSTANT("DBSHUTDOWN_TRANSACTIONAL",
429 DPI_MODE_SHUTDOWN_TRANSACTIONAL)
430 CXO_ADD_INT_CONSTANT("DBSHUTDOWN_TRANSACTIONAL_LOCAL",
431 DPI_MODE_SHUTDOWN_TRANSACTIONAL_LOCAL)
432
433 // add constants for purity
434 CXO_ADD_INT_CONSTANT("ATTR_PURITY_DEFAULT", DPI_PURITY_DEFAULT)
435 CXO_ADD_INT_CONSTANT("ATTR_PURITY_NEW", DPI_PURITY_NEW)
436 CXO_ADD_INT_CONSTANT("ATTR_PURITY_SELF", DPI_PURITY_SELF)
437
438 // add constants for subscription protocols
439 CXO_ADD_INT_CONSTANT("SUBSCR_PROTO_OCI", DPI_SUBSCR_PROTO_CALLBACK)
440 CXO_ADD_INT_CONSTANT("SUBSCR_PROTO_MAIL", DPI_SUBSCR_PROTO_MAIL)
441 CXO_ADD_INT_CONSTANT("SUBSCR_PROTO_SERVER", DPI_SUBSCR_PROTO_PLSQL)
442 CXO_ADD_INT_CONSTANT("SUBSCR_PROTO_HTTP", DPI_SUBSCR_PROTO_HTTP)
443
444 // add constants for subscription quality of service
445 CXO_ADD_INT_CONSTANT("SUBSCR_QOS_RELIABLE", DPI_SUBSCR_QOS_RELIABLE)
446 CXO_ADD_INT_CONSTANT("SUBSCR_QOS_DEREG_NFY", DPI_SUBSCR_QOS_DEREG_NFY)
447 CXO_ADD_INT_CONSTANT("SUBSCR_QOS_ROWIDS", DPI_SUBSCR_QOS_ROWIDS)
448 CXO_ADD_INT_CONSTANT("SUBSCR_QOS_QUERY", DPI_SUBSCR_QOS_QUERY)
449 CXO_ADD_INT_CONSTANT("SUBSCR_QOS_BEST_EFFORT", DPI_SUBSCR_QOS_BEST_EFFORT)
450
451 // add constants for subscription namespaces
452 CXO_ADD_INT_CONSTANT("SUBSCR_NAMESPACE_AQ", DPI_SUBSCR_NAMESPACE_AQ)
453 CXO_ADD_INT_CONSTANT("SUBSCR_NAMESPACE_DBCHANGE",
454 DPI_SUBSCR_NAMESPACE_DBCHANGE)
455
456 // add constants for subscription grouping classes
457 CXO_ADD_INT_CONSTANT("SUBSCR_GROUPING_CLASS_TIME",
458 DPI_SUBSCR_GROUPING_CLASS_TIME)
459
460 // add constants for subscription grouping types
461 CXO_ADD_INT_CONSTANT("SUBSCR_GROUPING_TYPE_SUMMARY",
462 DPI_SUBSCR_GROUPING_TYPE_SUMMARY)
463 CXO_ADD_INT_CONSTANT("SUBSCR_GROUPING_TYPE_LAST",
464 DPI_SUBSCR_GROUPING_TYPE_LAST)
465
466 // add constants for event types
467 CXO_ADD_INT_CONSTANT("EVENT_NONE", DPI_EVENT_NONE)
468 CXO_ADD_INT_CONSTANT("EVENT_STARTUP", DPI_EVENT_STARTUP)
469 CXO_ADD_INT_CONSTANT("EVENT_SHUTDOWN", DPI_EVENT_SHUTDOWN)
470 CXO_ADD_INT_CONSTANT("EVENT_SHUTDOWN_ANY", DPI_EVENT_SHUTDOWN_ANY)
471 CXO_ADD_INT_CONSTANT("EVENT_DEREG", DPI_EVENT_DEREG)
472 CXO_ADD_INT_CONSTANT("EVENT_OBJCHANGE", DPI_EVENT_OBJCHANGE)
473 CXO_ADD_INT_CONSTANT("EVENT_QUERYCHANGE", DPI_EVENT_QUERYCHANGE)
474 CXO_ADD_INT_CONSTANT("EVENT_AQ", DPI_EVENT_AQ)
475
476 // add constants for opcodes
477 CXO_ADD_INT_CONSTANT("OPCODE_ALLOPS", DPI_OPCODE_ALL_OPS)
478 CXO_ADD_INT_CONSTANT("OPCODE_ALLROWS", DPI_OPCODE_ALL_ROWS)
479 CXO_ADD_INT_CONSTANT("OPCODE_INSERT", DPI_OPCODE_INSERT)
480 CXO_ADD_INT_CONSTANT("OPCODE_UPDATE", DPI_OPCODE_UPDATE)
481 CXO_ADD_INT_CONSTANT("OPCODE_DELETE", DPI_OPCODE_DELETE)
482 CXO_ADD_INT_CONSTANT("OPCODE_ALTER", DPI_OPCODE_ALTER)
483 CXO_ADD_INT_CONSTANT("OPCODE_DROP", DPI_OPCODE_DROP)
484
485 // add constants for AQ dequeue modes
486 CXO_ADD_INT_CONSTANT("DEQ_BROWSE", DPI_MODE_DEQ_BROWSE)
487 CXO_ADD_INT_CONSTANT("DEQ_LOCKED", DPI_MODE_DEQ_LOCKED)
488 CXO_ADD_INT_CONSTANT("DEQ_REMOVE", DPI_MODE_DEQ_REMOVE)
489 CXO_ADD_INT_CONSTANT("DEQ_REMOVE_NODATA", DPI_MODE_DEQ_REMOVE_NO_DATA)
490
491 // add constants for AQ dequeue navigation
492 CXO_ADD_INT_CONSTANT("DEQ_FIRST_MSG", DPI_DEQ_NAV_FIRST_MSG)
493 CXO_ADD_INT_CONSTANT("DEQ_NEXT_TRANSACTION", DPI_DEQ_NAV_NEXT_TRANSACTION)
494 CXO_ADD_INT_CONSTANT("DEQ_NEXT_MSG", DPI_DEQ_NAV_NEXT_MSG)
495
496 // add constants for AQ dequeue visibility
497 CXO_ADD_INT_CONSTANT("DEQ_IMMEDIATE", DPI_VISIBILITY_IMMEDIATE)
498 CXO_ADD_INT_CONSTANT("DEQ_ON_COMMIT", DPI_VISIBILITY_ON_COMMIT)
499
500 // add constants for AQ dequeue wait
501 CXO_ADD_INT_CONSTANT("DEQ_NO_WAIT", DPI_DEQ_WAIT_NO_WAIT)
502 CXO_ADD_INT_CONSTANT("DEQ_WAIT_FOREVER", DPI_DEQ_WAIT_FOREVER)
503
504 // add constants for AQ enqueue visibility
505 CXO_ADD_INT_CONSTANT("ENQ_IMMEDIATE", DPI_VISIBILITY_IMMEDIATE)
506 CXO_ADD_INT_CONSTANT("ENQ_ON_COMMIT", DPI_VISIBILITY_ON_COMMIT)
507
508 // add constants for AQ table purge mode (message)
509 CXO_ADD_INT_CONSTANT("MSG_PERSISTENT", DPI_MODE_MSG_PERSISTENT)
510 CXO_ADD_INT_CONSTANT("MSG_BUFFERED", DPI_MODE_MSG_BUFFERED)
511 CXO_ADD_INT_CONSTANT("MSG_PERSISTENT_OR_BUFFERED",
512 DPI_MODE_MSG_PERSISTENT_OR_BUFFERED)
513
514 // add constants for AQ message state
515 CXO_ADD_INT_CONSTANT("MSG_EXPIRED", DPI_MSG_STATE_EXPIRED)
516 CXO_ADD_INT_CONSTANT("MSG_READY", DPI_MSG_STATE_READY)
517 CXO_ADD_INT_CONSTANT("MSG_PROCESSED", DPI_MSG_STATE_PROCESSED)
518 CXO_ADD_INT_CONSTANT("MSG_WAITING", DPI_MSG_STATE_WAITING)
519
520 // add special constants for AQ delay/expiration
521 CXO_ADD_INT_CONSTANT("MSG_NO_DELAY", 0)
522 CXO_ADD_INT_CONSTANT("MSG_NO_EXPIRATION", -1)
523
524 return module;
525 }
526
527
528 //-----------------------------------------------------------------------------
529 // Start routine for the module.
530 //-----------------------------------------------------------------------------
531 #if PY_MAJOR_VERSION >= 3
532 PyMODINIT_FUNC PyInit_cx_Oracle(void)
533 {
534 return cxoModule_initialize();
535 }
536 #else
537 void initcx_Oracle(void)
538 {
539 cxoModule_initialize();
540 }
541 #endif
542
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 //
3 // Licensed under BSD license (see LICENSE.txt).
4 //-----------------------------------------------------------------------------
5
6 //-----------------------------------------------------------------------------
7 // cxoModule.h
8 // Include file for all cx_Oracle source files.
9 //-----------------------------------------------------------------------------
10
11 #include <Python.h>
12 #include <structmember.h>
13 #include <time.h>
14 #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
51
52 // define macros to get the build version as a string and the driver name
53 #define xstr(s) str(s)
54 #define str(s) #s
55 #define CXO_BUILD_VERSION_STRING xstr(CXO_BUILD_VERSION)
56 #define CXO_DRIVER_NAME "cx_Oracle : "CXO_BUILD_VERSION_STRING
57
58 // define macro for clearing buffers
59 #define cxoBuffer_clear(buf) Py_CLEAR((buf)->obj)
60
61
62 //-----------------------------------------------------------------------------
63 // Forward Declarations
64 //-----------------------------------------------------------------------------
65 typedef struct cxoBuffer cxoBuffer;
66 typedef struct cxoError cxoError;
67 typedef struct cxoConnection cxoConnection;
68 typedef struct cxoCursor cxoCursor;
69 typedef struct cxoDeqOptions cxoDeqOptions;
70 typedef struct cxoEnqOptions cxoEnqOptions;
71 typedef struct cxoFuture cxoFuture;
72 typedef struct cxoLob cxoLob;
73 typedef struct cxoMessage cxoMessage;
74 typedef struct cxoMessageQuery cxoMessageQuery;
75 typedef struct cxoMessageRow cxoMessageRow;
76 typedef struct cxoMessageTable cxoMessageTable;
77 typedef struct cxoMsgProps cxoMsgProps;
78 typedef struct cxoObject cxoObject;
79 typedef struct cxoObjectAttr cxoObjectAttr;
80 typedef struct cxoObjectType cxoObjectType;
81 typedef struct cxoSessionPool cxoSessionPool;
82 typedef struct cxoSodaCollection cxoSodaCollection;
83 typedef struct cxoSodaDatabase cxoSodaDatabase;
84 typedef struct cxoSodaDoc cxoSodaDoc;
85 typedef struct cxoSodaDocCursor cxoSodaDocCursor;
86 typedef struct cxoSodaOperation cxoSodaOperation;
87 typedef struct cxoSubscr cxoSubscr;
88 typedef struct cxoVar cxoVar;
89 typedef struct cxoVarType cxoVarType;
90
91
92 //-----------------------------------------------------------------------------
93 // Globals
94 //-----------------------------------------------------------------------------
95
96 // exception objects
97 extern PyObject *cxoWarningException;
98 extern PyObject *cxoErrorException;
99 extern PyObject *cxoInterfaceErrorException;
100 extern PyObject *cxoDatabaseErrorException;
101 extern PyObject *cxoDataErrorException;
102 extern PyObject *cxoOperationalErrorException;
103 extern PyObject *cxoIntegrityErrorException;
104 extern PyObject *cxoInternalErrorException;
105 extern PyObject *cxoProgrammingErrorException;
106 extern PyObject *cxoNotSupportedErrorException;
107
108 // type objects
109 extern PyTypeObject cxoPyTypeBfileVar;
110 extern PyTypeObject cxoPyTypeBinaryVar;
111 extern PyTypeObject cxoPyTypeBlobVar;
112 extern PyTypeObject cxoPyTypeBooleanVar;
113 extern PyTypeObject cxoPyTypeClobVar;
114 extern PyTypeObject cxoPyTypeConnection;
115 extern PyTypeObject cxoPyTypeCursor;
116 extern PyTypeObject cxoPyTypeCursorVar;
117 extern PyTypeObject cxoPyTypeDateTimeVar;
118 extern PyTypeObject cxoPyTypeDeqOptions;
119 extern PyTypeObject cxoPyTypeEnqOptions;
120 extern PyTypeObject cxoPyTypeError;
121 extern PyTypeObject cxoPyTypeFixedCharVar;
122 extern PyTypeObject cxoPyTypeFixedNcharVar;
123 extern PyTypeObject cxoPyTypeFuture;
124 extern PyTypeObject cxoPyTypeIntervalVar;
125 extern PyTypeObject cxoPyTypeLob;
126 extern PyTypeObject cxoPyTypeLongBinaryVar;
127 extern PyTypeObject cxoPyTypeLongStringVar;
128 extern PyTypeObject cxoPyTypeMsgProps;
129 extern PyTypeObject cxoPyTypeMessage;
130 extern PyTypeObject cxoPyTypeMessageQuery;
131 extern PyTypeObject cxoPyTypeMessageRow;
132 extern PyTypeObject cxoPyTypeMessageTable;
133 extern PyTypeObject cxoPyTypeNativeFloatVar;
134 extern PyTypeObject cxoPyTypeNativeIntVar;
135 extern PyTypeObject cxoPyTypeNcharVar;
136 extern PyTypeObject cxoPyTypeNclobVar;
137 extern PyTypeObject cxoPyTypeNumberVar;
138 extern PyTypeObject cxoPyTypeObject;
139 extern PyTypeObject cxoPyTypeObjectAttr;
140 extern PyTypeObject cxoPyTypeObjectType;
141 extern PyTypeObject cxoPyTypeObjectVar;
142 extern PyTypeObject cxoPyTypeRowidVar;
143 extern PyTypeObject cxoPyTypeSessionPool;
144 extern PyTypeObject cxoPyTypeSodaCollection;
145 extern PyTypeObject cxoPyTypeSodaDatabase;
146 extern PyTypeObject cxoPyTypeSodaDoc;
147 extern PyTypeObject cxoPyTypeSodaDocCursor;
148 extern PyTypeObject cxoPyTypeSodaOperation;
149 extern PyTypeObject cxoPyTypeStringVar;
150 extern PyTypeObject cxoPyTypeSubscr;
151 extern PyTypeObject cxoPyTypeTimestampVar;
152
153 // datetime types
154 extern PyTypeObject *cxoPyTypeDate;
155 extern PyTypeObject *cxoPyTypeDateTime;
156
157 // JSON dump and load functions for use with SODA
158 extern PyObject *cxoJsonDumpFunction;
159 extern PyObject *cxoJsonLoadFunction;
160
161 // ODPI-C context and version information
162 extern dpiContext *cxoDpiContext;
163 extern dpiVersionInfo cxoClientVersionInfo;
164
165 // future object
166 extern cxoFuture *cxoFutureObj;
167
168
169 //-----------------------------------------------------------------------------
170 // Transforms
171 //-----------------------------------------------------------------------------
172 typedef enum {
173 CXO_TRANSFORM_NONE = 0,
174 CXO_TRANSFORM_BINARY,
175 CXO_TRANSFORM_BFILE,
176 CXO_TRANSFORM_BLOB,
177 CXO_TRANSFORM_BOOLEAN,
178 CXO_TRANSFORM_CLOB,
179 CXO_TRANSFORM_CURSOR,
180 CXO_TRANSFORM_DATE,
181 CXO_TRANSFORM_DATETIME,
182 CXO_TRANSFORM_DECIMAL,
183 CXO_TRANSFORM_FIXED_CHAR,
184 CXO_TRANSFORM_FIXED_NCHAR,
185 CXO_TRANSFORM_FLOAT,
186 CXO_TRANSFORM_INT,
187 CXO_TRANSFORM_LONG_BINARY,
188 CXO_TRANSFORM_LONG_STRING,
189 CXO_TRANSFORM_NATIVE_DOUBLE,
190 CXO_TRANSFORM_NATIVE_FLOAT,
191 CXO_TRANSFORM_NATIVE_INT,
192 CXO_TRANSFORM_NCLOB,
193 CXO_TRANSFORM_NSTRING,
194 CXO_TRANSFORM_OBJECT,
195 CXO_TRANSFORM_ROWID,
196 CXO_TRANSFORM_STRING,
197 CXO_TRANSFORM_TIMEDELTA,
198 CXO_TRANSFORM_TIMESTAMP,
199 CXO_TRANSFORM_TIMESTAMP_LTZ,
200 CXO_TRANSFORM_UNSUPPORTED
201 } cxoTransformNum;
202
203
204 //-----------------------------------------------------------------------------
205 // Structures
206 //-----------------------------------------------------------------------------
207 struct cxoBuffer {
208 const char *ptr;
209 uint32_t numCharacters;
210 uint32_t size;
211 PyObject *obj;
212 };
213
214 struct cxoError {
215 PyObject_HEAD
216 long code;
217 unsigned offset;
218 PyObject *message;
219 PyObject *context;
220 char isRecoverable;
221 };
222
223 struct cxoConnection {
224 PyObject_HEAD
225 dpiConn *handle;
226 cxoSessionPool *sessionPool;
227 PyObject *inputTypeHandler;
228 PyObject *outputTypeHandler;
229 PyObject *username;
230 PyObject *dsn;
231 PyObject *version;
232 PyObject *tag;
233 dpiEncodingInfo encodingInfo;
234 int autocommit;
235 };
236
237 struct cxoCursor {
238 PyObject_HEAD
239 dpiStmt *handle;
240 dpiStmtInfo stmtInfo;
241 cxoConnection *connection;
242 PyObject *statement;
243 PyObject *statementTag;
244 PyObject *bindVariables;
245 PyObject *fetchVariables;
246 PyObject *rowFactory;
247 PyObject *inputTypeHandler;
248 PyObject *outputTypeHandler;
249 uint32_t arraySize;
250 uint32_t bindArraySize;
251 uint32_t fetchArraySize;
252 int setInputSizes;
253 uint64_t rowCount;
254 uint32_t fetchBufferRowIndex;
255 uint32_t numRowsInFetchBuffer;
256 int moreRowsToFetch;
257 int isScrollable;
258 int fixupRefCursor;
259 int isOpen;
260 };
261
262 struct cxoDeqOptions {
263 PyObject_HEAD
264 dpiDeqOptions *handle;
265 const char *encoding;
266 };
267
268 struct cxoEnqOptions {
269 PyObject_HEAD
270 dpiEnqOptions *handle;
271 const char *encoding;
272 };
273
274 struct cxoFuture {
275 PyObject_HEAD
276 };
277
278 struct cxoLob {
279 PyObject_HEAD
280 cxoConnection *connection;
281 dpiOracleTypeNum oracleTypeNum;
282 dpiLob *handle;
283 };
284
285 struct cxoMessage {
286 PyObject_HEAD
287 cxoSubscr *subscription;
288 dpiEventType type;
289 PyObject *dbname;
290 PyObject *txId;
291 PyObject *tables;
292 PyObject *queries;
293 PyObject *queueName;
294 PyObject *consumerName;
295 int registered;
296 };
297
298 struct cxoMessageQuery {
299 PyObject_HEAD
300 uint64_t id;
301 dpiOpCode operation;
302 PyObject *tables;
303 };
304
305 struct cxoMessageRow {
306 PyObject_HEAD
307 PyObject *rowid;
308 dpiOpCode operation;
309 };
310
311 struct cxoMessageTable {
312 PyObject_HEAD
313 PyObject *name;
314 PyObject *rows;
315 dpiOpCode operation;
316 };
317
318 struct cxoMsgProps {
319 PyObject_HEAD
320 dpiMsgProps *handle;
321 const char *encoding;
322 };
323
324 struct cxoObject {
325 PyObject_HEAD
326 cxoObjectType *objectType;
327 dpiObject *handle;
328 };
329
330 struct cxoObjectAttr {
331 PyObject_HEAD
332 PyObject *name;
333 dpiObjectAttr *handle;
334 dpiOracleTypeNum oracleTypeNum;
335 cxoTransformNum transformNum;
336 cxoObjectType *type;
337 };
338
339 struct cxoObjectType {
340 PyObject_HEAD
341 dpiObjectType *handle;
342 PyObject *schema;
343 PyObject *name;
344 PyObject *attributes;
345 PyObject *attributesByName;
346 cxoConnection *connection;
347 dpiOracleTypeNum elementOracleTypeNum;
348 cxoTransformNum elementTransformNum;
349 PyObject *elementType;
350 char isCollection;
351 };
352
353 struct cxoSessionPool {
354 PyObject_HEAD
355 dpiPool *handle;
356 uint32_t minSessions;
357 uint32_t maxSessions;
358 uint32_t sessionIncrement;
359 uint32_t cacheSize;
360 dpiEncodingInfo encodingInfo;
361 int homogeneous;
362 int externalAuth;
363 PyObject *username;
364 PyObject *dsn;
365 PyObject *name;
366 PyObject *sessionCallback;
367 PyTypeObject *connectionType;
368 };
369
370 struct cxoSodaCollection {
371 PyObject_HEAD
372 dpiSodaColl *handle;
373 cxoSodaDatabase *db;
374 PyObject *name;
375 };
376
377 struct cxoSodaDatabase {
378 PyObject_HEAD
379 dpiSodaDb *handle;
380 cxoConnection *connection;
381 };
382
383 struct cxoSodaDoc {
384 PyObject_HEAD
385 cxoSodaDatabase *db;
386 dpiSodaDoc *handle;
387 };
388
389 struct cxoSodaDocCursor {
390 PyObject_HEAD
391 cxoSodaDatabase *db;
392 dpiSodaDocCursor *handle;
393 };
394
395 struct cxoSodaOperation {
396 PyObject_HEAD
397 cxoSodaCollection *coll;
398 dpiSodaOperOptions options;
399 uint32_t numKeyBuffers;
400 cxoBuffer *keyBuffers;
401 cxoBuffer keyBuffer;
402 cxoBuffer versionBuffer;
403 cxoBuffer filterBuffer;
404 };
405
406
407 struct cxoSubscr {
408 PyObject_HEAD
409 dpiSubscr *handle;
410 cxoConnection *connection;
411 PyObject *callback;
412 uint32_t namespace;
413 PyObject *name;
414 uint32_t protocol;
415 PyObject *ipAddress;
416 uint32_t port;
417 uint32_t timeout;
418 uint32_t operations;
419 uint32_t qos;
420 uint8_t groupingClass;
421 uint32_t groupingValue;
422 uint8_t groupingType;
423 uint64_t id;
424 };
425
426 struct cxoVar {
427 PyObject_HEAD
428 dpiVar *handle;
429 dpiData *data;
430 cxoConnection *connection;
431 PyObject *inConverter;
432 PyObject *outConverter;
433 cxoObjectType *objectType;
434 const char *encodingErrors;
435 uint32_t allocatedElements;
436 uint32_t size;
437 uint32_t bufferSize;
438 int isArray;
439 int isValueSet;
440 int getReturnedData;
441 cxoVarType *type;
442 };
443
444 struct cxoVarType {
445 cxoTransformNum transformNum;
446 PyTypeObject *pythonType;
447 uint32_t size;
448 };
449
450
451 //-----------------------------------------------------------------------------
452 // Functions
453 //-----------------------------------------------------------------------------
454 int cxoBuffer_fromObject(cxoBuffer *buf, PyObject *obj, const char *encoding);
455 int cxoBuffer_init(cxoBuffer *buf);
456
457 int cxoConnection_getSodaFlags(cxoConnection *conn, uint32_t *flags);
458 int cxoConnection_isConnected(cxoConnection *conn);
459
460 int cxoCursor_performBind(cxoCursor *cursor);
461 int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters,
462 unsigned numElements, unsigned arrayPos, int deferTypeAssignment);
463
464 cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection);
465
466 cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection);
467
468 cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo);
469 int cxoError_raiseAndReturnInt(void);
470 PyObject *cxoError_raiseAndReturnNull(void);
471 int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo);
472 PyObject *cxoError_raiseFromString(PyObject *exceptionType,
473 const char *message);
474
475 PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
476 dpiLob *handle);
477
478 cxoMsgProps *cxoMsgProps_new(cxoConnection*);
479
480 int cxoObject_internalExtend(cxoObject *obj, PyObject *sequence);
481 PyObject *cxoObject_new(cxoObjectType *objectType, dpiObject *handle);
482
483 cxoObjectAttr *cxoObjectAttr_new(cxoConnection *connection,
484 dpiObjectAttr *handle);
485
486 cxoObjectType *cxoObjectType_new(cxoConnection *connection,
487 dpiObjectType *handle);
488 cxoObjectType *cxoObjectType_newByName(cxoConnection *connection,
489 PyObject *name);
490
491 cxoSodaCollection *cxoSodaCollection_new(cxoSodaDatabase *db,
492 dpiSodaColl *handle);
493
494 cxoSodaDatabase *cxoSodaDatabase_new(cxoConnection *connection);
495
496 cxoSodaDoc *cxoSodaDoc_new(cxoSodaDatabase *db, dpiSodaDoc *handle);
497
498 cxoSodaDocCursor *cxoSodaDocCursor_new(cxoSodaDatabase *db,
499 dpiSodaDocCursor *handle);
500
501 cxoSodaOperation *cxoSodaOperation_new(cxoSodaCollection *collection);
502
503 void cxoSubscr_callback(cxoSubscr *subscr, dpiSubscrMessage *message);
504
505 PyObject *cxoTransform_dateFromTicks(PyObject *args);
506 int cxoTransform_fromPython(cxoTransformNum transformNum, PyObject *pyValue,
507 dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding,
508 const char *nencoding, cxoVar *var, uint32_t arrayPos);
509 cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info);
510 cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type);
511 cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql);
512 void cxoTransform_getTypeInfo(cxoTransformNum transformNum,
513 dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum);
514 int cxoTransform_init(void);
515 PyObject *cxoTransform_timestampFromTicks(PyObject *args);
516 PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
517 cxoConnection *connection, cxoObjectType *objType,
518 dpiDataBuffer *dbValue, const char *encodingErrors);
519
520 PyObject *cxoUtils_formatString(const char *format, PyObject *args);
521 const char *cxoUtils_getAdjustedEncoding(const char *encoding);
522 int cxoUtils_getBooleanValue(PyObject *obj, int defaultValue, int *value);
523 int cxoUtils_getModuleAndName(PyTypeObject *type, PyObject **module,
524 PyObject **name);
525 int cxoUtils_initializeDPI(void);
526 int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer);
527 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);
534
535 int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos);
536 int cxoVar_check(PyObject *object);
537 PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos);
538 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);
541 cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
542 uint32_t numElements);
543 cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
544 Py_ssize_t numElements);
545 int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value);
546
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoMsgProps.c
11 // Implements the message properties object used in Advanced Queuing.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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[] = {
42 { "attempts", (getter) cxoMsgProps_getNumAttempts, 0, 0, 0 },
43 { "correlation", (getter) cxoMsgProps_getCorrelation,
44 (setter) cxoMsgProps_setCorrelation, 0, 0 },
45 { "delay", (getter) cxoMsgProps_getDelay, (setter) cxoMsgProps_setDelay, 0,
46 0 },
47 { "deliverymode", (getter) cxoMsgProps_getDeliveryMode, 0, 0, 0 },
48 { "enqtime", (getter) cxoMsgProps_getEnqTime, 0, 0, 0 },
49 { "exceptionq", (getter) cxoMsgProps_getExceptionQ,
50 (setter) cxoMsgProps_setExceptionQ, 0, 0 },
51 { "expiration", (getter) cxoMsgProps_getExpiration,
52 (setter) cxoMsgProps_setExpiration, 0, 0 },
53 { "msgid", (getter) cxoMsgProps_getOriginalMsgId,
54 (setter) cxoMsgProps_setOriginalMsgId, 0, 0 },
55 { "priority", (getter) cxoMsgProps_getPriority,
56 (setter) cxoMsgProps_setPriority, 0, 0 },
57 { "state", (getter) cxoMsgProps_getState, 0, 0, 0 },
58 { NULL }
59 };
60
61
62 //-----------------------------------------------------------------------------
63 // Python type declarations
64 //-----------------------------------------------------------------------------
65 PyTypeObject cxoPyTypeMsgProps = {
66 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
107 };
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoObject.c
11 // Defines the routines for handling objects in Oracle.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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"
42 //-----------------------------------------------------------------------------
43 static PyMethodDef cxoObjectMethods[] = {
44 { "append", (PyCFunction) cxoObject_append, METH_O },
45 { "asdict", (PyCFunction) cxoObject_asDict, METH_NOARGS },
46 { "aslist", (PyCFunction) cxoObject_asList, METH_NOARGS },
47 { "copy", (PyCFunction) cxoObject_copy, METH_NOARGS },
48 { "delete", (PyCFunction) cxoObject_delete, METH_VARARGS },
49 { "exists", (PyCFunction) cxoObject_exists, METH_VARARGS },
50 { "extend", (PyCFunction) cxoObject_extend, METH_O },
51 { "first", (PyCFunction) cxoObject_getFirstIndex, METH_NOARGS },
52 { "getelement", (PyCFunction) cxoObject_getElement, METH_VARARGS },
53 { "last", (PyCFunction) cxoObject_getLastIndex, METH_NOARGS },
54 { "next", (PyCFunction) cxoObject_getNextIndex, METH_VARARGS },
55 { "prev", (PyCFunction) cxoObject_getPrevIndex, METH_VARARGS },
56 { "setelement", (PyCFunction) cxoObject_setElement, METH_VARARGS },
57 { "size", (PyCFunction) cxoObject_getSize, METH_NOARGS },
58 { "trim", (PyCFunction) cxoObject_trim, METH_VARARGS },
59 { NULL, NULL }
60 };
61
62
63 //-----------------------------------------------------------------------------
64 // Declaration of members for Python type "Object".
65 //-----------------------------------------------------------------------------
66 static PyMemberDef cxoObjectMembers[] = {
67 { "type", T_OBJECT, offsetof(cxoObject, objectType), READONLY },
68 { NULL }
69 };
70
71
72 //-----------------------------------------------------------------------------
73 // Python type declaration
74 //-----------------------------------------------------------------------------
75 PyTypeObject cxoPyTypeObject = {
76 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
105 };
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoObjectAttr.c
6 // Defines the routines for handling attributes of Oracle types.
7 //-----------------------------------------------------------------------------
8
9 #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
74
75 //-----------------------------------------------------------------------------
76 // cxoObjectAttr_initialize()
77 // Initialize the new object attribute.
78 //-----------------------------------------------------------------------------
79 static int cxoObjectAttr_initialize(cxoObjectAttr *attr,
80 cxoConnection *connection)
81 {
82 dpiObjectAttrInfo info;
83
84 if (dpiObjectAttr_getInfo(attr->handle, &info) < 0)
85 return cxoError_raiseAndReturnInt();
86 attr->transformNum = cxoTransform_getNumFromDataTypeInfo(&info.typeInfo);
87 attr->oracleTypeNum = info.typeInfo.oracleTypeNum;
88 attr->name = cxoPyString_fromEncodedString(info.name, info.nameLength,
89 connection->encodingInfo.encoding, NULL);
90 if (!attr->name)
91 return -1;
92 if (info.typeInfo.objectType) {
93 attr->type = cxoObjectType_new(connection,
94 info.typeInfo.objectType);
95 if (!attr->type)
96 return -1;
97 }
98
99 return 0;
100 }
101
102
103 //-----------------------------------------------------------------------------
104 // cxoObjectAttr_new()
105 // Allocate a new object attribute.
106 //-----------------------------------------------------------------------------
107 cxoObjectAttr *cxoObjectAttr_new(cxoConnection *connection,
108 dpiObjectAttr *handle)
109 {
110 cxoObjectAttr *attr;
111
112 attr = (cxoObjectAttr*)
113 cxoPyTypeObjectAttr.tp_alloc(&cxoPyTypeObjectAttr, 0);
114 if (!attr) {
115 dpiObjectAttr_release(handle);
116 return NULL;
117 }
118 attr->handle = handle;
119 if (cxoObjectAttr_initialize(attr, connection) < 0) {
120 Py_DECREF(attr);
121 return NULL;
122 }
123
124 return attr;
125 }
126
127
128 //-----------------------------------------------------------------------------
129 // cxoObjectAttr_free()
130 // Free the memory associated with an object attribute.
131 //-----------------------------------------------------------------------------
132 static void cxoObjectAttr_free(cxoObjectAttr *attr)
133 {
134 if (attr->handle) {
135 dpiObjectAttr_release(attr->handle);
136 attr->handle = NULL;
137 }
138 Py_CLEAR(attr->name);
139 Py_CLEAR(attr->type);
140 Py_TYPE(attr)->tp_free((PyObject*) attr);
141 }
142
143
144 //-----------------------------------------------------------------------------
145 // cxoObjectAttr_repr()
146 // Return a string representation of the object attribute.
147 //-----------------------------------------------------------------------------
148 static PyObject *cxoObjectAttr_repr(cxoObjectAttr *attr)
149 {
150 PyObject *module, *name, *result;
151
152 if (cxoUtils_getModuleAndName(Py_TYPE(attr), &module, &name) < 0)
153 return NULL;
154 result = cxoUtils_formatString("<%s.%s %s>",
155 PyTuple_Pack(3, module, name, attr->name));
156 Py_DECREF(module);
157 Py_DECREF(name);
158 return result;
159 }
160
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoObjectType.c
11 // Defines the routines for handling Oracle type information.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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 //-----------------------------------------------------------------------------
98 // cxoObjectType_initialize()
99 // Initialize the object type with the information that is required.
100 //-----------------------------------------------------------------------------
101 static int cxoObjectType_initialize(cxoObjectType *objType,
102 cxoConnection *connection)
103 {
104 dpiObjectAttr **attributes;
105 dpiObjectTypeInfo info;
106 cxoObjectAttr *attr;
107 uint16_t i;
108
109 // get object type information
110 if (dpiObjectType_getInfo(objType->handle, &info) < 0)
111 return cxoError_raiseAndReturnInt();
112 Py_INCREF(connection);
113 objType->connection = connection;
114 objType->schema = cxoPyString_fromEncodedString(info.schema,
115 info.schemaLength, connection->encodingInfo.encoding, NULL);
116 if (!objType->schema)
117 return -1;
118 objType->name = cxoPyString_fromEncodedString(info.name, info.nameLength,
119 connection->encodingInfo.encoding, NULL);
120 if (!objType->name)
121 return -1;
122 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)
130 return -1;
131 }
132
133 // allocate the attribute list (temporary and permanent) and dictionary
134 objType->attributes = PyList_New(info.numAttributes);
135 if (!objType->attributes)
136 return -1;
137 objType->attributesByName = PyDict_New();
138 if (!objType->attributesByName)
139 return -1;
140
141 // get the list of attributes from DPI
142 attributes = PyMem_Malloc(sizeof(dpiObjectAttr*) * info.numAttributes);
143 if (!attributes) {
144 PyErr_NoMemory();
145 return -1;
146 }
147 if (dpiObjectType_getAttributes(objType->handle, info.numAttributes,
148 attributes) < 0) {
149 PyMem_Free(attributes);
150 return cxoError_raiseAndReturnInt();
151 }
152
153 // create attribute information for each attribute
154 for (i = 0; i < info.numAttributes; i++) {
155 attr = cxoObjectAttr_new(connection, attributes[i]);
156 if (!attr) {
157 PyMem_Free(attributes);
158 return -1;
159 }
160 PyList_SET_ITEM(objType->attributes, i, (PyObject*) attr);
161 if (PyDict_SetItem(objType->attributesByName, attr->name,
162 (PyObject*) attr) < 0)
163 return -1;
164 }
165 PyMem_Free(attributes);
166 return 0;
167 }
168
169
170 //-----------------------------------------------------------------------------
171 // cxoObjectType_new()
172 // Allocate a new object type.
173 //-----------------------------------------------------------------------------
174 cxoObjectType *cxoObjectType_new(cxoConnection *connection,
175 dpiObjectType *handle)
176 {
177 cxoObjectType *objType;
178
179 objType = (cxoObjectType*)
180 cxoPyTypeObjectType.tp_alloc(&cxoPyTypeObjectType, 0);
181 if (!objType)
182 return NULL;
183 if (dpiObjectType_addRef(handle) < 0) {
184 Py_DECREF(objType);
185 cxoError_raiseAndReturnNull();
186 return NULL;
187 }
188 objType->handle = handle;
189 if (cxoObjectType_initialize(objType, connection) < 0) {
190 Py_DECREF(objType);
191 return NULL;
192 }
193
194 return objType;
195 }
196
197
198 //-----------------------------------------------------------------------------
199 // cxoObjectType_newByName()
200 // Create a new object type given its name.
201 //-----------------------------------------------------------------------------
202 cxoObjectType *cxoObjectType_newByName(cxoConnection *connection,
203 PyObject *name)
204 {
205 cxoObjectType *objType;
206 dpiObjectType *handle;
207 cxoBuffer buffer;
208 int status;
209
210 if (cxoBuffer_fromObject(&buffer, name,
211 connection->encodingInfo.encoding) < 0)
212 return NULL;
213 status = dpiConn_getObjectType(connection->handle, buffer.ptr,
214 buffer.size, &handle);
215 cxoBuffer_clear(&buffer);
216 if (status < 0)
217 return (cxoObjectType*) cxoError_raiseAndReturnNull();
218 objType = cxoObjectType_new(connection, handle);
219 dpiObjectType_release(handle);
220 return objType;
221 }
222
223
224 //-----------------------------------------------------------------------------
225 // cxoObjectType_free()
226 // Free the memory associated with an object type.
227 //-----------------------------------------------------------------------------
228 static void cxoObjectType_free(cxoObjectType *objType)
229 {
230 if (objType->handle) {
231 dpiObjectType_release(objType->handle);
232 objType->handle = NULL;
233 }
234 Py_CLEAR(objType->connection);
235 Py_CLEAR(objType->schema);
236 Py_CLEAR(objType->name);
237 Py_CLEAR(objType->attributes);
238 Py_CLEAR(objType->attributesByName);
239 Py_CLEAR(objType->elementType);
240 Py_TYPE(objType)->tp_free((PyObject*) objType);
241 }
242
243
244 //-----------------------------------------------------------------------------
245 // cxoObjectType_repr()
246 // Return a string representation of the object type.
247 //-----------------------------------------------------------------------------
248 static PyObject *cxoObjectType_repr(cxoObjectType *objType)
249 {
250 PyObject *module, *name, *result;
251
252 if (cxoUtils_getModuleAndName(Py_TYPE(objType), &module, &name) < 0)
253 return NULL;
254 result = cxoUtils_formatString("<%s.%s %s.%s>",
255 PyTuple_Pack(4, module, name, objType->schema, objType->name));
256 Py_DECREF(module);
257 Py_DECREF(name);
258 return result;
259 }
260
261
262 //-----------------------------------------------------------------------------
263 // cxoObjectType_newObject()
264 // Factory function for creating objects of the type which can be bound.
265 //-----------------------------------------------------------------------------
266 static PyObject *cxoObjectType_newObject(cxoObjectType *objType,
267 PyObject *args, PyObject *keywordArgs)
268 {
269 static char *keywordList[] = { "value", NULL };
270 PyObject *initialValue;
271 dpiObject *handle;
272 cxoObject *obj;
273
274 // parse arguments
275 initialValue = NULL;
276 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|O", keywordList,
277 &initialValue))
278 return NULL;
279
280 // get handle to newly created object
281 if (dpiObjectType_createObject(objType->handle, &handle) < 0)
282 return cxoError_raiseAndReturnNull();
283
284 // create the object
285 obj = (cxoObject*) cxoObject_new(objType, handle);
286 if (!obj) {
287 dpiObject_release(handle);
288 return NULL;
289 }
290
291 // populate collection, if applicable
292 if (initialValue) {
293 if (cxoObject_internalExtend(obj, initialValue) < 0) {
294 Py_DECREF(obj);
295 return NULL;
296 }
297 }
298
299 return (PyObject*) obj;
300 }
301
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 // cxoSessionPool.c
11 // Handles session pooling.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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[] = {
45 { "acquire", (PyCFunction) cxoSessionPool_acquire,
46 METH_VARARGS | METH_KEYWORDS },
47 { "close", (PyCFunction) cxoSessionPool_close,
48 METH_VARARGS | METH_KEYWORDS },
49 { "drop", (PyCFunction) cxoSessionPool_drop, METH_VARARGS },
50 { "release", (PyCFunction) cxoSessionPool_release,
51 METH_VARARGS | METH_KEYWORDS },
52 { NULL }
53 };
54
55
56 //-----------------------------------------------------------------------------
57 // declaration of members for Python type "SessionPool"
58 //-----------------------------------------------------------------------------
59 static PyMemberDef cxoSessionPoolMembers[] = {
60 { "username", T_OBJECT, offsetof(cxoSessionPool, username), READONLY },
61 { "dsn", T_OBJECT, offsetof(cxoSessionPool, dsn), READONLY },
62 { "tnsentry", T_OBJECT, offsetof(cxoSessionPool, dsn), READONLY },
63 { "name", T_OBJECT, offsetof(cxoSessionPool, name), READONLY },
64 { "max", T_INT, offsetof(cxoSessionPool, maxSessions), READONLY },
65 { "min", T_INT, offsetof(cxoSessionPool, minSessions), READONLY },
66 { "increment", T_INT, offsetof(cxoSessionPool, sessionIncrement),
67 READONLY },
68 { "homogeneous", T_INT, offsetof(cxoSessionPool, homogeneous), READONLY },
69 { NULL }
70 };
71
72
73 //-----------------------------------------------------------------------------
74 // declaration of calculated members for Python type "SessionPool"
75 //-----------------------------------------------------------------------------
76 static PyGetSetDef cxoSessionPoolCalcMembers[] = {
77 { "opened", (getter) cxoSessionPool_getOpenCount, 0, 0, 0 },
78 { "busy", (getter) cxoSessionPool_getBusyCount, 0, 0, 0 },
79 { "timeout", (getter) cxoSessionPool_getTimeout,
80 (setter) cxoSessionPool_setTimeout, 0, 0 },
81 { "getmode", (getter) cxoSessionPool_getGetMode,
82 (setter) cxoSessionPool_setGetMode, 0, 0 },
83 { "max_lifetime_session", (getter) cxoSessionPool_getMaxLifetimeSession,
84 (setter) cxoSessionPool_setMaxLifetimeSession, 0, 0 },
85 { "stmtcachesize", (getter) cxoSessionPool_getStmtCacheSize,
86 (setter) cxoSessionPool_setStmtCacheSize, 0, 0 },
87 { "wait_timeout", (getter) cxoSessionPool_getWaitTimeout,
88 (setter) cxoSessionPool_setWaitTimeout, 0, 0 },
89 { NULL }
90 };
91
92
93 //-----------------------------------------------------------------------------
94 // declaration of Python type "SessionPool"
95 //-----------------------------------------------------------------------------
96 PyTypeObject cxoPyTypeSessionPool = {
97 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
139 };
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoSodaCollection.c
6 // Defines the routines for handling the SODA collection.
7 //-----------------------------------------------------------------------------
8
9 #include "cxoModule.h"
10
11 //-----------------------------------------------------------------------------
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*);
26
27
28 //-----------------------------------------------------------------------------
29 // declaration of methods
30 //-----------------------------------------------------------------------------
31 static PyMethodDef cxoMethods[] = {
32 { "createIndex", (PyCFunction) cxoSodaCollection_createIndex, METH_O },
33 { "drop", (PyCFunction) cxoSodaCollection_drop, METH_NOARGS },
34 { "dropIndex", (PyCFunction) cxoSodaCollection_dropIndex,
35 METH_VARARGS | METH_KEYWORDS },
36 { "find", (PyCFunction) cxoSodaCollection_find, METH_NOARGS },
37 { "getDataGuide", (PyCFunction) cxoSodaCollection_getDataGuide,
38 METH_NOARGS },
39 { "insertOne", (PyCFunction) cxoSodaCollection_insertOne, METH_O },
40 { "insertOneAndGet", (PyCFunction) cxoSodaCollection_insertOneAndGet,
41 METH_O },
42 { NULL }
43 };
44
45
46 //-----------------------------------------------------------------------------
47 // declaration of members
48 //-----------------------------------------------------------------------------
49 static PyMemberDef cxoMembers[] = {
50 { "name", T_OBJECT, offsetof(cxoSodaCollection, name), READONLY },
51 { NULL }
52 };
53
54
55 //-----------------------------------------------------------------------------
56 // declaration of calculated members
57 //-----------------------------------------------------------------------------
58 static PyGetSetDef cxoCalcMembers[] = {
59 { "metadata", (getter) cxoSodaCollection_getMetadata, 0, 0, 0 },
60 { NULL }
61 };
62
63
64 //-----------------------------------------------------------------------------
65 // Python type declarations
66 //-----------------------------------------------------------------------------
67 PyTypeObject cxoPyTypeSodaCollection = {
68 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
109 };
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoSodaDatabase.c
6 // Defines the routines for handling the SODA database.
7 //-----------------------------------------------------------------------------
8
9 #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
87
88 //-----------------------------------------------------------------------------
89 // cxoSodaDatabase_new()
90 // Create a new SODA database object.
91 //-----------------------------------------------------------------------------
92 cxoSodaDatabase *cxoSodaDatabase_new(cxoConnection *connection)
93 {
94 cxoSodaDatabase *db;
95 PyObject *module;
96
97 // load JSON dump/load functions, if needed
98 if (!cxoJsonDumpFunction || !cxoJsonLoadFunction) {
99 module = PyImport_ImportModule("json");
100 if (!module)
101 return NULL;
102 if (!cxoJsonDumpFunction) {
103 cxoJsonDumpFunction = PyObject_GetAttrString(module, "dumps");
104 if (!cxoJsonDumpFunction)
105 return NULL;
106 }
107 if (!cxoJsonLoadFunction) {
108 cxoJsonLoadFunction = PyObject_GetAttrString(module, "loads");
109 if (!cxoJsonLoadFunction)
110 return NULL;
111 }
112 }
113
114 // create SODA database object
115 db = (cxoSodaDatabase*)
116 cxoPyTypeSodaDatabase.tp_alloc(&cxoPyTypeSodaDatabase, 0);
117 if (!db)
118 return NULL;
119 if (dpiConn_getSodaDb(connection->handle, &db->handle) < 0) {
120 Py_DECREF(db);
121 cxoError_raiseAndReturnNull();
122 return NULL;
123 }
124 Py_INCREF(connection);
125 db->connection = connection;
126
127 return db;
128 }
129
130
131 //-----------------------------------------------------------------------------
132 // cxoSodaDatabase_free()
133 // Free the memory associated with a SODA database.
134 //-----------------------------------------------------------------------------
135 static void cxoSodaDatabase_free(cxoSodaDatabase *db)
136 {
137 if (db->handle) {
138 dpiSodaDb_release(db->handle);
139 db->handle = NULL;
140 }
141 Py_CLEAR(db->connection);
142 Py_TYPE(db)->tp_free((PyObject*) db);
143 }
144
145
146 //-----------------------------------------------------------------------------
147 // cxoSodaDatabase_repr()
148 // Return a string representation of a SODA database.
149 //-----------------------------------------------------------------------------
150 static PyObject *cxoSodaDatabase_repr(cxoSodaDatabase *db)
151 {
152 PyObject *connectionRepr, *module, *name, *result;
153
154 connectionRepr = PyObject_Repr((PyObject*) db->connection);
155 if (!connectionRepr)
156 return NULL;
157 if (cxoUtils_getModuleAndName(Py_TYPE(db), &module, &name) < 0) {
158 Py_DECREF(connectionRepr);
159 return NULL;
160 }
161 result = cxoUtils_formatString("<%s.%s on %s>",
162 PyTuple_Pack(3, module, name, connectionRepr));
163 Py_DECREF(module);
164 Py_DECREF(name);
165 Py_DECREF(connectionRepr);
166 return result;
167 }
168
169
170 //-----------------------------------------------------------------------------
171 // cxoSodaDatabase_createCollection()
172 // Create a SODA collection and return it.
173 //-----------------------------------------------------------------------------
174 static PyObject *cxoSodaDatabase_createCollection(cxoSodaDatabase *db,
175 PyObject *args, PyObject *keywordArgs)
176 {
177 static char *keywordList[] = { "name", "metadata", "mapMode", NULL };
178 PyObject *nameObj, *metadataObj, *mapModeObj;
179 cxoBuffer nameBuffer, metadataBuffer;
180 cxoSodaCollection *coll;
181 const char *encoding;
182 dpiSodaColl *handle;
183 int status, mapMode;
184 uint32_t flags;
185
186 // parse arguments
187 nameObj = metadataObj = mapModeObj = NULL;
188 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OO", keywordList,
189 &nameObj, &metadataObj, &mapModeObj))
190 return NULL;
191 encoding = db->connection->encodingInfo.encoding;
192 if (cxoBuffer_fromObject(&nameBuffer, nameObj, encoding) < 0)
193 return NULL;
194 if (cxoUtils_processJsonArg(metadataObj, &metadataBuffer) < 0) {
195 cxoBuffer_clear(&nameBuffer);
196 return NULL;
197 }
198 if (cxoUtils_getBooleanValue(mapModeObj, 0, &mapMode) < 0) {
199 cxoBuffer_clear(&nameBuffer);
200 cxoBuffer_clear(&metadataBuffer);
201 return NULL;
202 }
203
204 // create collection
205 if (cxoConnection_getSodaFlags(db->connection, &flags) < 0)
206 return NULL;
207 if (mapMode)
208 flags |= DPI_SODA_FLAGS_CREATE_COLL_MAP;
209 Py_BEGIN_ALLOW_THREADS
210 status = dpiSodaDb_createCollection(db->handle, nameBuffer.ptr,
211 nameBuffer.size, metadataBuffer.ptr, metadataBuffer.size, flags,
212 &handle);
213 Py_END_ALLOW_THREADS
214 cxoBuffer_clear(&nameBuffer);
215 cxoBuffer_clear(&metadataBuffer);
216 if (status < 0)
217 return cxoError_raiseAndReturnNull();
218 coll = cxoSodaCollection_new(db, handle);
219 if (!coll) {
220 dpiSodaColl_release(handle);
221 return NULL;
222 }
223
224 return (PyObject*) coll;
225 }
226
227
228 //-----------------------------------------------------------------------------
229 // cxoSodaDatabase_createDocument()
230 // Create a SODA document with the specified key, content and media type.
231 //-----------------------------------------------------------------------------
232 static PyObject *cxoSodaDatabase_createDocument(cxoSodaDatabase *db,
233 PyObject *args, PyObject *keywordArgs)
234 {
235 static char *keywordList[] = { "content", "key", "mediaType", NULL };
236 cxoBuffer contentBuffer, keyBuffer, mediaTypeBuffer;
237 PyObject *contentObj, *keyObj, *mediaTypeObj;
238 const char *encoding;
239 dpiSodaDoc *doc;
240 int status;
241
242 // parse arguments
243 keyObj = mediaTypeObj = NULL;
244 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OO", keywordList,
245 &contentObj, &keyObj, &mediaTypeObj))
246 return NULL;
247
248 // content must be converted to string if it is a dictionary
249 if (PyDict_Check(contentObj)) {
250 contentObj = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction,
251 contentObj, NULL);
252 if (!contentObj)
253 return NULL;
254 }
255
256 // get buffers for each of the content, key and media type parameters
257 if (cxoUtils_processJsonArg(contentObj, &contentBuffer) < 0)
258 return NULL;
259 encoding = db->connection->encodingInfo.encoding;
260 if (cxoBuffer_fromObject(&keyBuffer, keyObj, encoding) < 0) {
261 cxoBuffer_clear(&contentBuffer);
262 return NULL;
263 }
264 if (cxoBuffer_fromObject(&mediaTypeBuffer, mediaTypeObj, encoding) < 0) {
265 cxoBuffer_clear(&contentBuffer);
266 cxoBuffer_clear(&keyBuffer);
267 return NULL;
268 }
269
270 // create SODA document
271 status = dpiSodaDb_createDocument(db->handle, keyBuffer.ptr,
272 keyBuffer.size, contentBuffer.ptr, contentBuffer.size,
273 mediaTypeBuffer.ptr, mediaTypeBuffer.size, DPI_SODA_FLAGS_DEFAULT,
274 &doc);
275 cxoBuffer_clear(&contentBuffer);
276 cxoBuffer_clear(&keyBuffer);
277 cxoBuffer_clear(&mediaTypeBuffer);
278 if (status < 0)
279 return cxoError_raiseAndReturnNull();
280
281 return (PyObject*) cxoSodaDoc_new(db, doc);
282 }
283
284
285 //-----------------------------------------------------------------------------
286 // cxoSodaDatabase_getCollectionNames()
287 // Return a list of the names of the collections found in the database.
288 //-----------------------------------------------------------------------------
289 static PyObject *cxoSodaDatabase_getCollectionNames(cxoSodaDatabase *db,
290 PyObject *args, PyObject *keywordArgs)
291 {
292 static char *keywordList[] = { "startName", "limit", NULL };
293 PyObject *startName, *result, *temp;
294 dpiSodaCollNames collNames;
295 cxoBuffer startNameBuffer;
296 uint32_t limit, i, flags;
297 const char *encoding;
298 int status;
299
300 // parse arguments
301 limit = 0;
302 startName = NULL;
303 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|Oi", keywordList,
304 &startName, &limit))
305 return NULL;
306
307 // get collection names from the database
308 encoding = db->connection->encodingInfo.encoding;
309 if (cxoBuffer_fromObject(&startNameBuffer, startName, encoding) < 0)
310 return NULL;
311 if (cxoConnection_getSodaFlags(db->connection, &flags) < 0)
312 return NULL;
313 Py_BEGIN_ALLOW_THREADS
314 status = dpiSodaDb_getCollectionNames(db->handle,
315 (const char*) startNameBuffer.ptr, startNameBuffer.size, limit,
316 flags, &collNames);
317 Py_END_ALLOW_THREADS
318 cxoBuffer_clear(&startNameBuffer);
319 if (status < 0)
320 return cxoError_raiseAndReturnNull();
321
322 // transform results into a Python list
323 result = PyList_New(collNames.numNames);
324 if (!result)
325 return NULL;
326 for (i = 0; i < collNames.numNames; i++) {
327 temp = cxoPyString_fromEncodedString(collNames.names[i],
328 collNames.nameLengths[i], encoding, NULL);
329 if (!temp) {
330 Py_DECREF(result);
331 return NULL;
332 }
333 PyList_SET_ITEM(result, i, temp);
334 }
335 if (dpiSodaDb_freeCollectionNames(db->handle, &collNames) < 0) {
336 Py_DECREF(result);
337 return cxoError_raiseAndReturnNull();
338 }
339
340 return result;
341 }
342
343
344 //-----------------------------------------------------------------------------
345 // cxoSodaDatabase_openCollection()
346 // Open a SODA collection and return it.
347 //-----------------------------------------------------------------------------
348 static PyObject *cxoSodaDatabase_openCollection(cxoSodaDatabase *db,
349 PyObject *nameObj)
350 {
351 cxoSodaCollection *coll;
352 cxoBuffer nameBuffer;
353 dpiSodaColl *handle;
354 uint32_t flags;
355 int status;
356
357 // open collection
358 if (cxoBuffer_fromObject(&nameBuffer, nameObj,
359 db->connection->encodingInfo.encoding) < 0)
360 return NULL;
361 if (cxoConnection_getSodaFlags(db->connection, &flags) < 0)
362 return NULL;
363 Py_BEGIN_ALLOW_THREADS
364 status = dpiSodaDb_openCollection(db->handle, nameBuffer.ptr,
365 nameBuffer.size, flags, &handle);
366 Py_END_ALLOW_THREADS
367 cxoBuffer_clear(&nameBuffer);
368 if (status < 0)
369 return cxoError_raiseAndReturnNull();
370 if (!handle)
371 Py_RETURN_NONE;
372 coll = cxoSodaCollection_new(db, handle);
373 if (!coll) {
374 dpiSodaColl_release(handle);
375 return NULL;
376 }
377
378 return (PyObject*) coll;
379 }
380
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoSodaDoc.c
6 // Defines the routines for handling SODA documents.
7 //-----------------------------------------------------------------------------
8
9 #include "cxoModule.h"
10
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*);
24
25
26 //-----------------------------------------------------------------------------
27 // declaration of methods
28 //-----------------------------------------------------------------------------
29 static PyMethodDef cxoMethods[] = {
30 { "getContent", (PyCFunction) cxoSodaDoc_getContent, METH_NOARGS },
31 { "getContentAsBytes", (PyCFunction) cxoSodaDoc_getContentAsBytes,
32 METH_NOARGS },
33 { "getContentAsString", (PyCFunction) cxoSodaDoc_getContentAsString,
34 METH_NOARGS },
35 { NULL }
36 };
37
38
39 //-----------------------------------------------------------------------------
40 // declaration of calculated members
41 //-----------------------------------------------------------------------------
42 static PyGetSetDef cxoCalcMembers[] = {
43 { "createdOn", (getter) cxoSodaDoc_getCreatedOn, 0, 0, 0 },
44 { "key", (getter) cxoSodaDoc_getKey, 0, 0, 0 },
45 { "lastModified", (getter) cxoSodaDoc_getLastModified, 0, 0, 0 },
46 { "mediaType", (getter) cxoSodaDoc_getMediaType, 0, 0, 0 },
47 { "version", (getter) cxoSodaDoc_getVersion, 0, 0, 0 },
48 { NULL }
49 };
50
51
52 //-----------------------------------------------------------------------------
53 // Python type declarations
54 //-----------------------------------------------------------------------------
55 PyTypeObject cxoPyTypeSodaDoc = {
56 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
97 };
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoSodaDocCursor.c
6 // Defines the routines for handling SODA document cursors. These cursors
7 // permit iterating over the documents that match the criteria that was
8 // specified by the user.
9 //-----------------------------------------------------------------------------
10
11 #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
79
80 //-----------------------------------------------------------------------------
81 // cxoSodaDocCursor_new()
82 // Create a new SODA document cursor.
83 //-----------------------------------------------------------------------------
84 cxoSodaDocCursor *cxoSodaDocCursor_new(cxoSodaDatabase *db,
85 dpiSodaDocCursor *handle)
86 {
87 cxoSodaDocCursor *cursor;
88
89 cursor = (cxoSodaDocCursor*)
90 cxoPyTypeSodaDocCursor.tp_alloc(&cxoPyTypeSodaDocCursor, 0);
91 if (!cursor) {
92 dpiSodaDocCursor_release(handle);
93 return NULL;
94 }
95 Py_INCREF(db);
96 cursor->db = db;
97 cursor->handle = handle;
98 return cursor;
99 }
100
101
102 //-----------------------------------------------------------------------------
103 // cxoSodaDocCursor_free()
104 // Free the memory associated with a SODA document cursor.
105 //-----------------------------------------------------------------------------
106 static void cxoSodaDocCursor_free(cxoSodaDocCursor *cursor)
107 {
108 if (cursor->handle) {
109 dpiSodaDocCursor_release(cursor->handle);
110 cursor->handle = NULL;
111 }
112 Py_CLEAR(cursor->db);
113 Py_TYPE(cursor)->tp_free((PyObject*) cursor);
114 }
115
116
117 //-----------------------------------------------------------------------------
118 // cxoSodaDocCursor_repr()
119 // Return a string representation of a SODA document cursor.
120 //-----------------------------------------------------------------------------
121 static PyObject *cxoSodaDocCursor_repr(cxoSodaDocCursor *cursor)
122 {
123 PyObject *module, *name, *result;
124
125 if (cxoUtils_getModuleAndName(Py_TYPE(cursor), &module, &name) < 0)
126 return NULL;
127 result = cxoUtils_formatString("<%s.%s>", PyTuple_Pack(2, module, name));
128 Py_DECREF(module);
129 Py_DECREF(name);
130 return result;
131 }
132
133
134 //-----------------------------------------------------------------------------
135 // cxoSodaDocCursor_close()
136 // Create a SODA collection and return it.
137 //-----------------------------------------------------------------------------
138 static PyObject *cxoSodaDocCursor_close(cxoSodaDocCursor *cursor,
139 PyObject *args)
140 {
141 if (dpiSodaDocCursor_close(cursor->handle) < 0)
142 return cxoError_raiseAndReturnNull();
143 Py_RETURN_NONE;
144 }
145
146
147 //-----------------------------------------------------------------------------
148 // cxoSodaDocCursor_getIter()
149 // Return a reference to the cursor which supports the iterator protocol.
150 //-----------------------------------------------------------------------------
151 static PyObject *cxoSodaDocCursor_getIter(cxoSodaDocCursor *cursor)
152 {
153 Py_INCREF(cursor);
154 return (PyObject*) cursor;
155 }
156
157
158 //-----------------------------------------------------------------------------
159 // cxoSodaDocCursor_getNext()
160 // Return the next document from the cursor.
161 //-----------------------------------------------------------------------------
162 static PyObject *cxoSodaDocCursor_getNext(cxoSodaDocCursor *cursor)
163 {
164 dpiSodaDoc *handle;
165 cxoSodaDoc *doc;
166 uint32_t flags;
167 int status;
168
169 if (cxoConnection_getSodaFlags(cursor->db->connection, &flags) < 0)
170 return NULL;
171 Py_BEGIN_ALLOW_THREADS
172 status = dpiSodaDocCursor_getNext(cursor->handle, flags, &handle);
173 Py_END_ALLOW_THREADS
174 if (status < 0)
175 return cxoError_raiseAndReturnNull();
176 if (!handle)
177 return NULL;
178 doc = cxoSodaDoc_new(cursor->db, handle);
179 if (!doc)
180 return NULL;
181 return (PyObject*) doc;
182 }
183
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoSodaOperation.c
6 // Defines the routines for the various operations performed on SODA
7 // collections.
8 //-----------------------------------------------------------------------------
9
10 #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
103
104 //-----------------------------------------------------------------------------
105 // cxoSodaOperation_clearKeys()
106 // Clear the keys set on the operation object, if applicable.
107 //-----------------------------------------------------------------------------
108 void cxoSodaOperation_clearKeys(cxoSodaOperation *op)
109 {
110 uint32_t i;
111
112 if (op->keyBuffers) {
113 for (i = 0; i < op->numKeyBuffers; i++)
114 cxoBuffer_clear(&op->keyBuffers[i]);
115 PyMem_Free(op->keyBuffers);
116 op->keyBuffers = NULL;
117 }
118 op->numKeyBuffers = 0;
119 op->options.numKeys = 0;
120 if (op->options.keys) {
121 PyMem_Free(op->options.keys);
122 op->options.keys = NULL;
123 }
124 if (op->options.keyLengths) {
125 PyMem_Free(op->options.keyLengths);
126 op->options.keyLengths = NULL;
127 }
128 }
129
130
131 //-----------------------------------------------------------------------------
132 // cxoSodaOperation_new()
133 // Create a new SODA operation object.
134 //-----------------------------------------------------------------------------
135 cxoSodaOperation *cxoSodaOperation_new(cxoSodaCollection *coll)
136 {
137 cxoSodaOperation *op;
138
139 op = (cxoSodaOperation*)
140 cxoPyTypeSodaOperation.tp_alloc(&cxoPyTypeSodaOperation, 0);
141 if (!op)
142 return NULL;
143 if (dpiContext_initSodaOperOptions(cxoDpiContext, &op->options) < 0) {
144 Py_DECREF(op);
145 return NULL;
146 }
147 cxoBuffer_init(&op->keyBuffer);
148 cxoBuffer_init(&op->versionBuffer);
149 cxoBuffer_init(&op->filterBuffer);
150 Py_INCREF(coll);
151 op->coll = coll;
152
153 return op;
154 }
155
156
157 //-----------------------------------------------------------------------------
158 // cxoSodaOperation_free()
159 // Free the memory associated with a SODA operation object.
160 //-----------------------------------------------------------------------------
161 static void cxoSodaOperation_free(cxoSodaOperation *op)
162 {
163 cxoSodaOperation_clearKeys(op);
164 cxoBuffer_clear(&op->keyBuffer);
165 cxoBuffer_clear(&op->versionBuffer);
166 cxoBuffer_clear(&op->filterBuffer);
167 Py_CLEAR(op->coll);
168 Py_TYPE(op)->tp_free((PyObject*) op);
169 }
170
171
172 //-----------------------------------------------------------------------------
173 // cxoSodaOperation_repr()
174 // Return a string representation of a SODA operation object.
175 //-----------------------------------------------------------------------------
176 static PyObject *cxoSodaOperation_repr(cxoSodaOperation *op)
177 {
178 PyObject *collRepr, *module, *name, *result;
179
180 collRepr = PyObject_Repr((PyObject*) op->coll);
181 if (!collRepr)
182 return NULL;
183 if (cxoUtils_getModuleAndName(Py_TYPE(op), &module, &name) < 0) {
184 Py_DECREF(collRepr);
185 return NULL;
186 }
187 result = cxoUtils_formatString("<%s.%s on %s>",
188 PyTuple_Pack(3, module, name, collRepr));
189 Py_DECREF(module);
190 Py_DECREF(name);
191 Py_DECREF(collRepr);
192 return result;
193 }
194
195
196 //-----------------------------------------------------------------------------
197 // cxoSodaOperation_filter()
198 // Set the filter to be used for the operation.
199 //-----------------------------------------------------------------------------
200 static PyObject *cxoSodaOperation_filter(cxoSodaOperation *op,
201 PyObject *filterObj)
202 {
203 cxoBuffer_clear(&op->filterBuffer);
204 if (PyDict_Check(filterObj)) {
205 filterObj = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction,
206 filterObj, NULL);
207 if (!filterObj)
208 return NULL;
209 }
210 if (cxoBuffer_fromObject(&op->filterBuffer, filterObj,
211 op->coll->db->connection->encodingInfo.encoding) < 0)
212 return NULL;
213 op->options.filter = op->filterBuffer.ptr;
214 op->options.filterLength = op->filterBuffer.size;
215 Py_INCREF(op);
216 return (PyObject*) op;
217 }
218
219
220 //-----------------------------------------------------------------------------
221 // cxoSodaOperation_key()
222 // Set the key to be used for the operation.
223 //-----------------------------------------------------------------------------
224 static PyObject *cxoSodaOperation_key(cxoSodaOperation *op,
225 PyObject *keyObj)
226 {
227 cxoBuffer_clear(&op->keyBuffer);
228 if (cxoBuffer_fromObject(&op->keyBuffer, keyObj,
229 op->coll->db->connection->encodingInfo.encoding) < 0)
230 return NULL;
231 op->options.key = op->keyBuffer.ptr;
232 op->options.keyLength = op->keyBuffer.size;
233 Py_INCREF(op);
234 return (PyObject*) op;
235 }
236
237
238 //-----------------------------------------------------------------------------
239 // cxoSodaOperation_keys()
240 // Set the keys to be used for the operation.
241 //-----------------------------------------------------------------------------
242 static PyObject *cxoSodaOperation_keys(cxoSodaOperation *op,
243 PyObject *keysObj)
244 {
245 Py_ssize_t size, i;
246 PyObject *element;
247
248 // determine size of sequence passed to method
249 size = PySequence_Size(keysObj);
250 if (PyErr_Occurred())
251 return NULL;
252
253 // clear original keys, if applicable
254 cxoSodaOperation_clearKeys(op);
255
256 // zero-length arrays don't need any further processing
257 if (size == 0) {
258 Py_INCREF(op);
259 return (PyObject*) op;
260 }
261
262 // initialize memory
263 op->keyBuffers = PyMem_Malloc(size * sizeof(cxoBuffer));
264 if (!op->keyBuffers)
265 return NULL;
266 op->numKeyBuffers = (uint32_t) size;
267 for (i = 0; i < size; i++)
268 cxoBuffer_init(&op->keyBuffers[i]);
269 op->options.keys = PyMem_Malloc(size * sizeof(const char *));
270 op->options.keyLengths = PyMem_Malloc(size * sizeof(uint32_t));
271 if (!op->options.keys || !op->options.keyLengths) {
272 cxoSodaOperation_clearKeys(op);
273 return NULL;
274 }
275 op->options.numKeys = op->numKeyBuffers;
276
277 // process each of the elements of the sequence
278 for (i = 0; i < size; i++) {
279 element = PySequence_GetItem(keysObj, i);
280 if (!element) {
281 cxoSodaOperation_clearKeys(op);
282 return NULL;
283 }
284 if (cxoBuffer_fromObject(&op->keyBuffers[i], element,
285 op->coll->db->connection->encodingInfo.encoding) < 0) {
286 Py_DECREF(element);
287 cxoSodaOperation_clearKeys(op);
288 return NULL;
289 }
290 Py_DECREF(element);
291 op->options.keys[i] = op->keyBuffers[i].ptr;
292 op->options.keyLengths[i] = op->keyBuffers[i].size;
293 }
294
295 Py_INCREF(op);
296 return (PyObject*) op;
297 }
298
299
300 //-----------------------------------------------------------------------------
301 // cxoSodaOperation_limit()
302 // Set the limit value to be used for the operation.
303 //-----------------------------------------------------------------------------
304 static PyObject *cxoSodaOperation_limit(cxoSodaOperation *op,
305 PyObject *limitObj)
306 {
307 op->options.limit = PyLong_AsUnsignedLong(limitObj);
308 if (PyErr_Occurred())
309 return NULL;
310 Py_INCREF(op);
311 return (PyObject*) op;
312 }
313
314
315 //-----------------------------------------------------------------------------
316 // cxoSodaOperation_skip()
317 // Set the skip value to be used for the operation.
318 //-----------------------------------------------------------------------------
319 static PyObject *cxoSodaOperation_skip(cxoSodaOperation *op,
320 PyObject *skipObj)
321 {
322 op->options.skip = PyLong_AsUnsignedLong(skipObj);
323 if (PyErr_Occurred())
324 return NULL;
325 Py_INCREF(op);
326 return (PyObject*) op;
327 }
328
329
330 //-----------------------------------------------------------------------------
331 // cxoSodaOperation_version()
332 // Set the version to be used for the operation.
333 //-----------------------------------------------------------------------------
334 static PyObject *cxoSodaOperation_version(cxoSodaOperation *op,
335 PyObject *versionObj)
336 {
337 cxoBuffer_clear(&op->versionBuffer);
338 if (cxoBuffer_fromObject(&op->versionBuffer, versionObj,
339 op->coll->db->connection->encodingInfo.encoding) < 0)
340 return NULL;
341 op->options.version = op->versionBuffer.ptr;
342 op->options.versionLength = op->versionBuffer.size;
343 Py_INCREF(op);
344 return (PyObject*) op;
345 }
346
347
348 //-----------------------------------------------------------------------------
349 // cxoSodaOperation_count()
350 // Returns the number of documents that match the criteria.
351 //-----------------------------------------------------------------------------
352 static PyObject *cxoSodaOperation_count(cxoSodaOperation *op, PyObject *args)
353 {
354 uint64_t count;
355 uint32_t flags;
356 int status;
357
358 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
359 return NULL;
360 Py_BEGIN_ALLOW_THREADS
361 status = dpiSodaColl_getDocCount(op->coll->handle, &op->options, flags,
362 &count);
363 Py_END_ALLOW_THREADS
364 if (status < 0)
365 return cxoError_raiseAndReturnNull();
366 return PyLong_FromUnsignedLongLong(count);
367 }
368
369
370 //-----------------------------------------------------------------------------
371 // cxoSodaOperation_getCursor()
372 // Returns a document cursor which can be used to iterate over the documents
373 // that match the criteria.
374 //-----------------------------------------------------------------------------
375 static PyObject *cxoSodaOperation_getCursor(cxoSodaOperation *op,
376 PyObject *args)
377 {
378 dpiSodaDocCursor *handle;
379 cxoSodaDocCursor *cursor;
380 uint32_t flags;
381 int status;
382
383 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
384 return NULL;
385 Py_BEGIN_ALLOW_THREADS
386 status = dpiSodaColl_find(op->coll->handle, &op->options, flags, &handle);
387 Py_END_ALLOW_THREADS
388 if (status < 0)
389 return cxoError_raiseAndReturnNull();
390 cursor = cxoSodaDocCursor_new(op->coll->db, handle);
391 if (!cursor)
392 return NULL;
393 return (PyObject*) cursor;
394 }
395
396
397 //-----------------------------------------------------------------------------
398 // cxoSodaOperation_getDocuments()
399 // Returns a list of documents that match the criteria.
400 //-----------------------------------------------------------------------------
401 static PyObject *cxoSodaOperation_getDocuments(cxoSodaOperation *op,
402 PyObject *args)
403 {
404 dpiSodaDocCursor *cursor;
405 PyObject *docObj;
406 dpiSodaDoc *doc;
407 PyObject *list;
408 uint32_t flags;
409 int status;
410
411 // acquire cursor
412 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
413 return NULL;
414 Py_BEGIN_ALLOW_THREADS
415 status = dpiSodaColl_find(op->coll->handle, &op->options, flags, &cursor);
416 Py_END_ALLOW_THREADS
417 if (status < 0)
418 return cxoError_raiseAndReturnNull();
419
420 // iterate cursor and create array of documents
421 list = PyList_New(0);
422 if (!list) {
423 dpiSodaDocCursor_release(cursor);
424 return NULL;
425 }
426 while (1) {
427 Py_BEGIN_ALLOW_THREADS
428 status = dpiSodaDocCursor_getNext(cursor, flags, &doc);
429 Py_END_ALLOW_THREADS
430 if (status < 0) {
431 cxoError_raiseAndReturnNull();
432 dpiSodaDocCursor_release(cursor);
433 return NULL;
434 }
435 if (!doc)
436 break;
437 docObj = (PyObject*) cxoSodaDoc_new(op->coll->db, doc);
438 if (!docObj) {
439 dpiSodaDocCursor_release(cursor);
440 return NULL;
441 }
442 if (PyList_Append(list, docObj) < 0) {
443 Py_DECREF(docObj);
444 dpiSodaDocCursor_release(cursor);
445 return NULL;
446 }
447 Py_DECREF(docObj);
448 }
449 dpiSodaDocCursor_release(cursor);
450
451 return list;
452 }
453
454
455 //-----------------------------------------------------------------------------
456 // cxoSodaOperation_getOne()
457 // Returns a single document that matches the criteria or None if no
458 // documents match the criteria.
459 //-----------------------------------------------------------------------------
460 static PyObject *cxoSodaOperation_getOne(cxoSodaOperation *op, PyObject *args)
461 {
462 dpiSodaDoc *handle;
463 uint32_t flags;
464 int status;
465
466 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
467 return NULL;
468 Py_BEGIN_ALLOW_THREADS
469 status = dpiSodaColl_findOne(op->coll->handle, &op->options, flags,
470 &handle);
471 Py_END_ALLOW_THREADS
472 if (status < 0)
473 return cxoError_raiseAndReturnNull();
474 if (handle)
475 return (PyObject*) cxoSodaDoc_new(op->coll->db, handle);
476 Py_RETURN_NONE;
477 }
478
479
480 //-----------------------------------------------------------------------------
481 // cxoSodaOperation_remove()
482 // Remove all of the documents that match the criteria.
483 //-----------------------------------------------------------------------------
484 static PyObject *cxoSodaOperation_remove(cxoSodaOperation *op, PyObject *args)
485 {
486 uint64_t count;
487 uint32_t flags;
488 int status;
489
490 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
491 return NULL;
492 Py_BEGIN_ALLOW_THREADS
493 status = dpiSodaColl_remove(op->coll->handle, &op->options, flags, &count);
494 Py_END_ALLOW_THREADS
495 if (status < 0)
496 return cxoError_raiseAndReturnNull();
497 return PyLong_FromUnsignedLongLong(count);
498 }
499
500
501 //-----------------------------------------------------------------------------
502 // cxoSodaOperation_replaceOne()
503 // Replace a single document in the collection with the provided replacement.
504 //-----------------------------------------------------------------------------
505 static PyObject *cxoSodaOperation_replaceOne(cxoSodaOperation *op,
506 PyObject *arg)
507 {
508 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)
515 return NULL;
516 Py_BEGIN_ALLOW_THREADS
517 status = dpiSodaColl_replaceOne(op->coll->handle, &op->options,
518 doc->handle, flags, &replaced, NULL);
519 Py_END_ALLOW_THREADS
520 if (status < 0) {
521 cxoError_raiseAndReturnNull();
522 Py_DECREF(doc);
523 return NULL;
524 }
525 Py_DECREF(doc);
526 if (replaced)
527 Py_RETURN_TRUE;
528 Py_RETURN_FALSE;
529 }
530
531
532 //-----------------------------------------------------------------------------
533 // cxoSodaOperation_replaceOneAndGet()
534 // Replace a single document in the collection with the provided replacement
535 // and return a document (without the content) to the caller.
536 //-----------------------------------------------------------------------------
537 static PyObject *cxoSodaOperation_replaceOneAndGet(cxoSodaOperation *op,
538 PyObject *arg)
539 {
540 dpiSodaDoc *replacedDoc;
541 cxoSodaDoc *doc;
542 uint32_t flags;
543 int status;
544
545 if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
546 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) {
554 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);
561 Py_RETURN_NONE;
562 }
563
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoSubscr.c
11 // Defines the routines for handling Oracle subscription information.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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 //-----------------------------------------------------------------------------
318 // cxoMessageRow_initialize()
319 // Initialize a new message row with the information from the descriptor.
320 //-----------------------------------------------------------------------------
321 static int cxoMessageRow_initialize(cxoMessageRow *rowObj,
322 const char *encoding, dpiSubscrMessageRow *row)
323 {
324 rowObj->operation = row->operation;
325 rowObj->rowid = cxoPyString_fromEncodedString(row->rowid, row->rowidLength,
326 encoding, NULL);
327 if (!rowObj->rowid)
328 return -1;
329
330 return 0;
331 }
332
333
334 //-----------------------------------------------------------------------------
335 // cxoMessageTable_initialize()
336 // Initialize a new message table with the information from the descriptor.
337 //-----------------------------------------------------------------------------
338 static int cxoMessageTable_initialize(cxoMessageTable *tableObj,
339 const char *encoding, dpiSubscrMessageTable *table)
340 {
341 cxoMessageRow *row;
342 uint32_t i;
343
344 tableObj->operation = table->operation;
345 tableObj->name = cxoPyString_fromEncodedString(table->name,
346 table->nameLength, encoding, NULL);
347 tableObj->rows = PyList_New(table->numRows);
348 if (!tableObj->rows)
349 return -1;
350 for (i = 0; i < table->numRows; i++) {
351 row = (cxoMessageRow*)
352 cxoPyTypeMessageRow.tp_alloc(&cxoPyTypeMessageRow, 0);
353 if (!row)
354 return -1;
355 PyList_SET_ITEM(tableObj->rows, i, (PyObject*) row);
356 if (cxoMessageRow_initialize(row, encoding, &table->rows[i]) < 0)
357 return -1;
358 }
359
360 return 0;
361 }
362
363
364 //-----------------------------------------------------------------------------
365 // cxoMessageQuery_initialize()
366 // Initialize a new message query with the information from the descriptor.
367 //-----------------------------------------------------------------------------
368 static int cxoMessageQuery_initialize(cxoMessageQuery *queryObj,
369 const char *encoding, dpiSubscrMessageQuery *query)
370 {
371 cxoMessageTable *table;
372 uint32_t i;
373
374 queryObj->id = query->id;
375 queryObj->operation = query->operation;
376 queryObj->tables = PyList_New(query->numTables);
377 if (!queryObj->tables)
378 return -1;
379 for (i = 0; i < query->numTables; i++) {
380 table = (cxoMessageTable*)
381 cxoPyTypeMessageTable.tp_alloc(&cxoPyTypeMessageTable, 0);
382 if (!table)
383 return -1;
384 PyList_SET_ITEM(queryObj->tables, i, (PyObject*) table);
385 if (cxoMessageTable_initialize(table, encoding, &query->tables[i]) < 0)
386 return -1;
387 }
388
389 return 0;
390 }
391
392
393 //-----------------------------------------------------------------------------
394 // cxoMessage_initialize()
395 // Initialize a new message with the information from the descriptor.
396 //-----------------------------------------------------------------------------
397 static int cxoMessage_initialize(cxoMessage *messageObj,
398 cxoSubscr *subscription, dpiSubscrMessage *message)
399 {
400 cxoMessageTable *table;
401 cxoMessageQuery *query;
402 const char *encoding;
403 uint32_t i;
404
405 Py_INCREF(subscription);
406 messageObj->subscription = subscription;
407 encoding = subscription->connection->encodingInfo.encoding;
408 messageObj->type = message->eventType;
409 messageObj->registered = message->registered;
410 messageObj->dbname = cxoPyString_fromEncodedString(message->dbName,
411 message->dbNameLength, encoding, NULL);
412 if (!messageObj->dbname)
413 return -1;
414 if (message->txId) {
415 messageObj->txId = PyBytes_FromStringAndSize(message->txId,
416 message->txIdLength);
417 if (!messageObj->txId)
418 return -1;
419 }
420 if (message->queueName) {
421 messageObj->queueName = cxoPyString_fromEncodedString(
422 message->queueName, message->queueNameLength, encoding, NULL);
423 if (!messageObj->queueName)
424 return -1;
425 }
426 if (message->consumerName) {
427 messageObj->consumerName = cxoPyString_fromEncodedString(
428 message->consumerName, message->consumerNameLength, encoding,
429 NULL);
430 if (!messageObj->consumerName)
431 return -1;
432 }
433 switch (message->eventType) {
434 case DPI_EVENT_OBJCHANGE:
435 messageObj->tables = PyList_New(message->numTables);
436 if (!messageObj->tables)
437 return -1;
438 for (i = 0; i < message->numTables; i++) {
439 table = (cxoMessageTable*)
440 cxoPyTypeMessageTable.tp_alloc(&cxoPyTypeMessageTable,
441 0);
442 if (!table)
443 return -1;
444 PyList_SET_ITEM(messageObj->tables, i, (PyObject*) table);
445 if (cxoMessageTable_initialize(table, encoding,
446 &message->tables[i]) < 0)
447 return -1;
448 }
449 break;
450 case DPI_EVENT_QUERYCHANGE:
451 messageObj->queries = PyList_New(message->numQueries);
452 if (!messageObj->queries)
453 return -1;
454 for (i = 0; i < message->numQueries; i++) {
455 query = (cxoMessageQuery*)
456 cxoPyTypeMessageQuery.tp_alloc(&cxoPyTypeMessageQuery,
457 0);
458 if (!query)
459 return -1;
460 PyList_SET_ITEM(messageObj->queries, i, (PyObject*) query);
461 if (cxoMessageQuery_initialize(query, encoding,
462 &message->queries[i]) < 0)
463 return -1;
464 }
465 break;
466 default:
467 break;
468 }
469
470 return 0;
471 }
472
473
474 //-----------------------------------------------------------------------------
475 // cxoSubscr_callbackHandler()
476 // Routine that performs the actual call.
477 //-----------------------------------------------------------------------------
478 static int cxoSubscr_callbackHandler(cxoSubscr *subscr,
479 dpiSubscrMessage *message)
480 {
481 PyObject *result, *args;
482 cxoMessage *messageObj;
483
484 // create the message
485 messageObj = (cxoMessage*) cxoPyTypeMessage.tp_alloc(&cxoPyTypeMessage, 0);
486 if (!messageObj)
487 return -1;
488 if (cxoMessage_initialize(messageObj, subscr, message) < 0) {
489 Py_DECREF(messageObj);
490 return -1;
491 }
492
493 // create the arguments for the call
494 args = PyTuple_Pack(1, messageObj);
495 Py_DECREF(messageObj);
496 if (!args)
497 return -1;
498
499 // make the actual call
500 result = PyObject_Call(subscr->callback, args, NULL);
501 Py_DECREF(args);
502 if (!result)
503 return -1;
504 Py_DECREF(result);
505
506 return 0;
507 }
508
509
510 //-----------------------------------------------------------------------------
511 // cxoSubscr_callback()
512 // Routine that is called when a callback needs to be invoked.
513 //-----------------------------------------------------------------------------
514 void cxoSubscr_callback(cxoSubscr *subscr, dpiSubscrMessage *message)
515 {
516 #ifdef WITH_THREAD
517 PyGILState_STATE gstate = PyGILState_Ensure();
518 #endif
519
520 if (message->errorInfo) {
521 cxoError_raiseFromInfo(message->errorInfo);
522 PyErr_Print();
523 } else if (cxoSubscr_callbackHandler(subscr, message) < 0)
524 PyErr_Print();
525
526 #ifdef WITH_THREAD
527 PyGILState_Release(gstate);
528 #endif
529 }
530
531
532 //-----------------------------------------------------------------------------
533 // cxoSubscr_free()
534 // Free the memory associated with a subscription.
535 //-----------------------------------------------------------------------------
536 static void cxoSubscr_free(cxoSubscr *subscr)
537 {
538 if (subscr->handle) {
539 dpiSubscr_release(subscr->handle);
540 subscr->handle = NULL;
541 }
542 Py_CLEAR(subscr->connection);
543 Py_CLEAR(subscr->callback);
544 Py_TYPE(subscr)->tp_free((PyObject*) subscr);
545 }
546
547
548 //-----------------------------------------------------------------------------
549 // cxoSubscr_repr()
550 // Return a string representation of the subscription.
551 //-----------------------------------------------------------------------------
552 static PyObject *cxoSubscr_repr(cxoSubscr *subscription)
553 {
554 PyObject *connectionRepr, *module, *name, *result;
555
556 connectionRepr = PyObject_Repr((PyObject*) subscription->connection);
557 if (!connectionRepr)
558 return NULL;
559 if (cxoUtils_getModuleAndName(Py_TYPE(subscription), &module, &name) < 0) {
560 Py_DECREF(connectionRepr);
561 return NULL;
562 }
563 result = cxoUtils_formatString("<%s.%s on %s>",
564 PyTuple_Pack(3, module, name, connectionRepr));
565 Py_DECREF(module);
566 Py_DECREF(name);
567 Py_DECREF(connectionRepr);
568 return result;
569 }
570
571
572 //-----------------------------------------------------------------------------
573 // cxoSubscr_registerQuery()
574 // Register a query for database change notification.
575 //-----------------------------------------------------------------------------
576 static PyObject *cxoSubscr_registerQuery(cxoSubscr *subscr,
577 PyObject *args)
578 {
579 PyObject *statement, *executeArgs;
580 cxoBuffer statementBuffer;
581 uint32_t numQueryColumns;
582 cxoCursor *cursor;
583 uint64_t queryId;
584 int status;
585
586 // parse arguments
587 executeArgs = NULL;
588 if (!PyArg_ParseTuple(args, "O|O", &statement, &executeArgs))
589 return NULL;
590 if (executeArgs) {
591 if (!PyDict_Check(executeArgs) && !PySequence_Check(executeArgs)) {
592 PyErr_SetString(PyExc_TypeError,
593 "expecting a dictionary or sequence");
594 return NULL;
595 }
596 }
597
598 // create cursor to perform query
599 cursor = (cxoCursor*) PyObject_CallMethod((PyObject*) subscr->connection,
600 "cursor", NULL);
601 if (!cursor)
602 return NULL;
603
604 // prepare the statement for execution
605 if (cxoBuffer_fromObject(&statementBuffer, statement,
606 subscr->connection->encodingInfo.encoding) < 0) {
607 Py_DECREF(cursor);
608 return NULL;
609 }
610 status = dpiSubscr_prepareStmt(subscr->handle, statementBuffer.ptr,
611 statementBuffer.size, &cursor->handle);
612 cxoBuffer_clear(&statementBuffer);
613 if (status < 0) {
614 cxoError_raiseAndReturnNull();
615 Py_DECREF(cursor);
616 return NULL;
617 }
618
619 // perform binds
620 if (executeArgs && cxoCursor_setBindVariables(cursor, executeArgs, 1, 0,
621 0) < 0) {
622 Py_DECREF(cursor);
623 return NULL;
624 }
625 if (cxoCursor_performBind(cursor) < 0) {
626 Py_DECREF(cursor);
627 return NULL;
628 }
629
630 // perform the execute (which registers the query)
631 Py_BEGIN_ALLOW_THREADS
632 status = dpiStmt_execute(cursor->handle, DPI_MODE_EXEC_DEFAULT,
633 &numQueryColumns);
634 Py_END_ALLOW_THREADS
635 if (status < 0) {
636 cxoError_raiseAndReturnNull();
637 Py_DECREF(cursor);
638 return NULL;
639 }
640
641 // return the query id, if applicable
642 if (subscr->qos & DPI_SUBSCR_QOS_QUERY) {
643 if (dpiStmt_getSubscrQueryId(cursor->handle, &queryId) < 0) {
644 cxoError_raiseAndReturnNull();
645 Py_DECREF(cursor);
646 return NULL;
647 }
648 Py_DECREF(cursor);
649 return PyInt_FromLong((long) queryId);
650 }
651
652 Py_DECREF(cursor);
653 Py_RETURN_NONE;
654 }
655
656
657 //-----------------------------------------------------------------------------
658 // cxoMessage_free()
659 // Free the memory associated with a message.
660 //-----------------------------------------------------------------------------
661 static void cxoMessage_free(cxoMessage *message)
662 {
663 Py_CLEAR(message->subscription);
664 Py_CLEAR(message->dbname);
665 Py_CLEAR(message->tables);
666 Py_CLEAR(message->queries);
667 Py_CLEAR(message->queueName);
668 Py_CLEAR(message->consumerName);
669 Py_TYPE(message)->tp_free((PyObject*) message);
670 }
671
672
673 //-----------------------------------------------------------------------------
674 // cxoMessageTable_free()
675 // Free the memory associated with a table in a message.
676 //-----------------------------------------------------------------------------
677 static void cxoMessageTable_free(cxoMessageTable *table)
678 {
679 Py_CLEAR(table->name);
680 Py_CLEAR(table->rows);
681 Py_TYPE(table)->tp_free((PyObject*) table);
682 }
683
684
685 //-----------------------------------------------------------------------------
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
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoTransform.c
6 // Defines routines used to transform Python objects to database values and
7 // database values to Python objects.
8 //-----------------------------------------------------------------------------
9
10 #include "cxoModule.h"
11 #include "datetime.h"
12
13 // PyPy compatibility
14 #ifndef PyDateTime_DELTA_GET_DAYS
15 #define PyDateTime_DELTA_GET_DAYS(x) ((x)->days)
16 #endif
17
18 #ifndef PyDateTime_DELTA_GET_SECONDS
19 #define PyDateTime_DELTA_GET_SECONDS(x) ((x)->seconds)
20 #endif
21
22 #ifndef PyDateTime_DELTA_GET_MICROSECONDS
23 #define PyDateTime_DELTA_GET_MICROSECONDS(x) ((x)->microseconds)
24 #endif
25
26 //-----------------------------------------------------------------------------
27 // Types
28 //-----------------------------------------------------------------------------
29 typedef struct {
30 cxoTransformNum transformNum;
31 dpiOracleTypeNum oracleTypeNum;
32 dpiNativeTypeNum nativeTypeNum;
33 } cxoTransform;
34
35
36 //-----------------------------------------------------------------------------
37 // Globals
38 //-----------------------------------------------------------------------------
39 PyTypeObject *cxoPyTypeDate;
40 PyTypeObject *cxoPyTypeDateTime;
41 static PyTypeObject *cxoPyTypeDecimal;
42 static const cxoTransform cxoAllTransforms[] = {
43 {
44 CXO_TRANSFORM_NONE,
45 DPI_ORACLE_TYPE_VARCHAR,
46 DPI_NATIVE_TYPE_BYTES
47 },
48 {
49 CXO_TRANSFORM_BINARY,
50 DPI_ORACLE_TYPE_RAW,
51 DPI_NATIVE_TYPE_BYTES
52 },
53 {
54 CXO_TRANSFORM_BFILE,
55 DPI_ORACLE_TYPE_BFILE,
56 DPI_NATIVE_TYPE_LOB
57 },
58 {
59 CXO_TRANSFORM_BLOB,
60 DPI_ORACLE_TYPE_BLOB,
61 DPI_NATIVE_TYPE_LOB
62 },
63 {
64 CXO_TRANSFORM_BOOLEAN,
65 DPI_ORACLE_TYPE_BOOLEAN,
66 DPI_NATIVE_TYPE_BOOLEAN
67 },
68 {
69 CXO_TRANSFORM_CLOB,
70 DPI_ORACLE_TYPE_CLOB,
71 DPI_NATIVE_TYPE_LOB
72 },
73 {
74 CXO_TRANSFORM_CURSOR,
75 DPI_ORACLE_TYPE_STMT,
76 DPI_NATIVE_TYPE_STMT
77 },
78 {
79 CXO_TRANSFORM_DATE,
80 DPI_ORACLE_TYPE_DATE,
81 DPI_NATIVE_TYPE_TIMESTAMP
82 },
83 {
84 CXO_TRANSFORM_DATETIME,
85 DPI_ORACLE_TYPE_DATE,
86 DPI_NATIVE_TYPE_TIMESTAMP
87 },
88 {
89 CXO_TRANSFORM_DECIMAL,
90 DPI_ORACLE_TYPE_NUMBER,
91 DPI_NATIVE_TYPE_BYTES
92 },
93 {
94 CXO_TRANSFORM_FIXED_CHAR,
95 DPI_ORACLE_TYPE_CHAR,
96 DPI_NATIVE_TYPE_BYTES
97 },
98 {
99 CXO_TRANSFORM_FIXED_NCHAR,
100 DPI_ORACLE_TYPE_NCHAR,
101 DPI_NATIVE_TYPE_BYTES
102 },
103 {
104 CXO_TRANSFORM_FLOAT,
105 DPI_ORACLE_TYPE_NUMBER,
106 DPI_NATIVE_TYPE_BYTES
107 },
108 {
109 CXO_TRANSFORM_INT,
110 DPI_ORACLE_TYPE_NUMBER,
111 DPI_NATIVE_TYPE_BYTES
112 },
113 {
114 CXO_TRANSFORM_LONG_BINARY,
115 DPI_ORACLE_TYPE_LONG_RAW,
116 DPI_NATIVE_TYPE_BYTES
117 },
118 {
119 CXO_TRANSFORM_LONG_STRING,
120 DPI_ORACLE_TYPE_LONG_VARCHAR,
121 DPI_NATIVE_TYPE_BYTES
122 },
123 {
124 CXO_TRANSFORM_NATIVE_DOUBLE,
125 DPI_ORACLE_TYPE_NATIVE_DOUBLE,
126 DPI_NATIVE_TYPE_DOUBLE
127 },
128 {
129 CXO_TRANSFORM_NATIVE_FLOAT,
130 DPI_ORACLE_TYPE_NATIVE_FLOAT,
131 DPI_NATIVE_TYPE_FLOAT
132 },
133 {
134 CXO_TRANSFORM_NATIVE_INT,
135 DPI_ORACLE_TYPE_NATIVE_INT,
136 DPI_NATIVE_TYPE_INT64
137 },
138 {
139 CXO_TRANSFORM_NCLOB,
140 DPI_ORACLE_TYPE_NCLOB,
141 DPI_NATIVE_TYPE_LOB
142 },
143 {
144 CXO_TRANSFORM_NSTRING,
145 DPI_ORACLE_TYPE_NVARCHAR,
146 DPI_NATIVE_TYPE_BYTES
147 },
148 {
149 CXO_TRANSFORM_OBJECT,
150 DPI_ORACLE_TYPE_OBJECT,
151 DPI_NATIVE_TYPE_OBJECT
152 },
153 {
154 CXO_TRANSFORM_ROWID,
155 DPI_ORACLE_TYPE_ROWID,
156 DPI_NATIVE_TYPE_ROWID
157 },
158 {
159 CXO_TRANSFORM_STRING,
160 DPI_ORACLE_TYPE_VARCHAR,
161 DPI_NATIVE_TYPE_BYTES
162 },
163 {
164 CXO_TRANSFORM_TIMEDELTA,
165 DPI_ORACLE_TYPE_INTERVAL_DS,
166 DPI_NATIVE_TYPE_INTERVAL_DS
167 },
168 {
169 CXO_TRANSFORM_TIMESTAMP,
170 DPI_ORACLE_TYPE_TIMESTAMP,
171 DPI_NATIVE_TYPE_TIMESTAMP
172 },
173 {
174 CXO_TRANSFORM_TIMESTAMP_LTZ,
175 DPI_ORACLE_TYPE_TIMESTAMP_LTZ,
176 DPI_NATIVE_TYPE_TIMESTAMP
177 }
178 };
179
180
181 //-----------------------------------------------------------------------------
182 // cxoTransform_dateFromTicks()
183 // Creates a date from ticks (number of seconds since Unix epoch).
184 //-----------------------------------------------------------------------------
185 PyObject *cxoTransform_dateFromTicks(PyObject *args)
186 {
187 return PyDate_FromTimestamp(args);
188 }
189
190
191 //-----------------------------------------------------------------------------
192 // cxoTransform_fromPython()
193 // Transforms a Python object into its corresponding database value.
194 //-----------------------------------------------------------------------------
195 int cxoTransform_fromPython(cxoTransformNum transformNum, PyObject *pyValue,
196 dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding,
197 const char *nencoding, cxoVar *var, uint32_t arrayPos)
198 {
199 dpiIntervalDS *interval;
200 PyDateTime_Delta *delta;
201 int32_t deltaSeconds;
202 PyObject *textValue;
203 cxoObject *obj;
204 cxoLob *lob;
205 int status;
206
207 switch (transformNum) {
208 case CXO_TRANSFORM_BOOLEAN:
209 dbValue->asBoolean = (pyValue == Py_True);
210 return 0;
211 case CXO_TRANSFORM_BINARY:
212 case CXO_TRANSFORM_FIXED_CHAR:
213 case CXO_TRANSFORM_LONG_BINARY:
214 case CXO_TRANSFORM_LONG_STRING:
215 case CXO_TRANSFORM_STRING:
216 if (cxoBuffer_fromObject(buffer, pyValue, encoding) < 0)
217 return -1;
218 dbValue->asBytes.ptr = (char*) buffer->ptr;
219 dbValue->asBytes.length = buffer->size;
220 return 0;
221 case CXO_TRANSFORM_FIXED_NCHAR:
222 case CXO_TRANSFORM_NSTRING:
223 if (cxoBuffer_fromObject(buffer, pyValue, nencoding) < 0)
224 return -1;
225 dbValue->asBytes.ptr = (char*) buffer->ptr;
226 dbValue->asBytes.length = buffer->size;
227 return 0;
228 case CXO_TRANSFORM_BLOB:
229 case CXO_TRANSFORM_CLOB:
230 case CXO_TRANSFORM_NCLOB:
231 if (Py_TYPE(pyValue) == &cxoPyTypeLob) {
232 lob = (cxoLob*) pyValue;
233 if (var) {
234 if (dpiVar_setFromLob(var->handle, arrayPos,
235 lob->handle) < 0)
236 return cxoError_raiseAndReturnInt();
237 } else dbValue->asLOB = lob->handle;
238 return 0;
239 }
240 if (transformNum == CXO_TRANSFORM_NCLOB)
241 encoding = nencoding;
242 if (cxoBuffer_fromObject(buffer, pyValue, encoding) < 0)
243 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;
249 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
256 if (PyBool_Check(pyValue)) {
257 dbValue->asInt64 = (pyValue == Py_True);
258 return 0;
259 }
260 dbValue->asInt64 = PyLong_AsLong(pyValue);
261 if (PyErr_Occurred())
262 return -1;
263 return 0;
264 case CXO_TRANSFORM_INT:
265 case CXO_TRANSFORM_DECIMAL:
266 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;
283 dbValue->asBytes.ptr = (char*) buffer->ptr;
284 dbValue->asBytes.length = buffer->size;
285 return 0;
286 case CXO_TRANSFORM_NATIVE_DOUBLE:
287 case CXO_TRANSFORM_NATIVE_FLOAT:
288 if (!PyFloat_Check(pyValue) &&
289 #if PY_MAJOR_VERSION < 3
290 !PyInt_Check(pyValue) &&
291 #endif
292 !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal) &&
293 !PyLong_Check(pyValue)) {
294 PyErr_SetString(PyExc_TypeError, "expecting float");
295 return -1;
296 }
297 if (transformNum == CXO_TRANSFORM_NATIVE_FLOAT)
298 dbValue->asFloat = (float) PyFloat_AsDouble(pyValue);
299 else dbValue->asDouble = PyFloat_AsDouble(pyValue);
300 if (PyErr_Occurred())
301 return -1;
302 return 0;
303 case CXO_TRANSFORM_OBJECT:
304 if (Py_TYPE(pyValue) != &cxoPyTypeObject) {
305 PyErr_SetString(PyExc_TypeError, "expecting cx_Oracle.Object");
306 return -1;
307 }
308 obj = (cxoObject*) pyValue;
309 if (var) {
310 if (dpiVar_setFromObject(var->handle, arrayPos,
311 obj->handle) < 0)
312 return cxoError_raiseAndReturnInt();
313 } else dbValue->asObject = obj->handle;
314 return 0;
315 case CXO_TRANSFORM_DATE:
316 case CXO_TRANSFORM_DATETIME:
317 case CXO_TRANSFORM_TIMESTAMP:
318 case CXO_TRANSFORM_TIMESTAMP_LTZ:
319 if (PyDateTime_Check(pyValue)) {
320 memset(&dbValue->asTimestamp, 0, sizeof(dbValue->asTimestamp));
321 dbValue->asTimestamp.year = PyDateTime_GET_YEAR(pyValue);
322 dbValue->asTimestamp.month = PyDateTime_GET_MONTH(pyValue);
323 dbValue->asTimestamp.day = PyDateTime_GET_DAY(pyValue);
324 dbValue->asTimestamp.hour = PyDateTime_DATE_GET_HOUR(pyValue);
325 dbValue->asTimestamp.minute =
326 PyDateTime_DATE_GET_MINUTE(pyValue);
327 dbValue->asTimestamp.second =
328 PyDateTime_DATE_GET_SECOND(pyValue);
329 dbValue->asTimestamp.fsecond =
330 PyDateTime_DATE_GET_MICROSECOND(pyValue) * 1000;
331 } else if (PyDate_Check(pyValue)) {
332 memset(&dbValue->asTimestamp, 0, sizeof(dbValue->asTimestamp));
333 dbValue->asTimestamp.year = PyDateTime_GET_YEAR(pyValue);
334 dbValue->asTimestamp.month = PyDateTime_GET_MONTH(pyValue);
335 dbValue->asTimestamp.day = PyDateTime_GET_DAY(pyValue);
336 } else {
337 PyErr_SetString(PyExc_TypeError, "expecting date or datetime");
338 return -1;
339 }
340 return 0;
341 case CXO_TRANSFORM_TIMEDELTA:
342 if (!PyDelta_Check(pyValue)) {
343 PyErr_SetString(PyExc_TypeError, "expecting timedelta");
344 return -1;
345 }
346 delta = (PyDateTime_Delta*) pyValue;
347 interval = &dbValue->asIntervalDS;
348 deltaSeconds = PyDateTime_DELTA_GET_SECONDS(delta);
349 interval->days = PyDateTime_DELTA_GET_DAYS(delta);
350 interval->hours = deltaSeconds / 3600;
351 interval->seconds = deltaSeconds % 3600;
352 interval->minutes = interval->seconds / 60;
353 interval->seconds = interval->seconds % 60;
354 interval->fseconds =
355 PyDateTime_DELTA_GET_MICROSECONDS(delta) * 1000;
356 return 0;
357 default:
358 break;
359 }
360
361 cxoError_raiseFromString(cxoNotSupportedErrorException,
362 "Python value cannot be converted to a database value");
363 return -1;
364 }
365
366
367 //-----------------------------------------------------------------------------
368 // cxoTransform_getNumFromDataTypeInfo()
369 // Get the default transformation to use for the specified data type.
370 //-----------------------------------------------------------------------------
371 cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info)
372 {
373 switch (info->oracleTypeNum) {
374 case DPI_ORACLE_TYPE_VARCHAR:
375 return CXO_TRANSFORM_STRING;
376 case DPI_ORACLE_TYPE_NVARCHAR:
377 return CXO_TRANSFORM_NSTRING;
378 case DPI_ORACLE_TYPE_CHAR:
379 return CXO_TRANSFORM_FIXED_CHAR;
380 case DPI_ORACLE_TYPE_NCHAR:
381 return CXO_TRANSFORM_FIXED_NCHAR;
382 case DPI_ORACLE_TYPE_ROWID:
383 return CXO_TRANSFORM_ROWID;
384 case DPI_ORACLE_TYPE_RAW:
385 return CXO_TRANSFORM_BINARY;
386 case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
387 return CXO_TRANSFORM_NATIVE_DOUBLE;
388 case DPI_ORACLE_TYPE_NATIVE_FLOAT:
389 return CXO_TRANSFORM_NATIVE_FLOAT;
390 case DPI_ORACLE_TYPE_NUMBER:
391 if (info->scale == 0 ||
392 (info->scale == -127 && info->precision == 0))
393 return CXO_TRANSFORM_INT;
394 return CXO_TRANSFORM_FLOAT;
395 case DPI_ORACLE_TYPE_NATIVE_INT:
396 return CXO_TRANSFORM_NATIVE_INT;
397 case DPI_ORACLE_TYPE_DATE:
398 return CXO_TRANSFORM_DATETIME;
399 case DPI_ORACLE_TYPE_TIMESTAMP:
400 return CXO_TRANSFORM_TIMESTAMP;
401 case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
402 case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
403 return CXO_TRANSFORM_TIMESTAMP_LTZ;
404 case DPI_ORACLE_TYPE_INTERVAL_DS:
405 return CXO_TRANSFORM_TIMEDELTA;
406 case DPI_ORACLE_TYPE_CLOB:
407 return CXO_TRANSFORM_CLOB;
408 case DPI_ORACLE_TYPE_NCLOB:
409 return CXO_TRANSFORM_NCLOB;
410 case DPI_ORACLE_TYPE_BLOB:
411 return CXO_TRANSFORM_BLOB;
412 case DPI_ORACLE_TYPE_BFILE:
413 return CXO_TRANSFORM_BFILE;
414 case DPI_ORACLE_TYPE_STMT:
415 return CXO_TRANSFORM_CURSOR;
416 case DPI_ORACLE_TYPE_OBJECT:
417 return CXO_TRANSFORM_OBJECT;
418 case DPI_ORACLE_TYPE_LONG_VARCHAR:
419 return CXO_TRANSFORM_LONG_STRING;
420 case DPI_ORACLE_TYPE_LONG_RAW:
421 return CXO_TRANSFORM_LONG_BINARY;
422 case DPI_ORACLE_TYPE_BOOLEAN:
423 return CXO_TRANSFORM_BOOLEAN;
424 default:
425 break;
426 }
427 return CXO_TRANSFORM_UNSUPPORTED;
428 }
429
430
431 //-----------------------------------------------------------------------------
432 // cxoTransform_getNumFromType()
433 // Get the appropriate transformation to use for the specified Python type.
434 //-----------------------------------------------------------------------------
435 cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type)
436 {
437 if (type == &cxoPyTypeString)
438 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)
458 return CXO_TRANSFORM_BINARY;
459 if (type == &PyFloat_Type)
460 return CXO_TRANSFORM_FLOAT;
461 if (type == &PyLong_Type)
462 return CXO_TRANSFORM_INT;
463 if (type == cxoPyTypeDecimal)
464 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;
471 if (type == &PyBool_Type)
472 return CXO_TRANSFORM_BOOLEAN;
473 if (type == &cxoPyTypeBooleanVar)
474 return CXO_TRANSFORM_BOOLEAN;
475 if (type == PyDateTimeAPI->DateType)
476 return CXO_TRANSFORM_DATE;
477 if (type == PyDateTimeAPI->DateTimeType)
478 return CXO_TRANSFORM_DATETIME;
479 if (type == &cxoPyTypeDateTimeVar)
480 return CXO_TRANSFORM_DATETIME;
481 if (type == &cxoPyTypeTimestampVar)
482 return CXO_TRANSFORM_TIMESTAMP;
483 if (type == PyDateTimeAPI->DeltaType)
484 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;
505
506 return CXO_TRANSFORM_UNSUPPORTED;
507 }
508
509
510 //-----------------------------------------------------------------------------
511 // cxoTransform_getNumFromValue()
512 // Get the appropriate transformation to use for the specified Python object.
513 //-----------------------------------------------------------------------------
514 cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql)
515 {
516 cxoLob *lob;
517
518 if (value == Py_None)
519 return CXO_TRANSFORM_NONE;
520 if (PyBool_Check(value)) {
521 if (cxoClientVersionInfo.versionNum < 12 || !plsql)
522 return CXO_TRANSFORM_NATIVE_INT;
523 return CXO_TRANSFORM_BOOLEAN;
524 }
525 #if PY_MAJOR_VERSION >= 3
526 if (PyUnicode_Check(value))
527 return CXO_TRANSFORM_STRING;
528 if (PyBytes_Check(value))
529 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
540 if (PyLong_Check(value))
541 return CXO_TRANSFORM_INT;
542 if (PyFloat_Check(value))
543 return CXO_TRANSFORM_FLOAT;
544 if (PyDateTime_Check(value))
545 return CXO_TRANSFORM_DATETIME;
546 if (PyDate_Check(value))
547 return CXO_TRANSFORM_DATE;
548 if (PyDelta_Check(value))
549 return CXO_TRANSFORM_TIMEDELTA;
550 if (PyObject_TypeCheck(value, &cxoPyTypeCursor))
551 return CXO_TRANSFORM_CURSOR;
552 if (PyObject_TypeCheck(value, cxoPyTypeDecimal))
553 return CXO_TRANSFORM_DECIMAL;
554 if (PyObject_TypeCheck(value, &cxoPyTypeObject))
555 return CXO_TRANSFORM_OBJECT;
556 if (PyObject_TypeCheck(value, &cxoPyTypeLob)) {
557 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;
566 }
567 return CXO_TRANSFORM_UNSUPPORTED;
568 }
569
570
571 //-----------------------------------------------------------------------------
572 // cxoTransform_getTypeInfo()
573 // Get type information for the specified transform. The transform number is
574 // assumed to be a valid value at this point (not CXO_TRANSFORM_UNSUPPORTED).
575 //-----------------------------------------------------------------------------
576 void cxoTransform_getTypeInfo(cxoTransformNum transformNum,
577 dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum)
578 {
579 const cxoTransform *transform;
580
581 transform = &cxoAllTransforms[transformNum];
582 *oracleTypeNum = transform->oracleTypeNum;
583 *nativeTypeNum = transform->nativeTypeNum;
584 }
585
586
587 //-----------------------------------------------------------------------------
588 // cxoTransform_init()
589 // Import the necessary modules for performing transformations.
590 //-----------------------------------------------------------------------------
591 int cxoTransform_init(void)
592 {
593 PyObject *module;
594
595 // import the datetime module for datetime support
596 PyDateTime_IMPORT;
597 if (PyErr_Occurred())
598 return -1;
599 cxoPyTypeDate = PyDateTimeAPI->DateType;
600 cxoPyTypeDateTime = PyDateTimeAPI->DateTimeType;
601
602 // import the decimal module for decimal support
603 module = PyImport_ImportModule("decimal");
604 if (!module)
605 return -1;
606 cxoPyTypeDecimal =
607 (PyTypeObject*) PyObject_GetAttrString(module, "Decimal");
608 Py_DECREF(module);
609 if (!cxoPyTypeDecimal)
610 return -1;
611
612 return 0;
613 }
614
615
616 //-----------------------------------------------------------------------------
617 // cxoTransform_timestampFromTicks()
618 // Creates a timestamp from ticks (number of seconds since Unix epoch).
619 //-----------------------------------------------------------------------------
620 PyObject *cxoTransform_timestampFromTicks(PyObject *args)
621 {
622 return PyDateTime_FromTimestamp(args);
623 }
624
625
626 //-----------------------------------------------------------------------------
627 // cxoTransform_toPython()
628 // Transforms a database value into its corresponding Python object.
629 //-----------------------------------------------------------------------------
630 PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
631 cxoConnection *connection, cxoObjectType *objType,
632 dpiDataBuffer *dbValue, const char *encodingErrors)
633 {
634 const cxoTransform *transform;
635 PyObject *stringObj, *result;
636 dpiIntervalDS *intervalDS;
637 dpiTimestamp *timestamp;
638 uint32_t rowidLength;
639 const char *rowid;
640 cxoCursor *cursor;
641 dpiBytes *bytes;
642 int32_t seconds;
643
644 transform = &cxoAllTransforms[transformNum];
645 switch (transformNum) {
646 case CXO_TRANSFORM_BINARY:
647 case CXO_TRANSFORM_LONG_BINARY:
648 bytes = &dbValue->asBytes;
649 return PyBytes_FromStringAndSize(bytes->ptr, bytes->length);
650 case CXO_TRANSFORM_BFILE:
651 case CXO_TRANSFORM_BLOB:
652 case CXO_TRANSFORM_CLOB:
653 case CXO_TRANSFORM_NCLOB:
654 return cxoLob_new(connection, transform->oracleTypeNum,
655 dbValue->asLOB);
656 case CXO_TRANSFORM_BOOLEAN:
657 if (dbValue->asBoolean)
658 Py_RETURN_TRUE;
659 Py_RETURN_FALSE;
660 case CXO_TRANSFORM_CURSOR:
661 cursor = (cxoCursor*) PyObject_CallMethod((PyObject*) connection,
662 "cursor", NULL);
663 if (!cursor)
664 return NULL;
665 cursor->handle = dbValue->asStmt;
666 dpiStmt_addRef(cursor->handle);
667 cursor->fixupRefCursor = 1;
668 return (PyObject*) cursor;
669 case CXO_TRANSFORM_DATE:
670 timestamp = &dbValue->asTimestamp;
671 return PyDate_FromDate(timestamp->year, timestamp->month,
672 timestamp->day);
673 case CXO_TRANSFORM_DATETIME:
674 case CXO_TRANSFORM_TIMESTAMP:
675 case CXO_TRANSFORM_TIMESTAMP_LTZ:
676 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
682 return PyDateTime_FromDateAndTime(timestamp->year,
683 timestamp->month, timestamp->day, timestamp->hour,
684 timestamp->minute, timestamp->second,
685 timestamp->fsecond / 1000);
686 case CXO_TRANSFORM_FIXED_NCHAR:
687 case CXO_TRANSFORM_NSTRING:
688 bytes = &dbValue->asBytes;
689 return PyUnicode_Decode(bytes->ptr, bytes->length, bytes->encoding,
690 encodingErrors);
691 case CXO_TRANSFORM_NATIVE_DOUBLE:
692 return PyFloat_FromDouble(dbValue->asDouble);
693 case CXO_TRANSFORM_NATIVE_FLOAT:
694 return PyFloat_FromDouble(dbValue->asFloat);
695 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
701 return PyLong_FromLongLong(dbValue->asInt64);
702 case CXO_TRANSFORM_DECIMAL:
703 case CXO_TRANSFORM_INT:
704 case CXO_TRANSFORM_FLOAT:
705 bytes = &dbValue->asBytes;
706 stringObj = cxoPyString_fromEncodedString(bytes->ptr,
707 bytes->length, bytes->encoding, encodingErrors);
708 if (!stringObj)
709 return NULL;
710 if (transformNum == CXO_TRANSFORM_INT &&
711 memchr(bytes->ptr, '.', bytes->length) == NULL) {
712 #if PY_MAJOR_VERSION >= 3
713 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) {
721 result = PyNumber_Float(stringObj);
722 Py_DECREF(stringObj);
723 return result;
724 }
725 result = PyObject_CallFunctionObjArgs(
726 (PyObject*) cxoPyTypeDecimal, stringObj, NULL);
727 Py_DECREF(stringObj);
728 return result;
729 case CXO_TRANSFORM_OBJECT:
730 return cxoObject_new(objType, dbValue->asObject);
731 case CXO_TRANSFORM_ROWID:
732 if (dpiRowid_getStringValue(dbValue->asRowid, &rowid,
733 &rowidLength) < 0)
734 return cxoError_raiseAndReturnNull();
735 return cxoPyString_fromEncodedString(rowid, rowidLength,
736 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);
743 case CXO_TRANSFORM_TIMEDELTA:
744 intervalDS = &dbValue->asIntervalDS;
745 seconds = intervalDS->hours * 60 * 60 + intervalDS->minutes * 60 +
746 intervalDS->seconds;
747 return PyDelta_FromDSU(intervalDS->days, seconds,
748 intervalDS->fseconds / 1000);
749 default:
750 break;
751 }
752
753 return cxoError_raiseFromString(cxoNotSupportedErrorException,
754 "Database value cannot be converted to a Python value");
755 }
756
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2 //-----------------------------------------------------------------------------
3
4 //-----------------------------------------------------------------------------
5 // cxoUtils.c
6 // Utility functions used in cx_Oracle.
7 //-----------------------------------------------------------------------------
8
9 #include "cxoModule.h"
10
11 //-----------------------------------------------------------------------------
12 // cxoUtils_formatString()
13 // Return a Python string formatted using the given format string and
14 // arguments. The arguments have a reference taken from them after they have
15 // been used (which should mean that they are destroyed).
16 //-----------------------------------------------------------------------------
17 PyObject *cxoUtils_formatString(const char *format, PyObject *args)
18 {
19 PyObject *formatObj, *result;
20
21 // assume that a NULL value for arguments implies building the arguments
22 // failed and a Python exception has already been raised
23 if (!args)
24 return NULL;
25
26 // convert string format to Python object
27 #if PY_MAJOR_VERSION >= 3
28 formatObj = PyUnicode_DecodeASCII(format, strlen(format), NULL);
29 #else
30 formatObj = PyString_FromString(format);
31 #endif
32 if (!formatObj) {
33 Py_DECREF(args);
34 return NULL;
35 }
36
37 // create formatted result
38 #if PY_MAJOR_VERSION >= 3
39 result = PyUnicode_Format(formatObj, args);
40 #else
41 result = PyString_Format(formatObj, args);
42 #endif
43 Py_DECREF(args);
44 Py_DECREF(formatObj);
45 return result;
46 }
47
48
49 //-----------------------------------------------------------------------------
50 // cxoUtils_getAdjustedEncoding()
51 // Return the adjusted encoding to use when encoding and decoding strings
52 // that are passed to and from the Oracle database. The Oracle client interface
53 // does not support the inclusion of a BOM in the encoded string but assumes
54 // native endian order for UTF-16. Python generates a BOM at the beginning of
55 // the encoded string if plain UTF-16 is specified. For this reason, the
56 // correct byte order must be determined and used inside Python so that the
57 // Oracle client receives the data it expects.
58 //-----------------------------------------------------------------------------
59 const char *cxoUtils_getAdjustedEncoding(const char *encoding)
60 {
61 static const union {
62 unsigned char bytes[4];
63 uint32_t value;
64 } hostOrder = { { 0, 1, 2, 3 } };
65
66 if (!encoding || strcmp(encoding, "UTF-16") != 0)
67 return encoding;
68 return (hostOrder.value == 0x03020100) ? "UTF-16LE" : "UTF-16BE";
69 }
70
71
72 //-----------------------------------------------------------------------------
73 // cxoUtils_getBooleanValue()
74 // Get a boolean value from a Python object.
75 //-----------------------------------------------------------------------------
76 int cxoUtils_getBooleanValue(PyObject *obj, int defaultValue, int *value)
77 {
78 if (!obj)
79 *value = defaultValue;
80 else {
81 *value = PyObject_IsTrue(obj);
82 if (*value < 0)
83 return -1;
84 }
85 return 0;
86 }
87
88
89 //-----------------------------------------------------------------------------
90 // cxoUtils_getModuleAndName()
91 // Return the module and name for the type.
92 //-----------------------------------------------------------------------------
93 int cxoUtils_getModuleAndName(PyTypeObject *type, PyObject **module,
94 PyObject **name)
95 {
96 *module = PyObject_GetAttrString( (PyObject*) type, "__module__");
97 if (!*module)
98 return -1;
99 *name = PyObject_GetAttrString( (PyObject*) type, "__name__");
100 if (!*name) {
101 Py_DECREF(*module);
102 return -1;
103 }
104 return 0;
105 }
106
107
108 //-----------------------------------------------------------------------------
109 // cxoUtils_initializeDPI()
110 // Initialize the ODPI-C library. This is done when the first standalone
111 // connection or session pool is created, rather than when the module is first
112 // imported so that manipulating environment variables such as NLS_LANG will
113 // work as expected. It also has the additional benefit of reducing the number
114 // of errors that can take place when the module is imported.
115 //-----------------------------------------------------------------------------
116 int cxoUtils_initializeDPI(void)
117 {
118 dpiErrorInfo errorInfo;
119 dpiContext *context;
120
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
130 return 0;
131 }
132
133
134 //-----------------------------------------------------------------------------
135 // cxoUtils_processJsonArg()
136 // Process the argument which is expected to be either a string or bytes, or
137 // a dictionary or list which is converted to a string via the json.dumps()
138 // method. All strings are encoded to UTF-8 which is what SODA expects.
139 //-----------------------------------------------------------------------------
140 int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer)
141 {
142 int converted = 0;
143
144 if (arg && (PyDict_Check(arg) || PyList_Check(arg))) {
145 arg = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, arg, NULL);
146 if (!arg)
147 return -1;
148 converted = 1;
149 }
150 if (cxoBuffer_fromObject(buffer, arg, "UTF-8") < 0)
151 return -1;
152 if (converted)
153 Py_DECREF(arg);
154
155 return 0;
156 }
157
158
159 //-----------------------------------------------------------------------------
160 // cxoUtils_processSodaDocArg()
161 // Process a SODA document argument. This is expectd to be an actual SODA
162 // document object or a dictionary. If the argument refers to a dictionary or
163 // list, a new SODA document will be created with the given content and without
164 // a key or media type specified.
165 //-----------------------------------------------------------------------------
166 int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg,
167 cxoSodaDoc **doc)
168 {
169 dpiSodaDoc *handle;
170 cxoBuffer buffer;
171
172 if (PyObject_TypeCheck(arg, &cxoPyTypeSodaDoc)) {
173 Py_INCREF(arg);
174 *doc = (cxoSodaDoc*) arg;
175 } else if (PyDict_Check(arg) || PyList_Check(arg)) {
176 arg = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, arg, NULL);
177 if (!arg)
178 return -1;
179 if (cxoBuffer_fromObject(&buffer, arg, "UTF-8") < 0) {
180 Py_DECREF(arg);
181 return -1;
182 }
183 Py_DECREF(arg);
184 if (dpiSodaDb_createDocument(db->handle, NULL, 0, buffer.ptr,
185 buffer.size, NULL, 0, DPI_SODA_FLAGS_DEFAULT, &handle) < 0) {
186 cxoError_raiseAndReturnNull();
187 cxoBuffer_clear(&buffer);
188 return -1;
189 }
190 cxoBuffer_clear(&buffer);
191 *doc = cxoSodaDoc_new(db, handle);
192 if (!*doc)
193 return -1;
194 } else {
195 PyErr_SetString(PyExc_TypeError,
196 "value must be a SODA document or dictionary");
197 return -1;
198 }
199
200 return 0;
201 }
202
0 //-----------------------------------------------------------------------------
1 // Copyright (c) 2016, 2018, 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 // cxoVar.c
11 // Defines Python types for Oracle variables.
12 //-----------------------------------------------------------------------------
13
14 #include "cxoModule.h"
15
16 //-----------------------------------------------------------------------------
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 //-----------------------------------------------------------------------------
126 // cxoVar_new()
127 // Allocate a new variable.
128 //-----------------------------------------------------------------------------
129 cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
130 Py_ssize_t size, int isArray, cxoObjectType *objType)
131 {
132 dpiObjectType *typeHandle = NULL;
133 dpiOracleTypeNum oracleTypeNum;
134 dpiNativeTypeNum nativeTypeNum;
135 cxoVar *var;
136
137 // attempt to allocate the object
138 var = (cxoVar*) type->pythonType->tp_alloc(type->pythonType, 0);
139 if (!var)
140 return NULL;
141
142 // perform basic initialization
143 Py_INCREF(cursor->connection);
144 var->connection = cursor->connection;
145 if (objType) {
146 Py_INCREF(objType);
147 var->objectType = objType;
148 typeHandle = objType->handle;
149 }
150 if (numElements == 0)
151 numElements = 1;
152 var->allocatedElements = (uint32_t) numElements;
153 var->type = type;
154 var->size = (size == 0) ? type->size : (uint32_t) size;
155 var->isArray = isArray;
156
157 // acquire and initialize DPI variable
158 cxoTransform_getTypeInfo(type->transformNum, &oracleTypeNum,
159 &nativeTypeNum);
160 if (dpiConn_newVar(cursor->connection->handle, oracleTypeNum,
161 nativeTypeNum, var->allocatedElements, var->size, 0, isArray,
162 typeHandle, &var->handle, &var->data) < 0) {
163 cxoError_raiseAndReturnNull();
164 Py_DECREF(var);
165 return NULL;
166 }
167
168 // get buffer size for information
169 if (dpiVar_getSizeInBytes(var->handle, &var->bufferSize) < 0) {
170 cxoError_raiseAndReturnNull();
171 Py_DECREF(var);
172 return NULL;
173 }
174
175 return var;
176 }
177
178
179 //-----------------------------------------------------------------------------
180 // cxoVar_free()
181 // Free an existing variable.
182 //-----------------------------------------------------------------------------
183 static void cxoVar_free(cxoVar *var)
184 {
185 if (var->handle) {
186 Py_BEGIN_ALLOW_THREADS
187 dpiVar_release(var->handle);
188 Py_END_ALLOW_THREADS
189 var->handle = NULL;
190 }
191 if (var->encodingErrors)
192 PyMem_Free((void*) var->encodingErrors);
193 Py_CLEAR(var->connection);
194 Py_CLEAR(var->inConverter);
195 Py_CLEAR(var->outConverter);
196 Py_CLEAR(var->objectType);
197 Py_TYPE(var)->tp_free((PyObject*) var);
198 }
199
200
201 //-----------------------------------------------------------------------------
202 // cxoVar_check()
203 // Returns a boolean indicating if the object is a variable.
204 //-----------------------------------------------------------------------------
205 int cxoVar_check(PyObject *object)
206 {
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);
230 }
231
232
233 //-----------------------------------------------------------------------------
234 // cxoVar_newByValue()
235 // Allocate a new variable by looking at the type of the data.
236 //-----------------------------------------------------------------------------
237 cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
238 Py_ssize_t numElements)
239 {
240 PyObject *result, *inputTypeHandler = NULL;
241 cxoObjectType *objType = NULL;
242 cxoVarType *varType;
243 Py_ssize_t size;
244 cxoObject *obj;
245 int isArray;
246
247 // determine if an input type handler should be used; an input type handler
248 // defined on the cursor takes precedence over one defined on the
249 // connection to which the cursor belongs; the input type handler should
250 // return a variable or None; the value None implies that the default
251 // processing should take place just as if no input type handler was
252 // defined
253 if (cursor->inputTypeHandler && cursor->inputTypeHandler != Py_None)
254 inputTypeHandler = cursor->inputTypeHandler;
255 else if (cursor->connection->inputTypeHandler &&
256 cursor->connection->inputTypeHandler != Py_None)
257 inputTypeHandler = cursor->connection->inputTypeHandler;
258 if (inputTypeHandler) {
259 result = PyObject_CallFunction(inputTypeHandler, "OOn", cursor, value,
260 numElements);
261 if (!result)
262 return NULL;
263 if (result != Py_None) {
264 if (!cxoVar_check(result)) {
265 Py_DECREF(result);
266 PyErr_SetString(PyExc_TypeError,
267 "expecting variable from input type handler");
268 return NULL;
269 }
270 return (cxoVar*) result;
271 }
272 Py_DECREF(result);
273 }
274
275 // 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) {
281 obj = (cxoObject*) value;
282 objType = obj->objectType;
283 }
284 return cxoVar_new(cursor, numElements, varType, size, isArray, objType);
285 }
286
287
288 //-----------------------------------------------------------------------------
289 // cxoVar_newArrayByType()
290 // Allocate a new PL/SQL array by looking at the Python data type.
291 //-----------------------------------------------------------------------------
292 static cxoVar *cxoVar_newArrayByType(cxoCursor *cursor,
293 PyObject *value)
294 {
295 PyObject *typeObj, *numElementsObj;
296 cxoObjectType *objType;
297 cxoVarType *varType;
298 uint32_t numElements;
299 int ok;
300
301 // validate parameters
302 ok = (PyList_GET_SIZE(value) == 2);
303 if (ok) {
304 typeObj = PyList_GET_ITEM(value, 0);
305 ok = PyType_Check(typeObj);
306 }
307 if (ok) {
308 numElementsObj = PyList_GET_ITEM(value, 1);
309 ok = PyInt_Check(numElementsObj);
310 }
311 if (!ok) {
312 cxoError_raiseFromString(cxoProgrammingErrorException,
313 "expecting an array of two elements [type, numelems]");
314 return NULL;
315 }
316
317 // create variable
318 varType = cxoVarType_fromPythonType(typeObj, &objType);
319 if (!varType)
320 return NULL;
321 numElements = PyInt_AsLong(numElementsObj);
322 if (PyErr_Occurred())
323 return NULL;
324 return cxoVar_new(cursor, numElements, varType, varType->size, 1, objType);
325 }
326
327
328 //-----------------------------------------------------------------------------
329 // cxoVar_newByType()
330 // Allocate a new variable by looking at the Python data type.
331 //-----------------------------------------------------------------------------
332 cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
333 uint32_t numElements)
334 {
335 cxoObjectType *objType;
336 cxoVarType *varType;
337 long size;
338
339 // passing an integer is assumed to be a string
340 if (PyInt_Check(value)) {
341 size = PyInt_AsLong(value);
342 if (PyErr_Occurred())
343 return NULL;
344 varType = cxoVarType_fromPythonType((PyObject*) &cxoPyTypeString,
345 &objType);
346 return cxoVar_new(cursor, numElements, varType, size, 0, objType);
347 }
348
349 // passing an array of two elements to define an array
350 if (PyList_Check(value))
351 return cxoVar_newArrayByType(cursor, value);
352
353 // handle directly bound variables
354 if (cxoVar_check(value)) {
355 Py_INCREF(value);
356 return (cxoVar*) value;
357 }
358
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;
370 }
371
372
373 //-----------------------------------------------------------------------------
374 // cxoVar_bind()
375 // Allocate a variable and bind it to the given statement.
376 //-----------------------------------------------------------------------------
377 int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos)
378 {
379 cxoBuffer nameBuffer;
380 int status;
381
382 // perform the bind
383 if (name) {
384 if (cxoBuffer_fromObject(&nameBuffer, name,
385 cursor->connection->encodingInfo.encoding) < 0)
386 return -1;
387 status = dpiStmt_bindByName(cursor->handle, (char*) nameBuffer.ptr,
388 nameBuffer.size, var->handle);
389 cxoBuffer_clear(&nameBuffer);
390 } else {
391 status = dpiStmt_bindByPos(cursor->handle, pos, var->handle);
392 }
393 if (status < 0)
394 return cxoError_raiseAndReturnInt();
395
396 // set flag if bound to a DML returning statement and no data set
397 if (cursor->stmtInfo.isReturning && !var->isValueSet)
398 var->getReturnedData = 1;
399
400 return 0;
401 }
402
403
404 //-----------------------------------------------------------------------------
405 // cxoVar_getArrayValue()
406 // Return the value of the variable as an array.
407 //-----------------------------------------------------------------------------
408 static PyObject *cxoVar_getArrayValue(cxoVar *var, uint32_t numElements,
409 dpiData *data)
410 {
411 PyObject *value, *singleValue;
412 uint32_t i;
413
414 value = PyList_New(numElements);
415 if (!value)
416 return NULL;
417
418 for (i = 0; i < numElements; i++) {
419 singleValue = cxoVar_getSingleValue(var, data, i);
420 if (!singleValue) {
421 Py_DECREF(value);
422 return NULL;
423 }
424 PyList_SET_ITEM(value, i, singleValue);
425 }
426
427 return value;
428 }
429
430
431 //-----------------------------------------------------------------------------
432 // cxoVar_getSingleValue()
433 // Return the value of the variable at the given position.
434 //-----------------------------------------------------------------------------
435 PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos)
436 {
437 PyObject *value, *result;
438 uint32_t numReturnedRows;
439 dpiData *returnedData;
440
441 // handle DML returning
442 if (!data && var->getReturnedData) {
443 if (dpiVar_getReturnedData(var->handle, arrayPos, &numReturnedRows,
444 &returnedData) < 0)
445 return cxoError_raiseAndReturnNull();
446 return cxoVar_getArrayValue(var, numReturnedRows, returnedData);
447 }
448
449 // in all other cases, just get the value stored at specified position
450 if (data)
451 data = &data[arrayPos];
452 else data = &var->data[arrayPos];
453 if (data->isNull)
454 Py_RETURN_NONE;
455 value = cxoTransform_toPython(var->type->transformNum, var->connection,
456 var->objectType, &data->value, var->encodingErrors);
457 if (value) {
458 switch (var->type->transformNum) {
459 case CXO_TRANSFORM_BFILE:
460 case CXO_TRANSFORM_BLOB:
461 case CXO_TRANSFORM_CLOB:
462 case CXO_TRANSFORM_NCLOB:
463 dpiLob_addRef(data->value.asLOB);
464 break;
465 case CXO_TRANSFORM_OBJECT:
466 dpiObject_addRef(data->value.asObject);
467 break;
468 default:
469 break;
470 }
471 if (var->outConverter && var->outConverter != Py_None) {
472 result = PyObject_CallFunctionObjArgs(var->outConverter, value,
473 NULL);
474 Py_DECREF(value);
475 return result;
476 }
477 }
478
479 return value;
480 }
481
482
483 //-----------------------------------------------------------------------------
484 // cxoVar_getValue()
485 // Return the value of the variable.
486 //-----------------------------------------------------------------------------
487 PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos)
488 {
489 uint32_t numElements;
490
491 if (var->isArray) {
492 if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
493 return cxoError_raiseAndReturnNull();
494 return cxoVar_getArrayValue(var, numElements, var->data);
495 }
496 if (arrayPos >= var->allocatedElements && !var->getReturnedData) {
497 PyErr_SetString(PyExc_IndexError,
498 "cxoVar_getSingleValue: array size exceeded");
499 return NULL;
500 }
501 return cxoVar_getSingleValue(var, NULL, arrayPos);
502 }
503
504
505 //-----------------------------------------------------------------------------
506 // cxoVar_setValueBytes()
507 // Set a value in the variable from a byte string of some sort.
508 //-----------------------------------------------------------------------------
509 static int cxoVar_setValueBytes(cxoVar *var, uint32_t pos, dpiData *data,
510 cxoBuffer *buffer)
511 {
512 dpiData *tempVarData, *sourceData;
513 dpiOracleTypeNum oracleTypeNum;
514 dpiNativeTypeNum nativeTypeNum;
515 uint32_t i, numElements;
516 dpiVar *tempVarHandle;
517 int status;
518
519 if (buffer->size > var->bufferSize) {
520 cxoTransform_getTypeInfo(var->type->transformNum, &oracleTypeNum,
521 &nativeTypeNum);
522 if (dpiConn_newVar(var->connection->handle, oracleTypeNum,
523 nativeTypeNum, var->allocatedElements, buffer->size, 0,
524 var->isArray, NULL, &tempVarHandle, &tempVarData) < 0)
525 return cxoError_raiseAndReturnInt();
526 if (var->isArray) {
527 if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0) {
528 cxoError_raiseAndReturnInt();
529 dpiVar_release(tempVarHandle);
530 return -1;
531 }
532 if (dpiVar_setNumElementsInArray(tempVarHandle, numElements) < 0) {
533 cxoError_raiseAndReturnInt();
534 dpiVar_release(tempVarHandle);
535 return -1;
536 }
537 }
538 for (i = 0; i < var->allocatedElements; i++) {
539 sourceData = &var->data[i];
540 if (i == pos || sourceData->isNull)
541 continue;
542 if (dpiVar_setFromBytes(tempVarHandle, i,
543 sourceData->value.asBytes.ptr,
544 sourceData->value.asBytes.length) < 0) {
545 cxoError_raiseAndReturnInt();
546 dpiVar_release(tempVarHandle);
547 return -1;
548 }
549 }
550 dpiVar_release(var->handle);
551 var->handle = tempVarHandle;
552 var->data = tempVarData;
553 var->size = buffer->numCharacters;
554 var->bufferSize = buffer->size;
555 }
556 status = dpiVar_setFromBytes(var->handle, pos, buffer->ptr, buffer->size);
557 if (status < 0)
558 return cxoError_raiseAndReturnInt();
559 return 0;
560 }
561
562
563 //-----------------------------------------------------------------------------
564 // cxoVar_setValueCursor()
565 // Set the value of the variable (which is assumed to be a cursor).
566 //-----------------------------------------------------------------------------
567 static int cxoVar_setValueCursor(cxoVar *var, uint32_t pos, dpiData *data,
568 PyObject *value)
569 {
570 cxoCursor *cursor;
571 dpiStmtInfo info;
572
573 if (!PyObject_IsInstance(value, (PyObject*) &cxoPyTypeCursor)) {
574 PyErr_SetString(PyExc_TypeError, "expecting cursor");
575 return -1;
576 }
577
578 // if the cursor already has a handle, use it directly
579 cursor = (cxoCursor *) value;
580 if (cursor->handle) {
581 if (dpiVar_setFromStmt(var->handle, pos, cursor->handle) < 0)
582 return cxoError_raiseAndReturnInt();
583
584 // otherwise, make use of the statement handle allocated by the variable
585 // BUT, make sure the statement handle is still valid as it may have been
586 // closed by some other code; the call to dpiStmt_getInfo() will ensure the
587 // statement is still open; if an error occurs, this bind will be discarded
588 // and a second attempt will be made with a new cursor
589 } else {
590 if (dpiStmt_getInfo(data->value.asStmt, &info) < 0)
591 return cxoError_raiseAndReturnInt();
592 cursor->handle = data->value.asStmt;
593 dpiStmt_addRef(cursor->handle);
594 }
595 cursor->fixupRefCursor = 1;
596 return 0;
597 }
598
599
600 //-----------------------------------------------------------------------------
601 // cxoVar_setSingleValue()
602 // Set a single value in the variable.
603 //-----------------------------------------------------------------------------
604 static int cxoVar_setSingleValue(cxoVar *var, uint32_t arrayPos,
605 PyObject *value)
606 {
607 dpiDataBuffer tempDbValue, *dbValue;
608 PyObject *convertedValue = NULL;
609 cxoBuffer buffer;
610 int result = 0;
611 dpiData *data;
612
613 // ensure we do not exceed the number of allocated elements
614 if (arrayPos >= var->allocatedElements) {
615 PyErr_SetString(PyExc_IndexError,
616 "cxoVar_setSingleValue: array size exceeded");
617 return -1;
618 }
619
620 // convert value, if necessary
621 if (var->inConverter && var->inConverter != Py_None) {
622 convertedValue = PyObject_CallFunctionObjArgs(var->inConverter, value,
623 NULL);
624 if (!convertedValue)
625 return -1;
626 value = convertedValue;
627 }
628
629 // tranform value from Python to value expected by ODPI-C
630 data = &var->data[arrayPos];
631 data->isNull = (value == Py_None);
632 if (!data->isNull) {
633 if (var->type->transformNum == CXO_TRANSFORM_CURSOR)
634 result = cxoVar_setValueCursor(var, arrayPos, data, value);
635 else {
636 cxoBuffer_init(&buffer);
637 if (var->type->size > 0)
638 dbValue = &tempDbValue;
639 else dbValue = &data->value;
640 result = cxoTransform_fromPython(var->type->transformNum, value,
641 dbValue, &buffer, var->connection->encodingInfo.encoding,
642 var->connection->encodingInfo.nencoding, var, arrayPos);
643 if (result == 0 && var->type->size > 0)
644 result = cxoVar_setValueBytes(var, arrayPos, data, &buffer);
645 cxoBuffer_clear(&buffer);
646 }
647 }
648 Py_CLEAR(convertedValue);
649 return result;
650 }
651
652
653 //-----------------------------------------------------------------------------
654 // cxoVar_setArrayValue()
655 // Set all of the array values for the variable.
656 //-----------------------------------------------------------------------------
657 static int cxoVar_setArrayValue(cxoVar *var, PyObject *value)
658 {
659 Py_ssize_t numElements, i;
660
661 // ensure we have an array to set
662 if (!PyList_Check(value)) {
663 PyErr_SetString(PyExc_TypeError, "expecting array data");
664 return -1;
665 }
666
667 // set the number of actual elements
668 numElements = PyList_GET_SIZE(value);
669 if (dpiVar_setNumElementsInArray(var->handle, (uint32_t) numElements) < 0)
670 return cxoError_raiseAndReturnInt();
671
672 // set all of the values
673 for (i = 0; i < numElements; i++) {
674 if (cxoVar_setSingleValue(var, i, PyList_GET_ITEM(value, i)) < 0)
675 return -1;
676 }
677
678 return 0;
679 }
680
681
682 //-----------------------------------------------------------------------------
683 // cxoVar_setValue()
684 // Set the value of the variable.
685 //-----------------------------------------------------------------------------
686 int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value)
687 {
688 var->isValueSet = 1;
689 if (var->isArray) {
690 if (arrayPos > 0) {
691 cxoError_raiseFromString(cxoNotSupportedErrorException,
692 "arrays of arrays are not supported by the OCI");
693 return -1;
694 }
695 return cxoVar_setArrayValue(var, value);
696 }
697 return cxoVar_setSingleValue(var, arrayPos, value);
698 }
699
700
701 //-----------------------------------------------------------------------------
702 // cxoVar_externalCopy()
703 // Copy the contents of the source variable to the destination variable.
704 //-----------------------------------------------------------------------------
705 static PyObject *cxoVar_externalCopy(cxoVar *targetVar, PyObject *args)
706 {
707 uint32_t sourcePos, targetPos;
708 cxoVar *sourceVar;
709
710 if (!PyArg_ParseTuple(args, "Oii", &sourceVar, &sourcePos, &targetPos))
711 return NULL;
712 if (Py_TYPE(targetVar) != Py_TYPE(sourceVar))
713 return cxoError_raiseFromString(cxoProgrammingErrorException,
714 "source and target variable type must match");
715 if (dpiVar_copyData(targetVar->handle, targetPos, sourceVar->handle,
716 sourcePos) < 0)
717 return cxoError_raiseAndReturnNull();
718
719 Py_RETURN_NONE;
720 }
721
722
723 //-----------------------------------------------------------------------------
724 // cxoVar_externalSetValue()
725 // Set the value of the variable at the given position.
726 //-----------------------------------------------------------------------------
727 static PyObject *cxoVar_externalSetValue(cxoVar *var, PyObject *args)
728 {
729 PyObject *value;
730 uint32_t pos;
731
732 if (!PyArg_ParseTuple(args, "iO", &pos, &value))
733 return NULL;
734 if (cxoVar_setValue(var, pos, value) < 0)
735 return NULL;
736
737 Py_RETURN_NONE;
738 }
739
740
741 //-----------------------------------------------------------------------------
742 // cxoVar_externalGetValue()
743 // Return the value of the variable at the given position.
744 //-----------------------------------------------------------------------------
745 static PyObject *cxoVar_externalGetValue(cxoVar *var, PyObject *args,
746 PyObject *keywordArgs)
747 {
748 static char *keywordList[] = { "pos", NULL };
749 uint32_t pos = 0;
750
751 if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
752 &pos))
753 return NULL;
754 return cxoVar_getValue(var, pos);
755 }
756
757
758 //-----------------------------------------------------------------------------
759 // cxoVar_externalGetActualElements()
760 // Return the values of the variable at all positions as a list.
761 //-----------------------------------------------------------------------------
762 static PyObject *cxoVar_externalGetActualElements(cxoVar *var,
763 void *unused)
764 {
765 uint32_t numElements = var->allocatedElements;
766
767 if (var->isArray &&
768 dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
769 return cxoError_raiseAndReturnNull();
770 return PyInt_FromLong(numElements);
771 }
772
773
774 //-----------------------------------------------------------------------------
775 // cxoVar_externalGetValues()
776 // Return the values of the variable at all positions as a list.
777 //-----------------------------------------------------------------------------
778 static PyObject *cxoVar_externalGetValues(cxoVar *var, void *unused)
779 {
780 uint32_t numElements = var->allocatedElements;
781
782 if (var->isArray &&
783 dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
784 return cxoError_raiseAndReturnNull();
785 return cxoVar_getArrayValue(var, numElements, NULL);
786 }
787
788
789 //-----------------------------------------------------------------------------
790 // cxoVar_repr()
791 // Return a string representation of the variable.
792 //-----------------------------------------------------------------------------
793 static PyObject *cxoVar_repr(cxoVar *var)
794 {
795 PyObject *value, *module, *name, *result;
796 uint32_t numElements;
797
798 if (var->isArray) {
799 if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
800 return cxoError_raiseAndReturnNull();
801 value = cxoVar_getArrayValue(var, numElements, var->data);
802 } else if (var->allocatedElements == 1)
803 value = cxoVar_getSingleValue(var, NULL, 0);
804 else value = cxoVar_getArrayValue(var, var->allocatedElements, NULL);
805 if (!value)
806 return NULL;
807 if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) {
808 Py_DECREF(value);
809 return NULL;
810 }
811 result = cxoUtils_formatString("<%s.%s with value %r>",
812 PyTuple_Pack(3, module, name, value));
813 Py_DECREF(module);
814 Py_DECREF(name);
815 Py_DECREF(value);
816 return result;
817 }
818
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # DropTest.py
6 #
7 # Drops the database objects used for the cx_Oracle test suite.
8 #------------------------------------------------------------------------------
9
10 from __future__ import print_function
11
12 import cx_Oracle
13 import TestEnv
14
15 def DropTests(conn):
16 print("Dropping test schemas...")
17 TestEnv.RunSqlScript(conn, "DropTest",
18 main_user = TestEnv.GetMainUser(),
19 proxy_user = TestEnv.GetProxyUser())
20
21 if __name__ == "__main__":
22 conn = cx_Oracle.connect(TestEnv.GetSysdbaConnectString(),
23 mode = cx_Oracle.SYSDBA)
24 DropTests(conn)
25 print("Done.")
26
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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
0 This directory contains the test suite for cx_Oracle.
1
2 1. The schemas and SQL objects that are referenced in the test suite can be
3 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:
8
9 python SetupTest.py
10
11 Alternatively, the [SQL script][3] can be run directly via SQL\*Plus, which
12 will always prompt for the names of the schemas that will be created. Run
13 the script using the following command:
14
15 sqlplus sys/syspassword@hostname/servicename @sql/SetupTest.sql
16
17 2. Run the test suite by issuing the following command in the top-level
18 directory of your cx_Oracle installation:
19
20 python setup.py test
21
22 Alternatively, you can run the test suite directly within this directory:
23
24 python test.py
25
26 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:
32
33 python DropTest.py
34
35 Alternatively, the [SQL script][5] can be run directly via SQL\*Plus, which
36 will always prompt for the names of the schemas that will be dropped. Run
37 the script using the following command:
38
39 sqlplus sys/syspassword@hostname/servicename @sql/DropTest.sql
40
41 [1]: https://github.com/oracle/python-cx_Oracle/blob/master/test/SetupTest.py
42 [2]: https://github.com/oracle/python-cx_Oracle/blob/master/test/TestEnv.py
43 [3]: https://github.com/oracle/python-cx_Oracle/blob/master/test/sql/SetupTest.sql
44 [4]: https://github.com/oracle/python-cx_Oracle/blob/master/test/DropTest.py
45 [5]: https://github.com/oracle/python-cx_Oracle/blob/master/test/sql/DropTest.sql
46
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 #------------------------------------------------------------------------------
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
0 #------------------------------------------------------------------------------
1 # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2 #------------------------------------------------------------------------------
3
4 #------------------------------------------------------------------------------
5 # SetupTest.py
6 #
7 # Creates users and populates their schemas with the tables and packages
8 # necessary for the cx_Oracle test suite.
9 #------------------------------------------------------------------------------
10
11 from __future__ import print_function
12
13 import cx_Oracle
14
15 import TestEnv
16 import DropTest
17
18 # connect as SYSDBA
19 conn = cx_Oracle.connect(TestEnv.GetSysdbaConnectString(),
20 mode = cx_Oracle.SYSDBA)
21
22 # drop existing users and editions, if applicable
23 DropTest.DropTests(conn)
24
25 # create test schemas
26 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())
32 print("Done.")
33
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 #------------------------------------------------------------------------------
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 # -*- 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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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 #------------------------------------------------------------------------------
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
0 /*-----------------------------------------------------------------------------
1 * Copyright 2019, Oracle and/or its affiliates. All rights reserved.
2 *---------------------------------------------------------------------------*/
3
4 /*-----------------------------------------------------------------------------
5 * DropTest.sql
6 * Drops database objects used for cx_Oracle tests.
7 *
8 * Run this like:
9 * sqlplus sys/syspassword@hostname/servicename as sysdba @DropTest
10 *---------------------------------------------------------------------------*/
11
12 whenever sqlerror exit failure
13
14 -- get parameters
15 set echo off termout on feedback off verify off
16 accept main_user char default pythontest -
17 prompt "Name of main schema [pythontest]: "
18 accept proxy_user char default pythontestproxy -
19 prompt "Name of proxy schema [pythontestproxy]: "
20 set feedback on
21
22 -- perform work
23 @@DropTestExec.sql
24
25 exit
26
0 /*-----------------------------------------------------------------------------
1 * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 *---------------------------------------------------------------------------*/
3
4 /*-----------------------------------------------------------------------------
5 * DropTestExec.sql
6 * This script performs the actual work of dropping the database schemas used
7 * by the cx_Oracle test suite. It is called by the DropTest.sql and
8 * SetupTest.sql scripts after acquiring the necessary parameters and also by
9 * the Python script DropTest.py.
10 *---------------------------------------------------------------------------*/
11
12 begin
13
14 for r in
15 ( select username
16 from dba_users
17 where username in (upper('&main_user'), upper('&proxy_user'))
18 ) loop
19 execute immediate 'drop user ' || r.username || ' cascade';
20 end loop;
21
22 end;
23 /
24
0 /*-----------------------------------------------------------------------------
1 * Copyright 2019, Oracle and/or its affiliates. All rights reserved.
2 *---------------------------------------------------------------------------*/
3
4 /*-----------------------------------------------------------------------------
5 * SetupTest.sql
6 * Creates and populates schemas with the database objects used by the
7 * cx_Oracle test suite.
8 *
9 * Run this like:
10 * sqlplus sys/syspassword@hostname/servicename as sysdba @SetupTest
11 *---------------------------------------------------------------------------*/
12
13 whenever sqlerror exit failure
14
15 -- get parameters
16 set echo off termout on feedback off verify off
17 accept main_user char default pythontest -
18 prompt "Name of main schema [pythontest]: "
19 accept main_password char prompt "Password for &main_user: " HIDE
20 accept proxy_user char default pythontestproxy -
21 prompt "Name of edition schema [pythontestproxy]: "
22 accept proxy_password char prompt "Password for &proxy_user: " HIDE
23 set feedback on
24
25 -- perform work
26 @@DropTestExec.sql
27 @@SetupTestExec.sql
28
29 exit
30
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 * SetupTestExec.sql
11 * This script performs the actual work of creating and populating the
12 * schemas with the database objects used by the cx_Oracle test suite. It is
13 * called by the SetupTest.sql file after acquiring the necessary parameters
14 * and also by the Python script SetupTest.py.
15 *---------------------------------------------------------------------------*/
16
17 alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'
18 /
19 alter session set nls_numeric_characters='.,'
20 /
21
22 create user &main_user identified by &main_password
23 quota unlimited on users
24 default tablespace users
25 /
26
27 create user &proxy_user identified by &proxy_password
28 /
29 alter user &proxy_user grant connect through &main_user
30 /
31
32 grant create session to &proxy_user
33 /
34
35 grant
36 create session,
37 create table,
38 create procedure,
39 create type,
40 select any dictionary,
41 change notification
42 to &main_user
43 /
44
45 grant execute on dbms_aqadm to &main_user
46 /
47
48 grant execute on dbms_transform to &main_user
49 /
50
51 begin
52
53 for r in
54 ( select role
55 from dba_roles
56 where role in ('SODA_APP')
57 ) loop
58 execute immediate 'grant ' || r.role || ' to &main_user';
59 end loop;
60
61 end;
62 /
63
64 -- create types
65 create type &main_user..udt_SubObject as object (
66 SubNumberValue number,
67 SubStringValue varchar2(60)
68 );
69 /
70
71 create type &main_user..udt_ObjectArray as
72 varray(10) of &main_user..udt_SubObject;
73 /
74
75 create type &main_user..udt_Object as object (
76 NumberValue number,
77 StringValue varchar2(60),
78 FixedCharValue char(10),
79 NStringValue nvarchar2(60),
80 NFixedCharValue nchar(10),
81 RawValue raw(16),
82 IntValue integer,
83 SmallIntValue smallint,
84 RealValue real,
85 DoublePrecisionValue double precision,
86 FloatValue float,
87 BinaryFloatValue binary_float,
88 BinaryDoubleValue binary_double,
89 DateValue date,
90 TimestampValue timestamp,
91 TimestampTZValue timestamp with time zone,
92 TimestampLTZValue timestamp with local time zone,
93 CLOBValue clob,
94 NCLOBValue nclob,
95 BLOBValue blob,
96 SubObjectValue &main_user..udt_SubObject,
97 SubObjectArray &main_user..udt_ObjectArray
98 );
99 /
100
101 create type &main_user..udt_Array as varray(10) of number;
102 /
103
104 create or replace type &main_user..udt_Building as object (
105 BuildingId number(9),
106 NumFloors number(3),
107 Description varchar2(60),
108 DateBuilt date
109 );
110 /
111
112 create or replace type &main_user..udt_Book as object (
113 Title varchar2(100),
114 Authors varchar2(100),
115 Price number(5,2)
116 );
117 /
118
119 -- create tables
120 create table &main_user..TestNumbers (
121 IntCol number(9) not null,
122 LongIntCol number(16) not null,
123 NumberCol number(9, 2) not null,
124 FloatCol float not null,
125 UnconstrainedCol number not null,
126 NullableCol number(38)
127 )
128 /
129
130 create table &main_user..TestStrings (
131 IntCol number(9) not null,
132 StringCol varchar2(20) not null,
133 RawCol raw(30) not null,
134 FixedCharCol char(40) not null,
135 NullableCol varchar2(50)
136 )
137 /
138
139 create table &main_user..TestUnicodes (
140 IntCol number(9) not null,
141 UnicodeCol nvarchar2(20) not null,
142 FixedUnicodeCol nchar(40) not null,
143 NullableCol nvarchar2(50)
144 )
145 /
146
147 create table &main_user..TestDates (
148 IntCol number(9) not null,
149 DateCol date not null,
150 NullableCol date
151 )
152 /
153
154 create table &main_user..TestCLOBs (
155 IntCol number(9) not null,
156 CLOBCol clob not null
157 )
158 /
159
160 create table &main_user..TestNCLOBs (
161 IntCol number(9) not null,
162 NCLOBCol nclob not null
163 )
164 /
165
166 create table &main_user..TestBLOBs (
167 IntCol number(9) not null,
168 BLOBCol blob not null
169 )
170 /
171
172 create table &main_user..TestXML (
173 IntCol number(9) not null,
174 XMLCol xmltype not null
175 )
176 /
177
178 create table &main_user..TestLongs (
179 IntCol number(9) not null,
180 LongCol long not null
181 )
182 /
183
184 create table &main_user..TestLongRaws (
185 IntCol number(9) not null,
186 LongRawCol long raw not null
187 )
188 /
189
190 create table &main_user..TestTempTable (
191 IntCol number(9) not null,
192 StringCol varchar2(400),
193 NumberCol number(25,2),
194 constraint TestTempTable_pk primary key (IntCol)
195 )
196 /
197
198 create table &main_user..TestArrayDML (
199 IntCol number(9) not null,
200 StringCol varchar2(100),
201 IntCol2 number(3),
202 constraint TestArrayDML_pk primary key (IntCol)
203 )
204 /
205
206 create table &main_user..TestObjects (
207 IntCol number(9) not null,
208 ObjectCol &main_user..udt_Object,
209 ArrayCol &main_user..udt_Array
210 )
211 /
212
213 create table &main_user..TestTimestamps (
214 IntCol number(9) not null,
215 TimestampCol timestamp not null,
216 NullableCol timestamp
217 )
218 /
219
220 create table &main_user..TestIntervals (
221 IntCol number(9) not null,
222 IntervalCol interval day to second not null,
223 NullableCol interval day to second
224 )
225 /
226
227 create table &main_user..TestUniversalRowids (
228 IntCol number(9) not null,
229 StringCol varchar2(250) not null,
230 DateCol date not null,
231 constraint TestUniversalRowids_pk primary key (IntCol, StringCol, DateCol)
232 ) organization index
233 /
234
235 create table &main_user..TestBuildings (
236 BuildingId number(9) not null,
237 BuildingObj &main_user..udt_Building not null
238 )
239 /
240
241 create table &main_user..TestRowids (
242 IntCol number(9) not null,
243 RowidCol rowid,
244 URowidCol urowid
245 )
246 /
247
248 -- create queue table and queues for testing advanced queuing
249 begin
250 dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE',
251 '&main_user..UDT_BOOK');
252 dbms_aqadm.create_queue('&main_user..BOOKS', '&main_user..BOOK_QUEUE');
253 dbms_aqadm.start_queue('&main_user..BOOKS');
254 end;
255 /
256
257 -- create transformations
258 begin
259 dbms_transform.create_transformation('&main_user', 'transform1',
260 '&main_user', 'UDT_BOOK', '&main_user', 'UDT_BOOK',
261 '&main_user..UDT_BOOK(source.user_data.TITLE, ' ||
262 'source.user_data.AUTHORS, source.user_data.PRICE + 5)');
263 dbms_transform.create_transformation('&main_user', 'transform2',
264 '&main_user', 'UDT_BOOK', '&main_user', 'UDT_BOOK',
265 '&main_user..UDT_BOOK(source.user_data.TITLE, ' ||
266 'source.user_data.AUTHORS, source.user_data.PRICE + 10)');
267 end;
268 /
269
270 -- populate tables
271 begin
272 for i in 1..10 loop
273 insert into &main_user..TestNumbers
274 values (i, power(38, i), i + i * 0.25, i + i * .75, i * i * i + i *.5,
275 decode(mod(i, 2), 0, null, power(143, i)));
276 end loop;
277 end;
278 /
279
280 declare
281
282 t_RawValue raw(30);
283
284 function ConvertHexDigit(a_Value number) return varchar2 is
285 begin
286 if a_Value between 0 and 9 then
287 return to_char(a_Value);
288 end if;
289 return chr(ascii('A') + a_Value - 10);
290 end;
291
292 function ConvertToHex(a_Value varchar2) return varchar2 is
293 t_HexValue varchar2(60);
294 t_Digit number;
295 begin
296 for i in 1..length(a_Value) loop
297 t_Digit := ascii(substr(a_Value, i, 1));
298 t_HexValue := t_HexValue ||
299 ConvertHexDigit(trunc(t_Digit / 16)) ||
300 ConvertHexDigit(mod(t_Digit, 16));
301 end loop;
302 return t_HexValue;
303 end;
304
305 begin
306 for i in 1..10 loop
307 t_RawValue := hextoraw(ConvertToHex('Raw ' || to_char(i)));
308 insert into &main_user..TestStrings
309 values (i, 'String ' || to_char(i), t_RawValue,
310 'Fixed Char ' || to_char(i),
311 decode(mod(i, 2), 0, null, 'Nullable ' || to_char(i)));
312 end loop;
313 end;
314 /
315
316 begin
317 for i in 1..10 loop
318 insert into &main_user..TestUnicodes
319 values (i, 'Unicode ' || unistr('\3042') || ' ' || to_char(i),
320 'Fixed Unicode ' || to_char(i),
321 decode(mod(i, 2), 0, null, unistr('Nullable ') || to_char(i)));
322 end loop;
323 end;
324 /
325
326 begin
327 for i in 1..10 loop
328 insert into &main_user..TestDates
329 values (i, to_date(20021209, 'YYYYMMDD') + i + i * .1,
330 decode(mod(i, 2), 0, null,
331 to_date(20021209, 'YYYYMMDD') + i + i + i * .15));
332 end loop;
333 end;
334 /
335
336 begin
337 for i in 1..100 loop
338 insert into &main_user..TestXML
339 values (i, '<?xml version="1.0"?><records>' ||
340 dbms_random.string('x', 1024) || '</records>');
341 end loop;
342 end;
343 /
344
345 begin
346 for i in 1..10 loop
347 insert into &main_user..TestTimestamps
348 values (i, to_timestamp('20021209', 'YYYYMMDD') +
349 to_dsinterval(to_char(i) || ' 00:00:' || to_char(i * 2) ||
350 '.' || to_char(i * 50)),
351 decode(mod(i, 2), 0, to_timestamp(null, 'YYYYMMDD'),
352 to_timestamp('20021209', 'YYYYMMDD') +
353 to_dsinterval(to_char(i + 1) || ' 00:00:' ||
354 to_char(i * 3) || '.' || to_char(i * 125))));
355 end loop;
356 end;
357 /
358
359 begin
360 for i in 1..10 loop
361 insert into &main_user..TestIntervals
362 values (i, to_dsinterval(to_char(i) || ' ' || to_char(i) || ':' ||
363 to_char(i * 2) || ':' || to_char(i * 3)),
364 decode(mod(i, 2), 0, to_dsinterval(null),
365 to_dsinterval(to_char(i + 5) || ' ' || to_char(i + 2) || ':' ||
366 to_char(i * 2 + 5) || ':' || to_char(i * 3 + 5))));
367 end loop;
368 end;
369 /
370
371 insert into &main_user..TestObjects values (1,
372 &main_user..udt_Object(1, 'First row', 'First', 'N First Row', 'N First',
373 '52617720446174612031', 2, 5, 12.125, 0.5, 12.5, 25.25, 50.125,
374 to_date(20070306, 'YYYYMMDD'),
375 to_timestamp('20080912 16:40:00', 'YYYYMMDD HH24:MI:SS'),
376 to_timestamp_tz('20091013 17:50:00 00:00',
377 'YYYYMMDD HH24:MI:SS TZH:TZM'),
378 to_timestamp_tz('20101114 18:55:00 00:00',
379 'YYYYMMDD HH24:MI:SS TZH:TZM'),
380 'Short CLOB value', 'Short NCLOB Value',
381 utl_raw.cast_to_raw('Short BLOB value'),
382 &main_user..udt_SubObject(11, 'Sub object 1'),
383 &main_user..udt_ObjectArray(
384 &main_user..udt_SubObject(5, 'first element'),
385 &main_user..udt_SubObject(6, 'second element'))),
386 &main_user..udt_Array(5, 10, null, 20))
387 /
388
389 insert into &main_user..TestObjects values (2, null,
390 &main_user..udt_Array(3, null, 9, 12, 15))
391 /
392
393 insert into &main_user..TestObjects values (3,
394 &main_user..udt_Object(3, 'Third row', 'Third', 'N Third Row', 'N Third',
395 '52617720446174612033', 4, 10, 6.5, 0.75, 43.25, 86.5, 192.125,
396 to_date(20070621, 'YYYYMMDD'),
397 to_timestamp('20071213 07:30:45', 'YYYYMMDD HH24:MI:SS'),
398 to_timestamp_tz('20170621 23:18:45 00:00',
399 'YYYYMMDD HH24:MI:SS TZH:TZM'),
400 to_timestamp_tz('20170721 08:27:13 00:00',
401 'YYYYMMDD HH24:MI:SS TZH:TZM'),
402 'Another short CLOB value', 'Another short NCLOB Value',
403 utl_raw.cast_to_raw('Yet another short BLOB value'),
404 &main_user..udt_SubObject(13, 'Sub object 3'),
405 &main_user..udt_ObjectArray(
406 &main_user..udt_SubObject(10, 'element #1'),
407 &main_user..udt_SubObject(20, 'element #2'),
408 &main_user..udt_SubObject(30, 'element #3'),
409 &main_user..udt_SubObject(40, 'element #4'))), null)
410 /
411
412 commit
413 /
414
415 -- create procedures for testing callproc()
416 create procedure &main_user..proc_Test (
417 a_InValue varchar2,
418 a_InOutValue in out number,
419 a_OutValue out number
420 ) as
421 begin
422 a_InOutValue := a_InOutValue * length(a_InValue);
423 a_OutValue := length(a_InValue);
424 end;
425 /
426
427 create procedure &main_user..proc_TestNoArgs as
428 begin
429 null;
430 end;
431 /
432
433 -- create functions for testing callfunc()
434 create function &main_user..func_Test (
435 a_String varchar2,
436 a_ExtraAmount number
437 ) return number as
438 begin
439 return length(a_String) + a_ExtraAmount;
440 end;
441 /
442
443 create function &main_user..func_TestNoArgs
444 return number as
445 begin
446 return 712;
447 end;
448 /
449
450 -- create packages
451 create or replace package &main_user..pkg_TestStringArrays as
452
453 type udt_StringList is table of varchar2(100) index by binary_integer;
454
455 function TestInArrays (
456 a_StartingLength number,
457 a_Array udt_StringList
458 ) return number;
459
460 procedure TestInOutArrays (
461 a_NumElems number,
462 a_Array in out nocopy udt_StringList
463 );
464
465 procedure TestOutArrays (
466 a_NumElems number,
467 a_Array out nocopy udt_StringList
468 );
469
470 procedure TestIndexBy (
471 a_Array out nocopy udt_StringList
472 );
473
474 end;
475 /
476
477 create or replace package body &main_user..pkg_TestStringArrays as
478
479 function TestInArrays (
480 a_StartingLength number,
481 a_Array udt_StringList
482 ) return number is
483 t_Length number;
484 begin
485 t_Length := a_StartingLength;
486 for i in 1..a_Array.count loop
487 t_Length := t_Length + length(a_Array(i));
488 end loop;
489 return t_Length;
490 end;
491
492 procedure TestInOutArrays (
493 a_NumElems number,
494 a_Array in out udt_StringList
495 ) is
496 begin
497 for i in 1..a_NumElems loop
498 a_Array(i) := 'Converted element # ' ||
499 to_char(i) || ' originally had length ' ||
500 to_char(length(a_Array(i)));
501 end loop;
502 end;
503
504 procedure TestOutArrays (
505 a_NumElems number,
506 a_Array out udt_StringList
507 ) is
508 begin
509 for i in 1..a_NumElems loop
510 a_Array(i) := 'Test out element # ' || to_char(i);
511 end loop;
512 end;
513
514 procedure TestIndexBy (
515 a_Array out nocopy udt_StringList
516 ) is
517 begin
518 a_Array(-1048576) := 'First element';
519 a_Array(-576) := 'Second element';
520 a_Array(284) := 'Third element';
521 a_Array(8388608) := 'Fourth element';
522 end;
523
524 end;
525 /
526
527 create or replace package &main_user..pkg_TestUnicodeArrays as
528
529 type udt_UnicodeList is table of nvarchar2(100) index by binary_integer;
530
531 function TestInArrays (
532 a_StartingLength number,
533 a_Array udt_UnicodeList
534 ) return number;
535
536 procedure TestInOutArrays (
537 a_NumElems number,
538 a_Array in out nocopy udt_UnicodeList
539 );
540
541 procedure TestOutArrays (
542 a_NumElems number,
543 a_Array out nocopy udt_UnicodeList
544 );
545
546 end;
547 /
548
549 create or replace package body &main_user..pkg_TestUnicodeArrays as
550
551 function TestInArrays (
552 a_StartingLength number,
553 a_Array udt_UnicodeList
554 ) return number is
555 t_Length number;
556 begin
557 t_Length := a_StartingLength;
558 for i in 1..a_Array.count loop
559 t_Length := t_Length + length(a_Array(i));
560 end loop;
561 return t_Length;
562 end;
563
564 procedure TestInOutArrays (
565 a_NumElems number,
566 a_Array in out udt_UnicodeList
567 ) is
568 begin
569 for i in 1..a_NumElems loop
570 a_Array(i) := unistr('Converted element ' || unistr('\3042') ||
571 ' # ') || to_char(i) || ' originally had length ' ||
572 to_char(length(a_Array(i)));
573 end loop;
574 end;
575
576 procedure TestOutArrays (
577 a_NumElems number,
578 a_Array out udt_UnicodeList
579 ) is
580 begin
581 for i in 1..a_NumElems loop
582 a_Array(i) := unistr('Test out element ') || unistr('\3042') ||
583 ' # ' || to_char(i);
584 end loop;
585 end;
586
587 end;
588 /
589
590 create or replace package &main_user..pkg_TestNumberArrays as
591
592 type udt_NumberList is table of number index by binary_integer;
593
594 function TestInArrays (
595 a_StartingValue number,
596 a_Array udt_NumberList
597 ) return number;
598
599 procedure TestInOutArrays (
600 a_NumElems number,
601 a_Array in out nocopy udt_NumberList
602 );
603
604 procedure TestOutArrays (
605 a_NumElems number,
606 a_Array out nocopy udt_NumberList
607 );
608
609 end;
610 /
611
612 create or replace package body &main_user..pkg_TestNumberArrays as
613
614 function TestInArrays (
615 a_StartingValue number,
616 a_Array udt_NumberList
617 ) return number is
618 t_Value number;
619 begin
620 t_Value := a_StartingValue;
621 for i in 1..a_Array.count loop
622 t_Value := t_Value + a_Array(i);
623 end loop;
624 return t_Value;
625 end;
626
627 procedure TestInOutArrays (
628 a_NumElems number,
629 a_Array in out udt_NumberList
630 ) is
631 begin
632 for i in 1..a_NumElems loop
633 a_Array(i) := a_Array(i) * 10;
634 end loop;
635 end;
636
637 procedure TestOutArrays (
638 a_NumElems number,
639 a_Array out udt_NumberList
640 ) is
641 begin
642 for i in 1..a_NumElems loop
643 a_Array(i) := i * 100;
644 end loop;
645 end;
646
647 end;
648 /
649
650 create or replace package &main_user..pkg_TestDateArrays as
651
652 type udt_DateList is table of date index by binary_integer;
653
654 function TestInArrays (
655 a_StartingValue number,
656 a_BaseDate date,
657 a_Array udt_DateList
658 ) return number;
659
660 procedure TestInOutArrays (
661 a_NumElems number,
662 a_Array in out nocopy udt_DateList
663 );
664
665 procedure TestOutArrays (
666 a_NumElems number,
667 a_Array out nocopy udt_DateList
668 );
669
670 end;
671 /
672
673 create or replace package body &main_user..pkg_TestDateArrays as
674
675 function TestInArrays (
676 a_StartingValue number,
677 a_BaseDate date,
678 a_Array udt_DateList
679 ) return number is
680 t_Value number;
681 begin
682 t_Value := a_StartingValue;
683 for i in 1..a_Array.count loop
684 t_Value := t_Value + a_Array(i) - a_BaseDate;
685 end loop;
686 return t_Value;
687 end;
688
689 procedure TestInOutArrays (
690 a_NumElems number,
691 a_Array in out udt_DateList
692 ) is
693 begin
694 for i in 1..a_NumElems loop
695 a_Array(i) := a_Array(i) + 7;
696 end loop;
697 end;
698
699 procedure TestOutArrays (
700 a_NumElems number,
701 a_Array out udt_DateList
702 ) is
703 begin
704 for i in 1..a_NumElems loop
705 a_Array(i) := to_date(20021212, 'YYYYMMDD') + i * 1.2;
706 end loop;
707 end;
708
709 end;
710 /
711
712 create or replace package &main_user..pkg_TestRefCursors as
713
714 procedure TestOutCursor (
715 a_MaxIntValue number,
716 a_Cursor out sys_refcursor
717 );
718
719 function TestInCursor (
720 a_Cursor sys_refcursor
721 ) return varchar2;
722
723 end;
724 /
725
726 create or replace package body &main_user..pkg_TestRefCursors as
727
728 procedure TestOutCursor (
729 a_MaxIntValue number,
730 a_Cursor out sys_refcursor
731 ) is
732 begin
733 open a_Cursor for
734 select
735 IntCol,
736 StringCol
737 from TestStrings
738 where IntCol <= a_MaxIntValue
739 order by IntCol;
740 end;
741
742 function TestInCursor (
743 a_Cursor sys_refcursor
744 ) return varchar2 is
745 t_String varchar2(100);
746 begin
747 fetch a_Cursor into t_String;
748 return t_String || ' (Modified)';
749 end;
750
751 end;
752 /
753
754 create or replace package &main_user..pkg_TestBooleans as
755
756 type udt_BooleanList is table of boolean index by binary_integer;
757
758 function GetStringRep (
759 a_Value boolean
760 ) return varchar2;
761
762 function IsLessThan10 (
763 a_Value number
764 ) return boolean;
765
766 function TestInArrays (
767 a_Value udt_BooleanList
768 ) return number;
769
770 procedure TestOutArrays (
771 a_NumElements number,
772 a_Value out nocopy udt_BooleanList
773 );
774
775 end;
776 /
777
778 create or replace package body &main_user..pkg_TestBooleans as
779
780 function GetStringRep (
781 a_Value boolean
782 ) return varchar2 is
783 begin
784 if a_Value is null then
785 return 'NULL';
786 elsif a_Value then
787 return 'TRUE';
788 end if;
789 return 'FALSE';
790 end;
791
792 function IsLessThan10 (
793 a_Value number
794 ) return boolean is
795 begin
796 return a_Value < 10;
797 end;
798
799 function TestInArrays (
800 a_Value udt_BooleanList
801 ) return number is
802 t_Result pls_integer;
803 begin
804 t_Result := 0;
805 for i in 1..a_Value.count loop
806 if a_Value(i) then
807 t_Result := t_Result + 1;
808 end if;
809 end loop;
810 return t_Result;
811 end;
812
813 procedure TestOutArrays (
814 a_NumElements number,
815 a_Value out nocopy udt_BooleanList
816 ) is
817 begin
818 for i in 1..a_NumElements loop
819 a_Value(i) := (mod(i, 2) = 1);
820 end loop;
821 end;
822
823 end;
824 /
825
826 create or replace package &main_user..pkg_TestBindObject as
827
828 function GetStringRep (
829 a_Object udt_Object
830 ) return varchar2;
831
832 procedure BindObjectOut (
833 a_NumberValue number,
834 a_StringValue varchar2,
835 a_Object out nocopy udt_Object
836 );
837
838 end;
839 /
840
841 create or replace package body &main_user..pkg_TestBindObject as
842
843 function GetStringRep (
844 a_Object udt_SubObject
845 ) return varchar2 is
846 begin
847 if a_Object is null then
848 return 'null';
849 end if;
850 return 'udt_SubObject(' ||
851 nvl(to_char(a_Object.SubNumberValue), 'null') || ', ' ||
852 case when a_Object.SubStringValue is null then 'null'
853 else '''' || a_Object.SubStringValue || '''' end || ')';
854 end;
855
856 function GetStringRep (
857 a_Array udt_ObjectArray
858 ) return varchar2 is
859 t_StringRep varchar2(4000);
860 begin
861 if a_Array is null then
862 return 'null';
863 end if;
864 t_StringRep := 'udt_ObjectArray(';
865 for i in 1..a_Array.count loop
866 if i > 1 then
867 t_StringRep := t_StringRep || ', ';
868 end if;
869 t_StringRep := t_StringRep || GetStringRep(a_Array(i));
870 end loop;
871 return t_StringRep || ')';
872 end;
873
874 function GetStringRep (
875 a_Object udt_Object
876 ) return varchar2 is
877 begin
878 if a_Object is null then
879 return 'null';
880 end if;
881 return 'udt_Object(' ||
882 nvl(to_char(a_Object.NumberValue), 'null') || ', ' ||
883 case when a_Object.StringValue is null then 'null'
884 else '''' || a_Object.StringValue || '''' end || ', ' ||
885 case when a_Object.FixedCharValue is null then 'null'
886 else '''' || a_Object.FixedCharValue || '''' end || ', ' ||
887 case when a_Object.DateValue is null then 'null'
888 else 'to_date(''' ||
889 to_char(a_Object.DateValue, 'YYYY-MM-DD') ||
890 ''', ''YYYY-MM-DD'')' end || ', ' ||
891 case when a_Object.TimestampValue is null then 'null'
892 else 'to_timestamp(''' || to_char(a_Object.TimestampValue,
893 'YYYY-MM-DD HH24:MI:SS') ||
894 ''', ''YYYY-MM-DD HH24:MI:SS'')' end || ', ' ||
895 GetStringRep(a_Object.SubObjectValue) || ', ' ||
896 GetStringRep(a_Object.SubObjectArray) || ')';
897 end;
898
899 procedure BindObjectOut (
900 a_NumberValue number,
901 a_StringValue varchar2,
902 a_Object out nocopy udt_Object
903 ) is
904 begin
905 a_Object := udt_Object(a_NumberValue, a_StringValue, null, null, null,
906 null, null, null, null, null, null, null, null, null, null,
907 null, null, null, null, null, null, null);
908 end;
909
910 end;
911 /
912
913 create or replace package &main_user..pkg_TestRecords as
914
915 type udt_Record is record (
916 NumberValue number,
917 StringValue varchar2(30),
918 DateValue date,
919 TimestampValue timestamp,
920 BooleanValue boolean
921 );
922
923 type udt_RecordArray is table of udt_Record index by binary_integer;
924
925 function GetStringRep (
926 a_Value udt_Record
927 ) return varchar2;
928
929 procedure TestOut (
930 a_Value out nocopy udt_Record
931 );
932
933 function TestInArrays (
934 a_Value udt_RecordArray
935 ) return varchar2;
936
937 end;
938 /
939
940 create or replace package body &main_user..pkg_TestRecords as
941
942 function GetStringRep (
943 a_Value udt_Record
944 ) return varchar2 is
945 begin
946 return 'udt_Record(' ||
947 nvl(to_char(a_Value.NumberValue), 'null') || ', ' ||
948 case when a_Value.StringValue is null then 'null'
949 else '''' || a_Value.StringValue || '''' end || ', ' ||
950 case when a_Value.DateValue is null then 'null'
951 else 'to_date(''' ||
952 to_char(a_Value.DateValue, 'YYYY-MM-DD') ||
953 ''', ''YYYY-MM-DD'')' end || ', ' ||
954 case when a_Value.TimestampValue is null then 'null'
955 else 'to_timestamp(''' || to_char(a_Value.TimestampValue,
956 'YYYY-MM-DD HH24:MI:SS') ||
957 ''', ''YYYY-MM-DD HH24:MI:SS'')' end || ', ' ||
958 case when a_Value.BooleanValue is null then 'null'
959 when a_Value.BooleanValue then 'true'
960 else 'false' end || ')';
961 end;
962
963 procedure TestOut (
964 a_Value out nocopy udt_Record
965 ) is
966 begin
967 a_Value.NumberValue := 25;
968 a_Value.StringValue := 'String in record';
969 a_Value.DateValue := to_date(20160216, 'YYYYMMDD');
970 a_Value.TimestampValue := to_timestamp('20160216 18:23:55',
971 'YYYYMMDD HH24:MI:SS');
972 a_Value.BooleanValue := true;
973 end;
974
975 function TestInArrays (
976 a_Value udt_RecordArray
977 ) return varchar2 is
978 t_Result varchar2(4000);
979 begin
980 for i in 0..a_Value.count - 1 loop
981 if t_Result is not null then
982 t_Result := t_Result || '; ';
983 end if;
984 t_Result := t_Result || GetStringRep(a_Value(i));
985 end loop;
986 return t_Result;
987 end;
988
989 end;
990 /
991
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) 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