Samples and documentation improvements.

This commit is contained in:
Anthony Tuininga 2023-12-19 11:56:04 -07:00
parent 541fad5d74
commit ef65a1f681
46 changed files with 4616 additions and 347 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ build/
dist/
doc/build
src/oracledb/*.c
.ipynb_checkpoints/

View File

@ -8,6 +8,8 @@ The module conforms to the [Python Database API 2.0 specification][pep249] with
a considerable number of additions and a couple of minor exclusions, see the
[feature list][features].
Synchronous and [concurrent][concurrent] coding styles are supported.
## Installation
Run `python -m pip install oracledb`
@ -95,3 +97,4 @@ See [LICENSE][license], [THIRD_PARTY_LICENSES][tplicense], and
[samples]: https://github.com/oracle/python-oracledb/tree/main/samples
[installation]: https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html
[features]: https://oracle.github.io/python-oracledb/#features
[concurrent]: https://python-oracledb.readthedocs.io/en/latest/user_guide/asyncio.html

View File

@ -0,0 +1,334 @@
.. _asyncconnobj:
****************************
API: AsyncConnection Objects
****************************
An AsyncConnection object can be created with :meth:`oracledb.connect_async()`
or with :meth:`AsyncConnectionPool.acquire()`. AsyncConnections support use of
concurrent programming with `asyncio <https://docs.python.org/3/library/
asyncio.html>`__. Unless explicitly noted as synchronous, the AsyncConnection
methods should be used with ``await``. This object is an extension to the DB
API.
.. versionadded:: 2.0.0
.. note::
The Asynchronous I/O (asyncio) support in python-oracledb 2.0.0 is a
pre-release and may change in the next version.
.. note::
AsyncConnection objects are only supported in the python-oracledb Thin
mode.
.. note::
Any outstanding database transaction will be rolled back when the
connection object is destroyed or closed. You must perform a
:meth:`commit <AsyncConnection.commit>` first if you want data to
persist in the database, see :ref:`txnasync`.
.. _asyncconnmeth:
AsyncConnection Methods
=======================
.. method:: AsyncConnection.__aenter__()
The entry point for the asynchronous connection as a context manager. It
returns itself.
.. method:: AsyncConnection.__aexit__()
The exit point for the asynchronous connection as a context manager. This
will close the connection and roll back any uncommitted transaction.
.. method:: AsyncConnection.callfunc(name, return_type, parameters=[], \
keyword_parameters={})
Calls a PL/SQL function with the given name.
This is a shortcut for creating a cursor, calling the stored function with
the cursor, and then closing the cursor.
.. method:: AsyncConnection.callproc(name, parameters=[], \
keyword_parameters={})
Calls a PL/SQL procedure with the given name.
This is a shortcut for creating a cursor, calling the stored procedure
with the cursor, and then closing the cursor.
.. method:: AsyncConnection.cancel()
A synchronous method that breaks a long-running statement.
.. method:: AsyncConnection.changepassword(old_password, new_password)
Changes the password for the user to which the connection is connected.
.. method:: AsyncConnection.close()
Closes the connection.
.. method:: AsyncConnection.commit()
Commits any pending transaction to the database.
.. method:: AsyncConnection.createlob(lob_type)
Creates and returns a new temporary LOB of the specified type.
.. method:: AsyncConnection.cursor(scrollable=False)
A synchronous method that returns a cursor associated with the connection.
.. method:: AsyncConnection.execute(statement, parameters=[])
Executes a statement against the database.
This is a shortcut for creating a cursor, executing a statement with the
cursor, and then closing the cursor.
.. method:: AsyncConnection.executemany(statement, parameters=[])
Prepares a statement for execution against a database and then executes it
against all parameter mappings or sequences found in the sequence
parameters.
This is a shortcut for creating a cursor, calling
:meth:`AsyncCursor.executemany()` on the cursor, and then closing the
cursor.
.. method:: AsyncConnection.fetchall(statement, parameters=None, \
arraysize=None, rowfactory=None)
Executes a query and returns all of the rows. After the rows are
fetched, the cursor is closed.
.. method:: AsyncConnection.fetchmany(statement, parameters=None, \
num_rows=None, rowfactory=None)
Executes a query and returns up to the specified number of rows. After the
rows are fetched, the cursor is closed.
.. method:: AsyncConnection.fetchone(statement, parameters=None, \
rowfactory=None)
Executes a query and returns the first row of the result set if one exists
(or None if no rows exist). After the row is fetched, the cursor is
closed.
.. method:: AsyncConnection.gettype(name)
Returns a :ref:`type object <dbobjecttype>` given its name. This can then
be used to create objects which can be bound to cursors created by this
connection.
.. method:: AsyncConnection.is_healthy()
A synchronous method that returns a boolean indicating the health status
of a connection.
Connections may become unusable in several cases, such as, if the network
socket is broken, if an Oracle error indicates the connection is unusable,
or, after receiving a planned down notification from the database.
This function is best used before starting a new database request on an
existing standalone connection. Pooled connections internally perform this
check before returning a connection to the application.
If this function returns False, the connection should be not be used by the
application and a new connection should be established instead.
This function performs a local check. To fully check a connection's health,
use :meth:`AsyncConnection.ping()` which performs a round-trip to the
database.
.. method:: AsyncConnection.ping()
Pings the database to verify if the connection is valid.
.. method:: AsyncConnection.rollback()
Rolls back any pending transaction.
.. _asynconnattr:
AsyncConnection Attributes
==========================
.. attribute:: AsyncConnection.action
This write-only attribute sets the action column in the v$session table. It
is a string attribute but the value None is accepted and treated as an
empty string.
.. attribute:: AsyncConnection.autocommit
This read-write attribute determines whether autocommit mode is on or off.
When autocommit mode is on, all statements are committed as soon as they
have completed executing.
.. attribute:: AsyncConnection.call_timeout
This read-write attribute specifies the amount of time (in milliseconds)
that a single round-trip to the database may take before a timeout will
occur. A value of 0 means that no timeout will take place.
If a timeout occurs, the error *DPI-1067* will be returned if the
connection is still usable. Alternatively the error *DPI-1080* will be
returned if the connection has become invalid and can no longer be used.
.. attribute:: AsyncConnection.client_identifier
This write-only attribute sets the client_identifier column in the
v$session table.
.. attribute:: AsyncConnection.clientinfo
This write-only attribute sets the client_info column in the v$session
table.
.. attribute:: AsyncConnection.current_schema
This read-write attribute sets the current schema attribute for the
session. Setting this value is the same as executing the SQL statement
``ALTER SESSION SET CURRENT_SCHEMA``. The attribute is set (and verified) on
the next call that does a round trip to the server. The value is placed
before unqualified database objects in SQL statements you then execute.
.. attribute:: AsyncConnection.db_domain
This read-only attribute specifies the Oracle Database domain name
associated with the connection. It is the same value returned by the SQL
``SELECT value FROM V$PARAMETER WHERE NAME = 'db_domain'``.
.. attribute:: AsyncConnection.db_name
This read-only attribute specifies the Oracle Database name associated with
the connection. It is the same value returned by the SQL
``SELECT NAME FROM V$DATABASE``.
.. attribute:: AsyncConnection.dbop
This write-only attribute sets the database operation that is to be
monitored. This can be viewed in the ``DBOP_NAME`` column of the
``v$sql_monitor`` table.
.. attribute:: AsyncConnection.dsn
This read-only attribute returns the TNS entry of the database to which a
connection has been established.
.. attribute:: AsyncConnection.econtext_id
This write-only attribute specifies the execution context id. This
value can be found as ecid in the v$session table and econtext_id in the
auditing tables. The maximum length is 64 bytes.
.. attribute:: AsyncConnection.edition
This read-only attribute gets the session edition and is only available in
Oracle Database 11.2 (the server must be at this level or higher for this
to work). This attribute is ignored in python-oracledb Thin mode.
.. attribute:: AsyncConnection.external_name
This read-write attribute specifies the external name that is used by the
connection when logging distributed transactions.
.. attribute:: AsyncConnection.inputtypehandler
This read-write attribute specifies a method called for each value that is
bound to a statement executed on any cursor associated with this
connection. The method signature is handler(cursor, value, arraysize) and
the return value is expected to be a variable object or None in which case
a default variable object will be created. If this attribute is None, the
default behavior will take place for all values bound to statements.
.. attribute:: AsyncConnection.instance_name
This read-only attribute specifies the Oracle Database instance name
associated with the connection. It is the same value as the SQL expression
``sys_context('userenv', 'instance_name')``.
.. attribute:: AsyncConnection.internal_name
This read-write attribute specifies the internal name that is used by the
connection when logging distributed transactions.
.. attribute:: AsyncConnection.ltxid
This read-only attribute returns the logical transaction id for the
connection. It is used within Oracle Transaction Guard as a means of
ensuring that transactions are not duplicated. See the Oracle documentation
and the provided sample for more information.
.. note:
This attribute is only available when Oracle Database 12.1 or later is
in use
.. attribute:: AsyncConnection.max_open_cursors
This read-only attribute specifies the maximum number of cursors that the
database can have open concurrently. It is the same value returned by the
SQL ``SELECT VALUE FROM V$PARAMETER WHERE NAME = 'open_cursors'``.
.. attribute:: AsyncConnection.module
This write-only attribute sets the module column in the v$session table.
The maximum length for this string is 48 and if you exceed this length you
will get ORA-24960.
.. attribute:: AsyncConnection.outputtypehandler
This read-write attribute specifies a method called for each column that is
going to be fetched from any cursor associated with this connection. The
method signature is ``handler(cursor, metadata)`` and the return value is
expected to be a :ref:`variable object<varobj>` or None in which case a
default variable object will be created. If this attribute is None, the
default behavior will take place for all columns fetched from cursors.
See :ref:`outputtypehandlers`.
.. attribute:: AsyncConnection.sdu
This read-only attribute specifies the size of the Session Data Unit (SDU)
that is being used by the connection. The value will be the lesser of the
requested python-oracledb size and the maximum size allowed by the database
network configuration.
.. attribute:: AsyncConnection.service_name
This read-only attribute specifies the Oracle Database service name
associated with the connection. This is the same value returned by the SQL
``SELECT SYS_CONTEXT('USERENV', 'SERVICE_NAME') FROM DUAL``.
.. attribute:: AsyncConnection.stmtcachesize
This read-write attribute specifies the size of the statement cache. This
value can make a significant difference in performance if you have a small
number of statements that you execute repeatedly.
The default value is 20.
See :ref:`Statement Caching <stmtcache>` for more information.
.. attribute:: AsyncConnection.transaction_in_progress
This read-only attribute specifies whether a transaction is currently in
progress on the database associated with the connection.
.. attribute:: AsyncConnection.username
This read-only attribute returns the name of the user which established the
connection to the database.
.. attribute:: AsyncConnection.version
This read-only attribute returns the version of the database to which a
connection has been established.

View File

@ -0,0 +1,213 @@
.. _asyncconnpoolobj:
********************************
API: AsyncConnectionPool Objects
********************************
An AsyncConnectionPool object can be created with
:meth:`oracledb.create_pool_async()`. This object is an extension to the DB
API.
.. versionadded:: 2.0.0
.. note::
The Asynchronous I/O (asyncio) support in python-oracledb 2.0.0 is a
pre-release and may change in the next version.
.. note::
AsyncConnectionPool objects are only supported in the python-oracledb Thin
mode.
.. _asynconnpoolmeth:
AsyncConnectionPool Methods
===========================
.. method:: AsyncConnectionPool.acquire(user=None, password=None, cclass=None, \
purity=oracledb.PURITY_DEFAULT, tag=None, matchanytag=False, \
shardingkey=[], supershardingkey=[])
Acquires a connection from the pool and returns an
:ref:`asynchronous connection object <asyncconnobj>`.
If the pool is :ref:`homogeneous <connpooltypes>`, the ``user`` and
``password`` parameters cannot be specified. If they are, an exception will
be raised.
The ``cclass`` parameter, if specified, should be a string corresponding to
the connection class for Database Resident Connection Pooling (DRCP).
The ``purity`` parameter is expected to be one of
:data:`~oracledb.PURITY_NEW`, :data:`~oracledb.PURITY_ANY`, or
:data:`~oracledb.PURITY_DEFAULT`.
The ``tag``, ``matchanytag``, ``shardingkey``, and ``supershardingkey``
parameters are ignored in python-oracledb Thin mode.
.. method:: AsyncConnectionPool.close(force=False)
Closes the pool now, rather than when the last reference to it is
released, which makes it unusable for further work.
If any connections have been acquired and not released back to the pool,
this method will fail unless the ``force`` parameter is set to *True*.
.. method:: AsyncConnectionPool.drop(connection)
Drops the connection from the pool which is useful if the connection is no
longer usable (such as when the session is killed).
.. method:: AsyncConnectionPool.release(connection, tag=None)
Releases the connection back to the pool now, rather than whenever
``__del__`` is called. The connection will be unusable from this point
forward; an Error exception will be raised if any operation is attempted
with the connection. Any cursors or LOBs created by the connection will
also be marked unusable and an Error exception will be raised if any
operation is attempted with them.
Internally, references to the connection are held by cursor objects,
LOB objects, and so on. Once all of these references are released, the
connection itself will be released back to the pool automatically. Either
control references to these related objects carefully or explicitly
release connections back to the pool in order to ensure sufficient
resources are available.
The ``tag`` parameter is ignored in python-oracledb Thin mode.
.. _asyncconnpoolattr:
AsyncConnectionPool Attributes
==============================
.. attribute:: AsyncConnectionPool.busy
This read-only attribute returns the number of connections currently
acquired.
.. attribute:: AsyncConnectionPool.dsn
This read-only attribute returns the TNS entry of the database to which a
connection has been established.
.. attribute:: AsyncConnectionPool.getmode
This read-write attribute determines how connections are returned from the
pool. If :data:`~oracledb.POOL_GETMODE_FORCEGET` is specified, a new
connection will be returned even if there are no free connections in the
pool. :data:`~oracledb.POOL_GETMODE_NOWAIT` will raise an exception if
there are no free connections are available in the pool. If
:data:`~oracledb.POOL_GETMODE_WAIT` is specified and there are no free
connections in the pool, the caller will wait until a free connection is
available. :data:`~oracledb.POOL_GETMODE_TIMEDWAIT` uses the value of
:data:`~ConnectionPool.wait_timeout` to determine how long the caller
should wait for a connection to become available before returning an error.
.. attribute:: AsyncConnectionPool.homogeneous
This read-only boolean attribute indicates whether the pool is considered
:ref:`homogeneous <connpooltypes>` or not. If the pool is not homogeneous,
different authentication can be used for each connection acquired from the
pool.
.. attribute:: AsyncConnectionPool.increment
This read-only attribute returns the number of connections that will be
established when additional connections need to be created.
.. attribute:: AsyncConnectionPool.max
This read-only attribute returns the maximum number of connections that the
pool can control.
.. attribute:: AsyncConnectionPool.max_lifetime_session
This read-write attribute returns the maximum length of time (in seconds)
that a pooled connection may exist. Connections that are in use will not be
closed. They become candidates for termination only when they are released
back to the pool and have existed for longer than max_lifetime_session
seconds. Note that termination only occurs when the pool is accessed. A
value of 0 means that there is no maximum length of time that a pooled
connection may exist. This attribute is only available in Oracle Database
12.1 or later.
.. attribute:: AsyncConnectionPool.max_sessions_per_shard
This read-write attribute returns the number of sessions that can be
created per shard in the pool. This attribute cannot be used in
python-oracledb Thin mode.
.. attribute:: AsyncConnectionPool.min
This read-only attribute returns the number of connections with which the
connection pool was created and the minimum number of connections that will
be controlled by the connection pool.
.. attribute:: AsyncConnectionPool.name
This read-only attribute returns the name assigned to the pool by Oracle.
.. attribute:: AsyncConnectionPool.opened
This read-only attribute returns the number of connections currently opened
by the pool.
.. attribute:: AsyncConnectionPool.ping_interval
This read-write integer attribute specifies the pool ping interval in
seconds. When a connection is acquired from the pool, a check is first made
to see how long it has been since the connection was put into the pool. If
this idle time exceeds ``ping_interval``, then a :ref:`round-trip
<roundtrips>` ping to the database is performed. If the connection is
unusable, it is discarded and a different connection is selected to be
returned by :meth:`AsyncConnectionPool.acquire()`. Setting
``ping_interval`` to a negative value disables pinging. Setting it to 0
forces a ping for every :meth:`AsyncConnectionPool.acquire()` and is not
recommended.
Prior to cx_Oracle 8.2, the ping interval was fixed at 60 seconds.
.. attribute:: AsyncConnectionPool.soda_metadata_cache
This read-write boolean attribute returns whether the SODA metadata cache
is enabled or not. This attribute cannot be used in python-oracledb Thin
mode.
.. attribute:: AsyncConnectionPool.stmtcachesize
This read-write attribute specifies the size of the statement cache that
will be used for connections obtained from the pool. Once a connection is
created, that connections statement cache size can only be changed by
setting the stmtcachesize attribute on the connection itself.
See :ref:`Statement Caching <stmtcache>` for more information.
.. attribute:: AsyncConnectionPool.thin
This attribute returns a boolean which indicates the python-oracledb mode
in which the pool was created. If the value of this attribute is True, it
indicates that the pool was created in the python-oracledb Thin mode. If
the value of this attribute is False, it indicates that the pool was created
in the python-oracledb Thick mode.
.. attribute:: AsyncConnectionPool.timeout
This read-write attribute specifies the time (in seconds) after which idle
connections will be terminated in order to maintain an optimum number of
open connections. A value of 0 means that no idle connections are
terminated.
.. attribute:: AsyncConnectionPool.username
This read-only attribute returns the name of the user which established the
connection to the database.
.. attribute:: AsyncConnectionPool.wait_timeout
This read-write attribute specifies the time (in milliseconds) that the
caller should wait for a connection to become available in the pool before
returning with an error. This value is only used if the ``getmode``
parameter to :meth:`oracledb.create_pool_async()` was the value
:data:`oracledb.POOL_GETMODE_TIMEDWAIT`.

View File

@ -0,0 +1,488 @@
.. _asynccursorobj:
************************
API: AsyncCursor Objects
************************
An AsyncCursor object can be created with :meth:`AsyncConnection.cursor()`.
Unless explicitly noted as synchronous, the AsyncCursor methods should be used
with ``await``. This object is an extension to the DB API.
.. versionadded:: 2.0.0
.. note::
The Asynchronous I/O (asyncio) support in python-oracledb 2.0.0 is a
pre-release and may change in the next version.
.. note::
AsyncCursor objects are only supported in the python-oracledb Thin mode.
.. _asynccursormeth:
AsyncCursor Methods
===================
.. method:: AsyncCursor.__aiter__()
Returns the cursor itself to be used as an asynchronous iterator.
.. method:: AsyncCursor.__enter__()
The entry point for the cursor as a context manager. It returns itself.
.. method:: AsyncCursor.__exit__()
The exit point for the cursor as a context manager. It closes the cursor.
.. method:: AsyncCursor.arrayvar(typ, value, [size])
A synchronous method that creates an array variable associated with the
cursor of the given type and size and returns a
:ref:`variable object <varobj>`. The value is either an integer specifying
the number of elements to allocate or it is a list and the number of
elements allocated is drawn from the size of the list. If the value is a
list, the variable is also set with the contents of the list. If the size
is not specified and the type is a string or binary, 4000 bytes is
allocated. This is needed for passing arrays to PL/SQL (in cases where
the list might be empty and the type cannot be determined automatically) or
returning arrays from PL/SQL.
Array variables can only be used for PL/SQL associative arrays with
contiguous keys. For PL/SQL associative arrays with sparsely populated keys
or for varrays and nested tables, the approach shown in this
`example <https://github.com/oracle/python-oracledb/blob/main/
samples/plsql_collection.py>`__ needs to be used.
.. method:: AsyncCursor.bindnames()
A synchronous method that returns the list of bind variable names bound to
the statement. Note that a statement must have been prepared first.
.. method:: AsyncCursor.callfunc(name, returnType, parameters=[], \
keyword_parameters={})
Calls a function with the given name. The return type is specified in the
same notation as is required by :meth:`~AsyncCursor.setinputsizes()`. The
sequence of parameters must contain one entry for each parameter that the
function expects. Any keyword parameters will be included after the
positional parameters. The result of the call is the return value of the
function.
See :ref:`plsqlfunc` for an example.
.. note::
If you intend to call :meth:`AsyncCursor.setinputsizes()` on the cursor
prior to making this call, then note that the first item in the
parameter list refers to the return value of the function.
.. method:: AsyncCursor.callproc(name, parameters=[], keyword_parameters={})
Calls a procedure with the given name. The sequence of parameters must
contain one entry for each parameter that the procedure expects. The result
of the call is a modified copy of the input sequence. Input parameters are
left untouched; output and input/output parameters are replaced with
possibly new values. Keyword parameters will be included after the
positional parameters and are not returned as part of the output sequence.
See :ref:`plsqlproc` for an example.
.. method:: AsyncCursor.close()
A synchronous method that closes the cursor now, rather than whenever
``__del__`` is called. The cursor will be unusable from this point
forward; an Error exception will be raised if any operation is attempted
with the cursor.
.. method:: AsyncCursor.execute(statement, parameters=[], ** keyword_parameters)
Executes a statement against the database. See :ref:`sqlexecution`.
Parameters may be passed as a dictionary or sequence or as keyword
parameters. If the parameters are a dictionary, the values will be bound by
name and if the parameters are a sequence the values will be bound by
position. Note that if the values are bound by position, the order of the
variables is from left to right as they are encountered in the statement
and SQL statements are processed differently than PL/SQL statements. For
this reason, it is generally recommended to bind parameters by name instead
of by position.
Parameters passed as a dictionary are name and value pairs. The name maps
to the bind variable name used by the statement and the value maps to the
Python value you wish bound to that bind variable.
A reference to the statement will be retained by the cursor. If None or the
same string object is passed in again, the cursor will execute that
statement again without performing a prepare or rebinding and redefining.
This is most effective for algorithms where the same statement is used, but
different parameters are bound to it (many times). Note that parameters
that are not passed in during subsequent executions will retain the value
passed in during the last execution that contained them.
For maximum efficiency when reusing a statement, it is best to use the
:meth:`~AsyncCursor.setinputsizes()` method to specify the parameter types and
sizes ahead of time; in particular, None is assumed to be a string of
length 1 so any values that are later bound as numbers or dates will raise
a TypeError exception.
If the statement is a query, the cursor is returned as a convenience to the
caller (so it can be used directly as an iterator over the rows in the
cursor); otherwise, ``None`` is returned.
.. method:: AsyncCursor.executemany(statement, parameters, batcherrors=False, \
arraydmlrowcounts=False)
Prepares a statement for execution against a database and then execute it
against all parameter mappings or sequences found in the sequence
parameters. See :ref:`batchstmnt`.
The ``statement`` parameter is managed in the same way as the
:meth:`~AsyncCursor.execute()` method manages it. If the size of the buffers
allocated for any of the parameters exceeds 2 GB, you will receive the
error "DPI-1015: array size of <n> is too large", where <n> varies with the
size of each element being allocated in the buffer. If you receive this
error, decrease the number of elements in the sequence parameters.
If there are no parameters, or parameters have previously been bound, the
number of iterations can be specified as an integer instead of needing to
provide a list of empty mappings or sequences.
When True, the ``batcherrors`` parameter enables batch error support within
Oracle and ensures that the call succeeds even if an exception takes place
in one or more of the sequence of parameters. The errors can then be
retrieved using :meth:`~AsyncCursor.getbatcherrors()`.
When True, the ``arraydmlrowcounts`` parameter enables DML row counts to be
retrieved from Oracle after the method has completed. The row counts can
then be retrieved using :meth:`~AsyncCursor.getarraydmlrowcounts()`.
Both the ``batcherrors`` parameter and the ``arraydmlrowcounts`` parameter
can only be true when executing an insert, update, delete or merge
statement; in all other cases an error will be raised.
For maximum efficiency, it is best to use the
:meth:`~AsyncCursor.setinputsizes()` method to specify the parameter types and
sizes ahead of time; in particular, None is assumed to be a string of
length 1 so any values that are later bound as numbers or dates will raise
a TypeError exception.
.. method:: AsyncCursor.fetchall()
Fetches all (remaining) rows of a query result, returning them as a list of
tuples. An empty list is returned if no more rows are available. Note that
the cursor's ``arraysize`` attribute can affect the performance of this
operation, as internally reads from the database are done in batches
corresponding to ``arraysize``.
An exception is raised if the previous call to
:meth:`~AsyncCursor.execute()` did not produce any result set or no call
was issued yet.
.. method:: AsyncCursor.fetchmany(size=cursor.arraysize)
Fetches the next set of rows of a query result, returning a list of tuples.
An empty list is returned if no more rows are available. Note that the
cursor's arraysize attribute can affect the performance of this operation.
The number of rows to fetch is specified by the parameter. If it is not
given, the cursor's arraysize attribute determines the number of rows to be
fetched. If the number of rows available to be fetched is fewer than the
amount requested, fewer rows will be returned.
An exception is raised if the previous call to
:meth:`~AsyncCursor.execute()` did not produce any result set or no call
was issued yet.
.. method:: AsyncCursor.fetchone()
Fetches the next row of a query result set, returning a single tuple or
None when no more data is available.
An exception is raised if the previous call to
:meth:`~AsyncCursor.execute()` did not produce any result set or no call
was issued yet.
.. method:: AsyncCursor.getarraydmlrowcounts()
A synchronous method that retrieves the DML row counts after a call to
:meth:`~AsyncCursor.executemany()` with arraydmlrowcounts enabled. This
will return a list of integers corresponding to the number of rows
affected by the DML statement for each element of the array passed to
:meth:`~AsyncCursor.executemany()`.
.. note::
This method is only available for Oracle 12.1 and later.
.. method:: AsyncCursor.getbatcherrors()
A synchronous method that retrieves the exceptions that took place after a
call to :meth:`~AsyncCursor.executemany()` with batcherrors enabled. This
will return a list of Error objects, one error for each iteration that
failed. The offset can be determined by looking at the offset attribute of
the error object.
.. method:: AsyncCursor.getimplicitresults()
A synchronous method that returns a list of cursors which correspond to
implicit results made available from a PL/SQL block or procedure without
the use of OUT ref cursor parameters. The PL/SQL block or procedure opens
the cursors and marks them for return to the driver using the procedure
dbms_sql.return_result. Cursors returned in this fashion should not be
closed. They will be closed automatically by the parent cursor when it is
closed. Closing the parent cursor will invalidate the cursors returned by
this method.
.. note::
This method is only available with Oracle Database 12.1 or later. It is
most like the DB API method nextset(), but unlike that method (which
requires that the next result set overwrite the current result set),
this method returns cursors which can be fetched independently of each
other.
.. method:: AsyncCursor.parse(statement)
This can be used to parse a statement without actually executing it
(parsing step is done automatically by Oracle when a statement is
:meth:`executed <AsyncCursor.execute>`).
.. note::
You can parse any DML or DDL statement. DDL statements are executed
immediately and an implied commit takes place.
.. method:: AsyncCursor.prepare(statement, tag, cache_statement=True)
A synchronous method that can be used before a call to
:meth:`~AsyncCursor.execute()` to define the statement that will be
executed. When this is done, the prepare phase will not be performed when
the call to :meth:`~AsyncCursor.execute()` is made with None or the same
string object as the statement.
If the ``tag`` parameter is specified and the ``cache_statement`` parameter
is True, the statement will be returned to the statement cache with the
given tag.
If the ``cache_statement`` parameter is False, the statement will be
removed from the statement cache (if it was found there) or will simply not
be cached.
See :ref:`Statement Caching <stmtcache>` for more information.
.. method:: AsyncCursor.setinputsizes(*args, **keywordArgs)
A synchronous method that can be used before a call to
:meth:`~AsyncCursor.execute()`, :meth:`~AsyncCursor.executemany()`,
:meth:`~AsyncCursor.callfunc()` or :meth:`~AsyncCursor.callproc()` to
predefine memory areas for the operation's parameters. Each parameter
should be a type object corresponding to the input that will be used or it
should be an integer specifying the maximum length of a string parameter.
Use keyword parameters when binding by name and positional parameters when
binding by position. The singleton None can be used as a parameter when
using positional parameters to indicate that no space should be reserved
for that position.
.. note::
If you plan to use :meth:`~AsyncCursor.callfunc()` then be aware that the
first parameter in the list refers to the return value of the function.
.. method:: AsyncCursor.setoutputsize(size, [column])
This method does nothing and is retained solely for compatibility with the
DB API. The module automatically allocates as much space as needed to fetch
LONG and LONG RAW columns (or CLOB as string and BLOB as bytes).
.. method:: AsyncCursor.var(typ, [size, arraysize, inconverter, outconverter, \
typename, encoding_errors, bypass_decode, convert_nulls])
A synchronous method that creates a variable with the specified
characteristics. This method was designed for use with PL/SQL in/out
variables where the length or type cannot be determined automatically from
the Python object passed in or for use in input and output type handlers
defined on cursors or connections.
The ``typ`` parameter specifies the type of data that should be stored in the
variable. This should be one of the :ref:`database type constants
<dbtypes>`, :ref:`DB API constants <types>`, an object type returned from
the method :meth:`AsyncConnection.gettype()` or one of the following Python
types:
.. list-table-with-summary::
:header-rows: 1
:class: wy-table-responsive
:align: center
:summary: The first column is the Python Type. The second column is the corresponding Database Type.
* - Python Type
- Database Type
* - bool
- :attr:`oracledb.DB_TYPE_BOOLEAN`
* - bytes
- :attr:`oracledb.DB_TYPE_RAW`
* - datetime.date
- :attr:`oracledb.DB_TYPE_DATE`
* - datetime.datetime
- :attr:`oracledb.DB_TYPE_DATE`
* - datetime.timedelta
- :attr:`oracledb.DB_TYPE_INTERVAL_DS`
* - decimal.Decimal
- :attr:`oracledb.DB_TYPE_NUMBER`
* - float
- :attr:`oracledb.DB_TYPE_NUMBER`
* - int
- :attr:`oracledb.DB_TYPE_NUMBER`
* - str
- :attr:`oracledb.DB_TYPE_VARCHAR`
The ``size`` parameter specifies the length of string and raw variables and is
ignored in all other cases. If not specified for string and raw variables,
the value 4000 is used.
The ``arraysize`` parameter specifies the number of elements the variable will
have. If not specified the bind array size (usually 1) is used. When a
variable is created in an output type handler this parameter should be set
to the cursor's array size.
The ``inconverter`` and ``outconverter`` parameters specify methods used for
converting values to/from the database. More information can be found in
the section on :ref:`variable objects<varobj>`.
The ``typename`` parameter specifies the name of a SQL object type and must be
specified when using type :data:`oracledb.OBJECT` unless the type object
was passed directly as the first parameter.
The ``encoding_errors`` parameter specifies what should happen when decoding
byte strings fetched from the database into strings. It should be one of
the values noted in the builtin
`decode <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__
function.
The ``bypass_decode`` parameter, if specified, should be passed as a
boolean value. Passing a `True` value causes values of database types
:data:`~oracledb.DB_TYPE_VARCHAR`, :data:`~oracledb.DB_TYPE_CHAR`,
:data:`~oracledb.DB_TYPE_NVARCHAR`, :data:`~oracledb.DB_TYPE_NCHAR` and
:data:`~oracledb.DB_TYPE_LONG` to be returned as `bytes` instead of `str`,
meaning that python-oracledb does not do any decoding. See :ref:`Fetching raw
data <fetching-raw-data>` for more information.
The ``convert_nulls`` parameter, if specified, should be passed as a boolean
value. Passing the value ``True`` causes the ``outconverter`` to be called
when a null value is fetched from the database; otherwise, the
``outconverter`` is only called when non-null values are fetched from the
database.
.. _asynccursorattr:
AsyncCursor Attributes
======================
.. attribute:: AsyncCursor.arraysize
This read-write attribute can be used to tune the number of rows internally
fetched and buffered by internal calls to the database when fetching rows
from SELECT statements and REF CURSORS. The value can drastically affect
the performance of a query since it directly affects the number of network
round trips between Python and the database. For methods like
:meth:`~AsyncCursor.fetchone()` and :meth:`~AsyncCursor.fetchall()` it
does not change how many rows are returned to the application. For
:meth:`~AsyncCursor.fetchmany()` it is the default number of rows to fetch.
The attribute is only used for tuning row and SODA document fetches from
the database. It does not affect data inserts.
Due to the performance benefits, the default ``Cursor.arraysize`` is 100
instead of the 1 that the Python DB API recommends.
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
.. attribute:: AsyncCursor.bindvars
This read-only attribute provides the bind variables used for the last
execute. The value will be either a list or a dictionary depending on
whether binding was done by position or name. Care should be taken when
referencing this attribute. In particular, elements should not be removed
or replaced.
.. attribute:: AsyncCursor.description
This read-only attribute is a sequence of :ref:`FetchInfo<fetchinfoobj>`
objects. This attribute will be None for operations that do not return rows
or if the cursor has not had an operation invoked via the
:meth:`~AsyncCursor.execute()` method yet.
.. attribute:: AsyncCursor.fetchvars
This read-only attribute specifies the list of variables created for the
last query that was executed on the cursor. Care should be taken when
referencing this attribute. In particular, elements should not be removed
or replaced.
.. attribute:: AsyncCursor.inputtypehandler
This read-write attribute specifies a method called for each value that is
bound to a statement executed on the cursor and overrides the attribute
with the same name on the connection if specified. The method signature is
handler(cursor, value, arraysize) and the return value is expected to be a
variable object or None in which case a default variable object will be
created. If this attribute is None, the default behavior will take place
for all values bound to the statements.
.. attribute:: AsyncCursor.lastrowid
This read-only attribute returns the rowid of the last row modified by the
cursor. If no row was modified by the last operation performed on the
cursor, the value None is returned.
.. attribute:: AsyncCursor.outputtypehandler
This read-write attribute specifies a method called for each column that is
to be fetched from this cursor. The method signature is
handler(cursor, metadata) and the return value is expected to be a
:ref:`variable object<varobj>` or None in which case a default variable
object will be created. If this attribute is None, then the default
behavior will take place for all columns fetched from this cursor.
See :ref:`outputtypehandlers`.
.. attribute:: AsyncCursor.prefetchrows
This read-write attribute can be used to tune the number of rows that the
python-oracledb fetches when a SELECT statement is executed. This value can
reduce the number of round-trips to the database that are required to fetch
rows but at the cost of additional memory. Setting this value to 0 can be
useful when the timing of fetches must be explicitly controlled.
The attribute is only used for tuning row fetches from the database. It
does not affect data inserts.
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
.. attribute:: AsyncCursor.rowcount
This read-only attribute specifies the number of rows that have currently
been fetched from the cursor (for select statements), that have been
affected by the operation (for insert, update, delete and merge
statements), or the number of successful executions of the statement
(for PL/SQL statements).
.. attribute:: AsyncCursor.rowfactory
This read-write attribute specifies a method to call for each row that is
retrieved from the database. Ordinarily, a tuple is returned for each row
but if this attribute is set, the method is called with the tuple that
would normally be returned, and the result of the method is returned
instead.
See :ref:`rowfactories`.
.. attribute:: AsyncCursor.scrollable
This read-write boolean attribute specifies whether the cursor can be
scrolled or not. By default, cursors are not scrollable, as the server
resources and response times are greater than nonscrollable cursors. This
attribute is checked and the corresponding mode set in Oracle when calling
the method :meth:`~AsyncCursor.execute()`.

View File

@ -0,0 +1,102 @@
.. _asynclobobj:
*********************
API: AsyncLOB Objects
*********************
An AsyncLOB object can be created with :meth:`AsyncConnection.createlob()`.
Also, this object is returned whenever Oracle :data:`CLOB`, :data:`BLOB` and
:data:`BFILE` columns are fetched. This object is an extension to the DB API.
See :ref:`lobdata` for more information about using LOBs.
.. note::
The Asynchronous I/O (asyncio) support in python-oracledb 2.0.0 is a
pre-release and may change in the next version.
.. note::
AsyncLOB objects are only supported in the python-oracledb Thin mode.
.. _asynclobmeth:
AsyncLOB Methods
================
.. method:: AsyncLOB.close()
Closes the LOB. Call this when writing is completed so that the indexes
associated with the LOB can be updated -- but only if :meth:`~AsyncLOB.open()`
was called first.
.. method:: AsyncLOB.fileexists()
Returns a boolean indicating if the file referenced by the BFILE type LOB
exists.
.. method:: AsyncLOB.getchunksize()
Returns the chunk size for the internal LOB. Reading and writing to the LOB
in chunks of multiples of this size will improve performance.
.. method:: AsyncLOB.getfilename()
Returns a two-tuple consisting of the directory alias and file name for a
BFILE type LOB.
.. method:: AsyncLOB.isopen()
Returns a boolean indicating if the LOB has been opened using the method
:meth:`~AsyncLOB.open()`.
.. method:: AsyncLOB.open()
Opens the LOB for writing. This will improve performance when writing to a
LOB in chunks and there are functional or extensible indexes associated
with the LOB. If this method is not called, each write will perform an open
internally followed by a close after the write has been completed.
.. method:: AsyncLOB.read([offset=1, [amount]])
Returns a portion (or all) of the data in the LOB object. Note that the
amount and offset are in bytes for BLOB and BFILE type LOBs and in UCS-2
code points for CLOB and NCLOB type LOBs. UCS-2 code points are equivalent
to characters for all but supplemental characters. If supplemental
characters are in the LOB, the offset and amount will have to be chosen
carefully to avoid splitting a character.
.. method:: AsyncLOB.setfilename(dirAlias, name)
Sets the directory alias and name of the BFILE type LOB.
.. method:: AsyncLOB.size()
Returns the size of the data in the LOB object. For BLOB and BFILE type
LOBs, this is the number of bytes. For CLOB and NCLOB type LOBs, this is the
number of UCS-2 code points. UCS-2 code points are equivalent to characters
for all but supplemental characters.
.. method:: AsyncLOB.trim(new_size=0)
Trims the LOB to the new size.
.. method:: AsyncLOB.write(data, offset=1)
Writes the data to the LOB object at the given offset. The offset is in
bytes for BLOB type LOBs and in UCS-2 code points for CLOB and NCLOB type
LOBs. UCS-2 code points are equivalent to characters for all but
supplemental characters. If supplemental characters are in the LOB, the
offset will have to be chosen carefully to avoid splitting a character.
Note that if you want to make the LOB value smaller, you must use the
:meth:`~AsyncLOB.trim()` function.
.. _asynclobattr:
AsyncLOB Attributes
===================
.. attribute:: AsyncLOB.type
This read-only attribute returns the type of the LOB as one of the
:ref:`database type constants <dbtypes>`.

View File

@ -24,7 +24,6 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.__exit__()
The exit point for the connection as a context manager. This will close
@ -34,7 +33,6 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.begin([formatId, transactionId, branchId])
Explicitly begins a new transaction. Without parameters, this explicitly
@ -62,7 +60,6 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.changepassword(oldpassword, newpassword)
Changes the password for the user to which the connection is
@ -126,7 +123,6 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.gettype(name)
Returns a :ref:`type object <dbobjecttype>` given its name. This can then be
@ -137,7 +133,6 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.is_healthy()
This function returns a boolean indicating the health status of a connection.
@ -176,7 +171,6 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.prepare()
Prepares the distributed (global) transaction for commit. Return a boolean
@ -191,7 +185,6 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.queue(name, payload_type=None)
Creates a :ref:`queue <queue>` which is used to enqueue and dequeue
@ -213,12 +206,10 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.rollback()
Rolls back any pending transactions.
.. method:: Connection.shutdown([mode])
Shuts down the database. In order to do this the connection must be connected
@ -556,7 +547,6 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.autocommit
This read-write attribute determines whether autocommit mode is on or off.
@ -596,7 +586,6 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.clientinfo
This write-only attribute sets the client_info column in the v$session
@ -646,7 +635,7 @@ Connection Attributes
This write-only attribute sets the database operation that is to be
monitored. This can be viewed in the ``DBOP_NAME`` column of the
``V$SQL_MONITOR`` table.
``v$sql_monitor`` table.
.. note::
@ -688,9 +677,9 @@ Connection Attributes
.. attribute:: Connection.handle
This read-only attribute returns the Oracle Call Interface (OCI) service context handle for the
connection. It is primarily provided to facilitate testing the creation of
a connection using the OCI service context handle.
This read-only attribute returns the Oracle Call Interface (OCI) service
context handle for the connection. It is primarily provided to facilitate
testing the creation of a connection using the OCI service context handle.
This property is only relevant in the python-oracledb Thick mode.
@ -698,7 +687,6 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.inputtypehandler
This read-write attribute specifies a method called for each value that is
@ -712,7 +700,6 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.instance_name
This read-only attribute specifies the Oracle Database instance name
@ -760,7 +747,6 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.module
This write-only attribute sets the module column in the v$session table.

View File

@ -77,7 +77,6 @@ ConnectionPool Methods
database shard to connect to. The key values can be strings, numbers, bytes
or dates.
.. method:: ConnectionPool.close(force=False)
Closes the pool now, rather than when the last reference to it is
@ -86,13 +85,11 @@ ConnectionPool Methods
If any connections have been acquired and not released back to the pool,
this method will fail unless the ``force`` parameter is set to True.
.. method:: ConnectionPool.drop(connection)
Drops the connection from the pool which is useful if the connection is no
longer usable (such as when the session is killed).
.. method:: ConnectionPool.reconfigure([min, max, increment, getmode, \
timeout, wait_timeout, max_lifetime_session, max_sessions_per_shard, \
soda_metadata_cache, stmtcachesize, ping_interval])
@ -145,7 +142,6 @@ ConnectionPool Methods
See :ref:`Connection Pool Reconfiguration <poolreconfiguration>`.
.. method:: ConnectionPool.release(connection, tag=None)
Releases the connection back to the pool now, rather than whenever __del__
@ -168,7 +164,6 @@ ConnectionPool Methods
parameter are not None, the connection will be retagged when it is released
back to the pool.
ConnectionPool Attributes
=========================
@ -177,13 +172,11 @@ ConnectionPool Attributes
This read-only attribute returns the number of connections currently
acquired.
.. attribute:: ConnectionPool.dsn
This read-only attribute returns the TNS entry of the database to which a
connection has been established.
.. attribute:: ConnectionPool.getmode
This read-write attribute determines how connections are returned from the
@ -204,19 +197,16 @@ ConnectionPool Attributes
different authentication can be used for each connection acquired from the
pool.
.. attribute:: ConnectionPool.increment
This read-only attribute returns the number of connections that will be
established when additional connections need to be created.
.. attribute:: ConnectionPool.max
This read-only attribute returns the maximum number of connections that the
pool can control.
.. attribute:: ConnectionPool.max_lifetime_session
This read-write attribute returns the maximum length of time (in seconds)
@ -238,25 +228,21 @@ ConnectionPool Attributes
of sessions for each shard. This attribute is only available in Oracle
Client 18.3 and higher.
.. attribute:: ConnectionPool.min
This read-only attribute returns the number of connections with which the
connection pool was created and the minimum number of connections that will
be controlled by the connection pool.
.. attribute:: ConnectionPool.name
This read-only attribute returns the name assigned to the pool by Oracle.
.. attribute:: ConnectionPool.opened
This read-only attribute returns the number of connections currently opened
by the pool.
.. attribute:: ConnectionPool.ping_interval
This read-write integer attribute specifies the pool ping interval in
@ -290,7 +276,6 @@ ConnectionPool Attributes
See :ref:`Statement Caching <stmtcache>` for more information.
.. attribute:: ConnectionPool.thin
This attribute returns a boolean which indicates the python-oracledb mode
@ -313,7 +298,6 @@ ConnectionPool Attributes
This read-only attribute returns the name of the user which established the
connection to the database.
.. attribute:: ConnectionPool.wait_timeout
This read-write attribute specifies the time (in milliseconds) that the

View File

@ -4,7 +4,7 @@
API: Cursor Objects
*******************
A cursor object can be created with :meth:`Connection.cursor`.
A cursor object can be created with :meth:`Connection.cursor()`.
Cursor Methods
==============
@ -26,6 +26,14 @@ Cursor Methods
This method is an extension to the DB API definition.
.. method:: Cursor.__iter__()
Returns the cursor itself to be used as an iterator.
.. note::
This method is an extension to the DB API definition but it is
mentioned in PEP 249 as an optional extension.
.. method:: Cursor.arrayvar(typ, value, [size])
@ -49,7 +57,6 @@ Cursor Methods
The DB API definition does not define this method.
.. method:: Cursor.bindnames()
Returns the list of bind variable names bound to the statement. Note that a
@ -86,7 +93,6 @@ Cursor Methods
prior to making this call, then note that the first item in the
parameter list refers to the return value of the function.
.. method:: Cursor.callproc(name, parameters=[], keyword_parameters={})
Calls a procedure with the given name. The sequence of parameters must
@ -138,7 +144,7 @@ Cursor Methods
that are not passed in during subsequent executions will retain the value
passed in during the last execution that contained them.
For maximum efficiency when reusing an statement, it is best to use the
For maximum efficiency when reusing a statement, it is best to use the
:meth:`~Cursor.setinputsizes()` method to specify the parameter types and
sizes ahead of time; in particular, None is assumed to be a string of
length 1 so any values that are later bound as numbers or dates will raise
@ -152,7 +158,6 @@ Cursor Methods
The DB API definition does not define the return value of this method.
.. method:: Cursor.executemany(statement, parameters, batcherrors=False, \
arraydmlrowcounts=False)
@ -190,7 +195,6 @@ Cursor Methods
length 1 so any values that are later bound as numbers or dates will raise
a TypeError exception.
.. method:: Cursor.fetchall()
Fetches all (remaining) rows of a query result, returning them as a list of
@ -204,7 +208,6 @@ Cursor Methods
See :ref:`fetching` for an example.
.. method:: Cursor.fetchmany(size=cursor.arraysize)
Fetches the next set of rows of a query result, returning a list of tuples.
@ -231,7 +234,6 @@ Cursor Methods
See :ref:`fetching` for an example.
.. method:: Cursor.getarraydmlrowcounts()
Retrieves the DML row counts after a call to :meth:`~Cursor.executemany()`
@ -242,8 +244,7 @@ Cursor Methods
.. note::
The DB API definition does not define this method and it is only
available for Oracle 12.1 and higher.
available for Oracle 12.1 and later.
.. method:: Cursor.getbatcherrors()
@ -256,7 +257,6 @@ Cursor Methods
The DB API definition does not define this method.
.. method:: Cursor.getimplicitresults()
Returns a list of cursors which correspond to implicit results made
@ -277,21 +277,11 @@ Cursor Methods
the current result set), this method returns cursors which can be
fetched independently of each other.
.. method:: Cursor.__iter__()
Returns the cursor itself to be used as an iterator.
.. note::
This method is an extension to the DB API definition but it is
mentioned in PEP 249 as an optional extension.
.. method:: Cursor.parse(statement)
This can be used to parse a statement without actually executing it (this
step is done automatically by Oracle when a statement is executed).
This can be used to parse a statement without actually executing it
(parsing step is done automatically by Oracle when a statement is
:meth:`executed <Cursor.execute>`).
.. note::
@ -302,7 +292,6 @@ Cursor Methods
You can parse any DML or DDL statement. DDL statements are executed
immediately and an implied commit takes place.
.. method:: Cursor.prepare(statement, tag, cache_statement=True)
This can be used before a call to :meth:`~Cursor.execute()` to define the
@ -324,7 +313,6 @@ Cursor Methods
The DB API definition does not define this method.
.. method:: Cursor.scroll(value=0, mode="relative")
Scrolls the cursor in the result set to a new position according to the
@ -344,8 +332,7 @@ Cursor Methods
This method is an extension to the DB API definition but it is
mentioned in PEP 249 as an optional extension.
.. method:: Cursor.setinputsizes(\*args, \*\*keywordArgs)
.. method:: Cursor.setinputsizes(*args, **keywordArgs)
This can be used before a call to :meth:`~Cursor.execute()`,
:meth:`~Cursor.executemany()`, :meth:`~Cursor.callfunc()` or
@ -362,14 +349,12 @@ Cursor Methods
If you plan to use :meth:`~Cursor.callfunc()` then be aware that the
first parameter in the list refers to the return value of the function.
.. method:: Cursor.setoutputsize(size, [column])
This method does nothing and is retained solely for compatibility with the
DB API. The module automatically allocates as much space as needed to fetch
LONG and LONG RAW columns (or CLOB as string and BLOB as bytes).
.. method:: Cursor.var(typ, [size, arraysize, inconverter, outconverter, \
typename, encoding_errors, bypass_decode, convert_nulls])
@ -504,7 +489,7 @@ Cursor Attributes
This attribute is an extension to the DB API definition but it is
mentioned in PEP 249 as an optional extension.
.. data:: Cursor.description
.. attribute:: Cursor.description
This read-only attribute is a sequence of :ref:`FetchInfo<fetchinfoobj>`
objects. This attribute will be None for operations that do not return rows
@ -542,13 +527,12 @@ Cursor Attributes
This attribute is an extension to the DB API definition.
.. data:: Cursor.lastrowid
.. attribute:: Cursor.lastrowid
This read-only attribute returns the rowid of the last row modified by the
cursor. If no row was modified by the last operation performed on the
cursor, the value None is returned.
.. attribute:: Cursor.outputtypehandler
This read-write attribute specifies a method called for each column that is
@ -598,7 +582,6 @@ Cursor Attributes
statements), or the number of successful executions of the statement
(for PL/SQL statements).
.. attribute:: Cursor.rowfactory
This read-write attribute specifies a method to call for each row that is

View File

@ -4,7 +4,8 @@
API: LOB Objects
****************
See :ref:`lobdata` for more information about using LOBs.
A LOB object can be created with :meth:`Connection.createlob()`. See
:ref:`lobdata` for more information about using LOBs.
.. note::
@ -20,31 +21,26 @@ LOB Methods
associated with the LOB can be updated -- but only if :meth:`~LOB.open()`
was called first.
.. method:: LOB.fileexists()
Returns a boolean indicating if the file referenced by the BFILE type LOB
exists.
.. method:: LOB.getchunksize()
Returns the chunk size for the internal LOB. Reading and writing to the LOB
in chunks of multiples of this size will improve performance.
.. method:: LOB.getfilename()
Returns a two-tuple consisting of the directory alias and file name for a
BFILE type LOB.
.. method:: LOB.isopen()
Returns a boolean indicating if the LOB has been opened using the method
:meth:`~LOB.open()`.
.. method:: LOB.open()
Opens the LOB for writing. This will improve performance when writing to a
@ -52,7 +48,6 @@ LOB Methods
with the LOB. If this method is not called, each write will perform an open
internally followed by a close after the write has been completed.
.. method:: LOB.read([offset=1, [amount]])
Returns a portion (or all) of the data in the LOB object. Note that the
@ -62,12 +57,10 @@ LOB Methods
characters are in the LOB, the offset and amount will have to be chosen
carefully to avoid splitting a character.
.. method:: LOB.setfilename(dirAlias, name)
Sets the directory alias and name of the BFILE type LOB.
.. method:: LOB.size()
Returns the size of the data in the LOB object. For BLOB and BFILE type
@ -75,12 +68,10 @@ LOB Methods
number of UCS-2 code points. UCS-2 code points are equivalent to characters
for all but supplemental characters.
.. method:: LOB.trim(new_size=0)
Trims the LOB to the new size.
.. method:: LOB.write(data, offset=1)
Writes the data to the LOB object at the given offset. The offset is in
@ -92,7 +83,7 @@ LOB Methods
:meth:`~LOB.trim()` function.
LOB Attributes
===============
==============
.. attribute:: LOB.type

View File

@ -221,17 +221,17 @@ Oracledb Methods
The ``events`` parameter is expected to be a boolean that specifies whether
the events mode should be enabled. This value is only used in the
python-oracledb Thick mode. This parameter is needed for continuous
query notification and high availability event notifications. The default
value is False.
python-oracledb Thick mode and is ignored in the Thin mode. This parameter
is needed for continuous query notification and high availability event
notifications. The default value is False.
The ``externalauth`` parameter is a boolean that specifies whether external
authentication should be used. This value is only used in the
python-oracledb Thick mode. The default value is False. For standalone
connections, external authentication occurs when the ``user`` and
``password`` attributes are not used. If these attributes are not used, you
can optionally set the ``externalauth`` attribute to True, which may aid
code auditing.
python-oracledb Thick mode and is ignored in the Thin mode. The default
value is False. For standalone connections, external authentication occurs
when the ``user`` and ``password`` attributes are not used. If these
attributes are not used, you can optionally set the ``externalauth``
attribute to True, which may aid code auditing.
If the ``mode`` parameter is specified, it must be one of the
:ref:`connection authorization modes <connection-authorization-modes>`
@ -251,17 +251,18 @@ Oracledb Methods
The ``edition`` parameter is expected to be a string that indicates the
edition to use for the connection. This parameter cannot be used
simultaneously with the ``cclass`` parameter. This value is used in the
python-oracledb Thick mode.
simultaneously with the ``cclass`` parameter. This value is only used in
the python-oracledb Thick mode and is ignored in the Thin mode.
The ``tag`` parameter is expected to be a string that identifies the type
of connection that should be returned from a pool. This value is only used
in the python-oracledb Thick mode.
in the python-oracledb Thick mode and is ignored in the Thin mode.
The ``matchanytag`` parameter is expected to be a boolean specifying
whether any tag can be used when acquiring a connection from the pool. This
value is only used in the python-oracledb Thick mode when acquiring a
connection from a pool. The default value is False.
connection from a pool. This value is ignored in the python-oracledb Thin
mode. The default value is False.
The ``config_dir`` parameter is expected to be a string that indicates the
directory in which configuration files (tnsnames.ora) are found. This value
@ -273,13 +274,13 @@ Oracledb Methods
identifies the application context used by the connection. This parameter
should contain namespace, name, and value and each entry in the tuple
should be a string. This value is only used in the python-oracledb Thick
mode.
mode and is ignored in the Thin mode.
The ``shardingkey`` parameter and ``supershardingkey`` parameters, if
specified, are expected to be a sequence of values which identifies the
database shard to connect to. The key values can be a list of strings,
numbers, bytes, or dates. This value is only used in the python-oracledb
Thick mode.
numbers, bytes, or dates. These values are only used in the
python-oracledb Thick mode and are ignored in the Thin mode.
The ``debug_jdwp`` parameter is expected to be a string with the format
`host=<host>;port=<port>` that specifies the host and port of the PL/SQL
@ -318,13 +319,234 @@ Oracledb Methods
and is only of use when embedding Python in an application (like
PowerBuilder) which has already made the connection. The connection thus
created should *never* be used after the source handle has been closed or
destroyed. This value is only used in the python-oracledb Thick mode. It
should be used with extreme caution. The default value is 0.
destroyed. This value is only used in the python-oracledb Thick mode and
is ignored in the Thin mode. It should be used with extreme caution. The
default value is 0.
.. versionchanged:: 2.0.0
The ``ssl_context`` and ``sdu`` parameters were added.
.. function:: connect_async(dsn=None, pool=None, conn_class=None, params=None, \
user=None, proxy_user=None, password=None, newpassword=None, \
wallet_password=None, access_token=None, host=None, port=1521, \
protocol="tcp", https_proxy=None, https_proxy_port=0, \
service_name=None, sid=None, server_type=None, cclass=None, \
purity=oracledb.PURITY_DEFAULT, expire_time=0, retry_count=0, \
retry_delay=0, tcp_connect_timeout=60.0, ssl_server_dn_match=True, \
ssl_server_cert_dn=None, wallet_location=None, events=False, \
externalauth=False, mode=oracledb.AUTH_MODE_DEFAULT, \
disable_oob=False, stmtcachesize=oracledb.defaults.stmtcachesize, \
edition=None, tag=None, matchanytag=False, \
config_dir=oracledb.defaults.config_dir, appcontext=[], \
shardingkey=[], supershardingkey=[], debug_jdwp=None, \
connection_id_prefix=None, ssl_context=None, sdu=8192, handle=0)
Constructor for creating a connection to the database. Returns an
:ref:`AsyncConnection Object <asyncconnobj>`. All parameters are optional
and can be specified as keyword parameters. See
:ref:`standaloneconnection` information about connections.
.. note::
The asyncio support in python-oracledb 2.0.0 is a pre-release and may
change in the next version. This method can only be used in
python-oracledb Thin mode.
.. versionadded:: 2.0.0
Some values, such as the database host name, can be specified as
parameters, as part of the connect string, and in the params object.
The precedence is that values in the ``dsn`` parameter override values
passed as individual parameters, which themselves override values set in
the ``params`` parameter object. Similar precedence rules also apply to
other values.
The ``dsn`` (data source name) parameter can be a string in the format
``user/password@connect_string`` or can simply be the connect string (in
which case authentication credentials such as the username and password
need to be specified separately). See :ref:`connstr` for more information.
The ``pool`` parameter is expected to be an AsyncConnectionPool object. The
use of this parameter is the equivalent of calling
:meth:`AsyncConnectionPool.acquire()`.
The ``conn_class`` parameter is expected to be AsyncConnection or a
subclass of AsyncConnection.
The ``params`` parameter is expected to be of type :ref:`ConnectParams
<connparam>` and contains connection parameters that will be used when
establishing the connection. If this parameter is not specified, the
additional keyword parameters will be used to create an instance of
ConnectParams. If both the params parameter and additional keyword
parameters are specified, the values in the keyword parameters have
precedence. Note that if a ``dsn`` is also supplied, then the values of the
parameters specified (if any) within the ``dsn`` will override the values
passed as additional keyword parameters, which themselves override the
values set in the ``params`` parameter object.
The ``user`` parameter is expected to be a string which indicates the name
of the user to connect to.
The ``proxy_user`` parameter is expected to be a string which indicates the
name of the proxy user to connect to. If this value is not specified, it
will be parsed out of user if user is in the form "user[proxy_user]".
The ``password`` parameter expected to be a string which indicates the
password for the user.
The ``newpassword`` parameter is expected to be a string which indicates
the new password for the user. The new password will take effect
immediately upon a successful connection to the database.
The ``wallet_password`` parameter is expected to be a string which
indicates the password to use to decrypt the PEM-encoded wallet, if it is
encrypted.
The ``access_token`` parameter is expected to be a string or a 2-tuple or
a callable. If it is a string, it specifies an Azure AD OAuth2 token used
for Open Authorization (OAuth 2.0) token based authentication. If it is a
2-tuple, it specifies the token and private key strings used for Oracle
Cloud Infrastructure (OCI) Identity and Access Management (IAM) token based
authentication. If it is a callable, it returns either a string or a
2-tuple used for OAuth 2.0 or OCI IAM token based authentication and is
useful when the pool needs to expand and create new connections but the
current authentication token has expired.
The ``host`` parameter is expected to be a string which specifies the name
or IP address of the machine hosting the listener, which handles the
initial connection to the database.
The ``port`` parameter is expected to be an integer which indicates the
port number on which the listener is listening. The default value is 1521.
The ``protocol`` parameter is expected to be one of the strings "tcp" or
"tcps" which indicates whether to use unencrypted network traffic or
encrypted network traffic (TLS). The default value is tcp.
The ``https_proxy`` parameter is expected to be a string which indicates
the name or IP address of a proxy host to use for tunneling secure
connections.
The ``https_proxy_port`` parameter is expected to be an integer which
indicates the port that is to be used to communicate with the proxy host.
The default value is 0.
The ``service_name`` parameter is expected to be a string which indicates
the service name of the database.
The ``sid`` parameter is expected to be a string which indicates the SID of
the database. It is recommended to use ``service_name`` instead.
The ``server_type`` parameter is expected to be a string that indicates the
type of server connection that should be established. If specified, it
should be one of `dedicated`, `shared`, or `pooled`.
The ``cclass`` parameter is expected to be a string that identifies the
connection class to use for Database Resident Connection Pooling (DRCP).
The ``purity`` parameter is expected to be one of the
:ref:`oracledb.PURITY_* <drcppurityconsts>` constants that identifies the
purity to use for DRCP. The purity will internally default to
:data:`~oracledb.PURITY_SELF` for pooled connections. For standalone
connections, the purity will internally default to
:data:`~oracledb.PURITY_NEW`.
The ``expire_time`` parameter is expected to be an integer which indicates
the number of minutes between the sending of keepalive probes. If this
parameter is set to a value greater than zero it enables keepalive. The
default value is 0.
The ``retry_count`` parameter is expected to be an integer that identifies
the number of times that a connection attempt should be retried before the
attempt is terminated. The default value is 0.
The ``retry_delay`` parameter is expected to be an integer that identifies
the number of seconds to wait before making a new connection attempt. The
default value is 0.
The ``tcp_connect_timeout`` parameter is expected to be a float that
indicates the maximum number of seconds to wait for establishing a
connection to the database host. The default value is 60.0.
The ``ssl_server_dn_match`` parameter is expected to be a boolean that
indicates whether the server certificate distinguished name (DN) should be
matched in addition to the regular certificate verification that is
performed. Note that if the ``ssl_server_cert_dn`` parameter is not
provided, host name matching is performed instead. The default value is
True.
The ``ssl_server_cert_dn`` parameter is expected to be a string that
indicates the distinguished name (DN) which should be matched with the
server. This value is ignored if the ``ssl_server_dn_match`` parameter is
not set to the value True.
The ``wallet_location`` parameter is expected to be a string that
identifies the directory where the wallet can be found. In python-oracledb
Thin mode, this must be the directory of the PEM-encoded wallet file,
ewallet.pem.
The ``events`` parameter is ignored in the python-oracledb Thin mode.
The ``externalauth`` parameter is ignored in the python-oracledb Thin mode.
If the ``mode`` parameter is specified, it must be one of the
:ref:`connection authorization modes <connection-authorization-modes>`
which are defined at the module level. The default value is
:data:`oracledb.AUTH_MODE_DEFAULT`.
The ``disable_oob`` parameter is expected to be a boolean that indicates
whether out-of-band breaks should be disabled. This value has no effect on
Windows which does not support this functionality. The default value is
False.
The ``stmtcachesize`` parameter is expected to be an integer which
specifies the initial size of the statement cache. The default is the
value of :attr:`defaults.stmtcachesize`.
The ``edition`` parameter is ignored in the python-oracledb Thin mode.
The ``tag`` parameter is ignored in the python-oracledb Thin mode.
The ``matchanytag`` parameter is ignored in the python-oracledb Thin mode.
The ``config_dir`` parameter is expected to be a string that indicates the
directory in which configuration files (tnsnames.ora) are found. The
default is the value of :attr:`defaults.config_dir`.
The ``appcontext`` parameter is ignored in the python-oracledb Thin mode.
The ``shardingkey`` parameter and ``supershardingkey`` parameters are
ignored in the python-oracledb Thin mode.
The ``debug_jdwp`` parameter is expected to be a string with the format
`host=<host>;port=<port>` that specifies the host and port of the PL/SQL
debugger. This allows using the Java Debug Wire Protocol (JDWP) to debug
PL/SQL code called by python-oracledb.
The ``connection_id_prefix`` parameter is expected to be a string and is
added to the beginning of the generated ``connection_id`` that is sent to
the database for `tracing <https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-B0FC69F9-2EBC-44E8-ACB2-62FBA14ABD5C>`__.
The ``ssl_context`` parameter is expected to be an SSLContext object used
for connecting to the database using TLS. This SSL context will be
modified to include the private key or any certificates found in a
separately supplied wallet. This parameter should only be specified if
the default SSLContext object cannot be used.
The ``sdu`` parameter is expected to be an integer that returns the
requested size of the Session Data Unit (SDU), in bytes. The value tunes
internal buffers used for communication to the database. Bigger values can
increase throughput for large queries or bulk data loads, but at the cost
of higher memory use. The SDU size that will actually be used is
negotiated down to the lower of this value and the database network SDU
configuration value. See the `SQL*Net documentation <https://www.oracle.
com/pls/topic/lookup?ctx=dblatest&id=GUID-86D61D6F-AD26-421A-BABA-
77949C8A2B04>`__ for more details. The default value is 8192 bytes.
The ``handle`` parameter is ignored in the python-oracledb Thin mode.
.. function:: ConnectParams(user=None, proxy_user=None, password=None, \
newpassword=None, wallet_password=None, access_token=None, host=None, \
port=1521, protocol="tcp", https_proxy=None, https_proxy_port=0, \
@ -687,12 +909,14 @@ Oracledb Methods
connection tag differs from the one requested.
The ``max_sessions_per_shard`` parameter is the maximum number of
connections that may be associated with a particular shard. The default
value is 0.
connections that may be associated with a particular shard. This value is
only used in the python-oracledb Thick mode and is ignored in the
python-oracledb Thin mode. The default value is 0.
The ``soda_metadata_cache`` parameter is a boolean that indicates whether
or not the SODA metadata cache should be enabled. The default value is
False.
or not the SODA metadata cache should be enabled. This value is only used
in the python-oracledb Thick mode and is ignored in the python-oracledb
Thin mode. The default value is False.
The ``ping_interval`` parameter is the length of time (in seconds) after
which an unused connection in the pool will be a candidate for pinging when
@ -819,13 +1043,13 @@ Oracledb Methods
The ``events`` parameter is expected to be a boolean that specifies whether
the events mode should be enabled. This value is only used in the
python-oracledb Thick mode. This parameter is needed for continuous
query notification and high availability event notifications. The default
value is False.
python-oracledb Thick mode and is ignored in the Thin mode. This parameter
is needed for continuous query notification and high availability event
notifications. The default value is False.
The ``externalauth`` parameter is a boolean that determines whether to use
external authentication. This value is only used in the python-oracledb
Thick mode. The default value is False.
Thick mode and is ignored in the Thin mode. The default value is False.
If the ``mode`` parameter is specified, it must be one of the
:ref:`connection authorization modes <connection-authorization-modes>`
@ -846,16 +1070,17 @@ Oracledb Methods
The ``edition`` parameter is expected to be a string that indicates the
edition to use for the connection. This parameter cannot be used
simultaneously with the ``cclass`` parameter. This value is used in the
python-oracledb Thick mode.
python-oracledb Thick mode and is ignored in the Thin mode.
The ``tag`` parameter is expected to be a string that identifies the type
of connection that should be returned from a pool. This value is only used
in the python-oracledb Thick mode.
in the python-oracledb Thick mode and is ignored in the Thin mode.
The ``matchanytag`` parameter is expected to be a boolean specifying
whether any tag can be used when acquiring a connection from the pool. This
value is only used in the python-oracledb Thick mode when acquiring a
connection from a pool. The default value is False.
connection from a pool. This value is ignored in the python-oracledb Thin
mode. The default value is False.
The ``config_dir`` parameter is expected to be a string that indicates the
directory in which configuration files (tnsnames.ora) are found. This value
@ -866,14 +1091,14 @@ Oracledb Methods
The ``appcontext`` parameter is expected to be a list of 3-tuples that
identifies the application context used by the connection. This parameter
should contain namespace, name, and value and each entry in the tuple
should be a string. This value is only used inthe python-oracledb Thick
mode.
should be a string. This value is only used in the python-oracledb Thick
mode and is ignored in the Thin mode.
The ``shardingkey`` parameter and ``supershardingkey`` parameters, if
specified, are expected to be a sequence of values which identifies the
database shard to connect to. The key values can be a list of strings,
numbers, bytes, or dates. This value is only used in the python-oracledb
Thick mode.
numbers, bytes, or dates. These values are only used in the
python-oracledb Thick mode and are ignored in the Thin mode.
The ``debug_jdwp`` parameter is expected to be a string with the format
`host=<host>;port=<port>` that specifies the host and port of the PL/SQL
@ -912,8 +1137,9 @@ Oracledb Methods
and is only of use when embedding Python in an application (like
PowerBuilder) which has already made the connection. The connection thus
created should *never* be used after the source handle has been closed or
destroyed. This value is only used in the python-oracledb Thick mode. It
should be used with extreme caution. The default value is 0.
destroyed. This value is only used in the python-oracledb Thick mode and
is ignored in the Thin mode. It should be used with extreme caution. The
default value is 0.
In the python-oracledb Thick mode, connection pooling is handled by
Oracle's `Session pooling <https://www.oracle.com/pls/topic/lookup?
@ -926,6 +1152,277 @@ Oracledb Methods
The ``ssl_context`` and ``sdu`` parameters were added.
.. function:: create_pool_async(dsn=None, pool_class=oracledb.AsyncConnectionPool, \
params=None, min=1, max=2, increment=1, \
connectiontype=oracledb.Connection, \
getmode=oracledb.POOL_GETMODE_WAIT, homogeneous=True, timeout=0, \
wait_timeout=0, max_lifetime_session=0, session_callback=None, \
max_sessions_per_shard=0, soda_metadata_cache=False, ping_interval=60, \
user=None, proxy_user=None, password=None, newpassword=None, \
wallet_password=None, access_token=None, host=None, port=1521, \
protocol="tcp", https_proxy=None, https_proxy_port=0, \
service_name=None, sid=None, server_type=None, cclass=None, \
purity=oracledb.PURITY_DEFAULT, expire_time=0, retry_count=0, \
retry_delay=0, tcp_connect_timeout=60.0, ssl_server_dn_match=True, \
ssl_server_cert_dn=None, wallet_location=None, events=False, \
externalauth=False, mode=oracledb.AUTH_MODE_DEFAULT, \
disable_oob=False, stmtcachesize=oracledb.defaults.stmtcachesize, \
edition=None, tag=None, matchanytag=False, \
config_dir=oracledb.defaults.config_dir, appcontext=[], \
shardingkey=[], supershardingkey=[], debug_jdwp=None, \
connection_id_prefix=None, ssl_context=None, sdu=8192, handle=0)
Creates a connection pool with the supplied parameters and returns the
:ref:`AsyncConnectionPool object <asyncconnpoolobj>` for the pool.
``create_pool_async()`` is a synchronous method. See
:ref:`Connection pooling <asyncconnpool>` for more information.
.. note::
The asyncio support in python-oracledb 2.0.0 is a pre-release and may
change in the next version. This method can only used in
python-oracledb Thin mode.
.. versionadded:: 2.0.0
Some values, such as the database host name, can be specified as
parameters, as part of the connect string, and in the params object.
The precedence is that values in the ``dsn`` parameter override values
passed as individual parameters, which themselves override values set in
the ``params`` parameter object. Similar precedence rules also apply to
other values.
The ``user``, ``password``, and ``dsn`` parameters are the same as for
:meth:`oracledb.connect_async()`.
The ``pool_class`` parameter is expected to be a
:ref:`AsyncConnectionPool Object <asyncconnpoolobj>` or a subclass of
AsyncConnectionPool.
The ``params`` parameter is expected to be of type :ref:`PoolParams
<poolparam>` and contains parameters that are used to create the pool.
If this parameter is not specified, the additional keyword parameters will
be used to create an instance of PoolParams. If both the params parameter
and additional keyword parameters are specified, the values in the keyword
parameters have precedence. Note that if a ``dsn`` is also supplied, then
the values of the parameters specified (if any) within the ``dsn`` will
override the values passed as additional keyword parameters, which
themselves override the values set in the ``params`` parameter object.
The ``min``, ``max`` and ``increment`` parameters control pool growth
behavior. A fixed pool size where ``min`` equals ``max`` is
:ref:`recommended <connpoolsize>` to help prevent connection storms and to
help overall system stability. The ``min`` parameter is the number of
connections opened when the pool is created. The default value of the
``min`` parameter is 1. The ``increment`` parameter is the number of
connections that are opened whenever a connection request exceeds the
number of currently open connections. The default value of the
``increment`` parameter is 1. The ``max`` parameter is the maximum number
of connections that can be open in the connection pool. The default value
of the ``max`` parameter is 2.
If the ``connectiontype`` parameter is specified, all calls to
:meth:`AsyncConnectionPool.acquire()` will create connection objects of
that type, rather than the base type defined at the module level.
The ``getmode`` parameter determines the behavior of
:meth:`AsyncConnectionPool.acquire()`. One of the constants
:data:`oracledb.POOL_GETMODE_WAIT`, :data:`oracledb.POOL_GETMODE_NOWAIT`,
:data:`oracledb.POOL_GETMODE_FORCEGET`, or
:data:`oracledb.POOL_GETMODE_TIMEDWAIT`. The default value is
:data:`oracledb.POOL_GETMODE_WAIT`.
The ``homogeneous`` parameter is a boolean that indicates whether the
connections are homogeneous (same user) or heterogeneous (multiple
users). The default value is True.
The ``timeout`` parameter is the length of time (in seconds) that a
connection may remain idle in the pool before it is terminated. This
applies only when the pool has more than ``min`` connections open, allowing
it to shrink to the specified minimum size. If the value of this parameter
is 0, then the connections are never terminated. The default value is 0.
The ``wait_timeout`` parameter is the length of time (in milliseconds) that
a caller should wait when acquiring a connection from the pool with
``getmode`` set to :data:`oracledb.POOL_GETMODE_TIMEDWAIT`. The default
value is 0.
The ``max_lifetime_session`` parameter is the length of time (in seconds)
that connections can remain in the pool. If the value of this parameter is
0, then the connections may remain in the pool indefinitely. The default
value is 0.
The ``session_callback`` parameter is a callable that is invoked when a
connection is returned from the pool for the first time, or when the
connection tag differs from the one requested.
The ``max_sessions_per_shard`` parameter is ignored in the python-oracledb
Thin mode.
The ``soda_metadata_cache`` parameter is ignored in the python-oracledb
Thin mode.
The ``ping_interval`` parameter is the length of time (in seconds) after
which an unused connection in the pool will be a candidate for pinging when
:meth:`AsyncConnectionPool.acquire()` is called. If the ping to the
database indicates the connection is not alive a replacement connection
will be returned by :meth:`~AsyncConnectionPool.acquire()`. If
``ping_interval`` is a negative value, then the ping functionality will be
disabled. The default value is 60 seconds.
The ``proxy_user`` parameter is expected to be a string which indicates the
name of the proxy user to connect to. If this value is not specified, it
will be parsed out of user if user is in the form "user[proxy_user]".
The ``newpassword`` parameter is expected to be a string which indicates
the new password for the user. The new password will take effect
immediately upon a successful connection to the database.
The ``wallet_password`` parameter is expected to be a string which
indicates the password to use to decrypt the PEM-encoded wallet, if it is
encrypted.
The ``access_token`` parameter is expected to be a string or a 2-tuple or
a callable. If it is a string, it specifies an Azure AD OAuth2 token used
for Open Authorization (OAuth 2.0) token based authentication. If it is a
2-tuple, it specifies the token and private key strings used for Oracle
Cloud Infrastructure (OCI) Identity and Access Management (IAM) token based
authentication. If it is a callable, it returns either a string or a
2-tuple used for OAuth 2.0 or OCI IAM token based authentication and is
useful when the pool needs to expand and create new connections but the
current authentication token has expired.
The ``host`` parameter is expected to be a string which specifies the name
or IP address of the machine hosting the listener, which handles the
initial connection to the database.
The ``port`` parameter is expected to be an integer which indicates the
port number on which the listener is listening. The default value is 1521.
The ``protocol`` parameter is expected to be one of the strings "tcp" or
"tcps" which indicates whether to use unencrypted network traffic or
encrypted network traffic (TLS). The default value is tcp.
The ``https_proxy`` parameter is expected to be a string which indicates
the name or IP address of a proxy host to use for tunneling secure
connections.
The ``https_proxy_port`` parameter is expected to be an integer which
indicates the port that is to be used to communicate with the proxy host.
The default value is 0.
The ``service_name`` parameter is expected to be a string which indicates
the service name of the database.
The ``sid`` parameter is expected to be a string which indicates the SID of
the database. It is recommended to use ``service_name`` instead.
The ``server_type`` parameter is expected to be a string that indicates the
type of server connection that should be established. If specified, it
should be one of `dedicated`, `shared`, or `pooled`.
The ``cclass`` parameter is expected to be a string that identifies the
connection class to use for Database Resident Connection Pooling (DRCP).
The ``purity`` parameter is expected to be one of the
:ref:`oracledb.PURITY_* <drcppurityconsts>` constants that identifies the
purity to use for DRCP. The purity will internally default to
:data:`~oracledb.PURITY_SELF` for pooled connections.
The ``expire_time`` parameter is expected to be an integer which indicates
the number of minutes between the sending of keepalive probes. If this
parameter is set to a value greater than zero it enables keepalive. The
default value is 0.
The ``retry_count`` parameter is expected to be an integer that identifies
the number of times that a connection attempt should be retried before the
attempt is terminated. The default value is 0.
The ``retry_delay`` parameter is expected to be an integer that identifies
the number of seconds to wait before making a new connection attempt. The
default value is 0.
The ``tcp_connect_timeout`` parameter is expected to be a float that
indicates the maximum number of seconds to wait for establishing a
connection to the database host. The default value is 60.0.
The ``ssl_server_dn_match`` parameter is expected to be a boolean that
indicates whether the server certificate distinguished name (DN) should be
matched in addition to the regular certificate verification that is
performed. Note that if the ``ssl_server_cert_dn`` parameter is not
provided, host name matching is performed instead. The default value is
True.
The ``ssl_server_cert_dn`` parameter is expected to be a string that
indicates the distinguished name (DN) which should be matched with the
server. This value is ignored if the ``ssl_server_dn_match`` parameter is
not set to the value True.
The ``wallet_location`` parameter is expected to be a string that
identifies the directory where the wallet can be found. In python-oracledb
Thin mode, this must be the directory of the PEM-encoded wallet file,
ewallet.pem.
The ``events`` parameter is ignored in the python-oracledb Thin mode.
The ``externalauth`` parameter is ignored in the python-oracledb Thin mode.
If the ``mode`` parameter is specified, it must be one of the
:ref:`connection authorization modes <connection-authorization-modes>`
which are defined at the module level. The default value is
:data:`oracledb.AUTH_MODE_DEFAULT`.
The ``disable_oob`` parameter is expected to be a boolean that indicates
whether out-of-band breaks should be disabled. This value has no effect
on Windows which does not support this functionality. The default value
is False.
The ``stmtcachesize`` parameter is expected to be an integer which
specifies the initial size of the statement cache. The default is the
value of :attr:`defaults.stmtcachesize`.
The ``edition`` parameter is ignored in the python-oracledb Thin mode.
The ``tag`` parameter is ignored in the python-oracledb Thin mode.
The ``matchanytag`` parameter is ignored in the python-oracledb Thin mode.
The ``config_dir`` parameter is expected to be a string that indicates the
directory in which configuration files (tnsnames.ora) are found. The
default is the value of :attr:`defaults.config_dir`.
The ``appcontext`` parameter is ignored in the python-oracledb Thin mode.
The ``shardingkey`` parameter and ``supershardingkey`` parameters are
ignored in the python-oracledb Thin mode.
The ``debug_jdwp`` parameter is expected to be a string with the format
`host=<host>;port=<port>` that specifies the host and port of the PL/SQL
debugger. This allows using the Java Debug Wire Protocol (JDWP) to debug
PL/SQL code invoked by python-oracledb.
The ``connection_id_prefix`` parameter is expected to be a string and is
added to the beginning of the generated ``connection_id`` that is sent to
the database for `tracing <https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-B0FC69F9-2EBC-44E8-ACB2-62FBA14ABD5C>`__.
The ``ssl_context`` parameter is expected to be an SSLContext object used
for connecting to the database using TLS. This SSL context will be
modified to include the private key or any certificates found in a
separately supplied wallet. This parameter should only be specified if
the default SSLContext object cannot be used.
The ``sdu`` parameter is expected to be an integer that returns the
requested size of the Session Data Unit (SDU), in bytes. The value tunes
internal buffers used for communication to the database. Bigger values can
increase throughput for large queries or bulk data loads, but at the cost
of higher memory use. The SDU size that will actually be used is
negotiated down to the lower of this value and the database network SDU
configuration value. See the `SQL*Net documentation <https://www.oracle.
com/pls/topic/lookup?ctx=dblatest&id=GUID-86D61D6F-AD26-421A-BABA-
77949C8A2B04>`__ for more details. The default value is 8192 bytes.
The ``handle`` parameter is ignored in the python-oracledb Thin mode.
.. function:: Cursor(connection)
@ -2232,6 +2729,8 @@ All of these types are extensions to the DB API definition. They are found in
query and object metadata. They can also be used to specify the database type
when binding data.
Also see the table :ref:`supporteddbtypes`.
.. data:: DB_TYPE_BFILE
Describes columns, attributes or array elements in a database that are of

View File

@ -6,7 +6,8 @@ Python-oracledb is the new name for the Python `cx_Oracle driver
an open source module that enables Python programs to access Oracle
Database. It conforms to the `Python Database API v2.0 Specification
<https://www.python.org/dev/peps/pep-0249/>`__ with a considerable number of
additions and a couple of exclusions.
additions and a couple of exclusions. Synchronous and
:ref:`concurrent <asyncio>` coding styles are supported.
This module is currently tested with Python 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12
against Oracle Database 23c, 21c, 19c, 18c, 12c, and 11gR2.
@ -45,9 +46,10 @@ User Guide
user_guide/two_phase_commit.rst
user_guide/startup.rst
user_guide/ha.rst
user_guide/tracing.rst
user_guide/globalization.rst
user_guide/asyncio.rst
user_guide/exception_handling.rst
user_guide/tracing.rst
user_guide/troubleshooting.rst
user_guide/appendix_a.rst
user_guide/appendix_b.rst
@ -74,6 +76,10 @@ API Manual
api_manual/dbobject_type.rst
api_manual/aq.rst
api_manual/soda.rst
api_manual/async_connection.rst
api_manual/async_connection_pool.rst
api_manual/async_cursor.rst
api_manual/async_lob.rst
api_manual/deprecations.rst
.. toctree::

View File

@ -13,7 +13,7 @@ oracledb 2.0.0 (TBD)
Thin Mode Changes
+++++++++++++++++
#) Added support for asyncio
#) Added support for :ref:`concurrent programming with asyncio <asyncio>`
(`issue 6 <https://github.com/oracle/python-oracledb/issues/6>`__).
#) Added parameter :attr:`ConnectParams.sdu` for configuring the Session Data
Unit (SDU) size for sizing internal buffers used for tuning communication
@ -103,7 +103,8 @@ Common Changes
:meth:`Cursor.executemany()` with the parameter ``batcherrors`` set to the
value ``True``. Note that in thick mode this error is not raised unless the
number of batch errors is a multiple of 65536; instead, the number of batch
errors returned is modulo 65536.
errors returned is modulo 65536
(`issue 262 <https://github.com/oracle/python-oracledb/issues/262>`__).
#) Black is now used to format Python code and ruff to lint Python code.

View File

@ -282,6 +282,10 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- No
- Yes
- Yes
* - Concurrent programming with asyncio (see :ref:`asyncio`)
- Yes
- No
- No
* - End-to-end monitoring and tracing attributes (see :ref:`tracingsql`)
- Yes
- Yes
@ -296,7 +300,7 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- Yes
* - Two-phase Commit (TPC)
- No
- Yes - improved support (see :ref:`tcp`)
- Yes - improved support. See :ref:`tcp`.
- Yes - limited support
* - REF CURSORs and Nested Cursors
- Yes
@ -326,62 +330,6 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- Yes
- Yes
- Yes
* - CHAR, VARCHAR2, NUMBER, FLOAT, DATE, and LONG data types
- Yes
- Yes
- Yes
* - BLOB and CLOB data types
- Yes
- Yes
- Yes
* - BINARY_DOUBLE and BINARY_FLOAT data types
- Yes
- Yes
- Yes
* - RAW and LONG RAW data types
- Yes
- Yes
- Yes
* - INTERVAL DAY TO SECOND data type (see :data:`~oracledb.DB_TYPE_INTERVAL_DS`)
- Yes
- Yes
- Yes
* - INTERVAL YEAR TO MONTH data type (see :data:`~oracledb.DB_TYPE_INTERVAL_YM`)
- No
- No
- No
* - Oracle 12c JSON
- Yes
- Yes
- Yes
* - Oracle 21c JSON data type (see :data:`~oracledb.DB_TYPE_JSON`)
- Yes
- Yes
- Yes
* - ROWID, UROWID data types
- Yes
- Yes
- Yes
* - TIMESTAMP, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE data types
- Yes
- Yes
- Yes
* - NCHAR, NVARCHAR2, NCLOB data types
- Yes
- Yes
- Yes
* - PL/SQL data types BOOLEAN, PLS_INTEGER and BINARY_INTEGER
- Yes
- Yes
- Yes
* - XMLType data type (see :ref:`xmldatatype`)
- Yes
- Yes - may need to fetch as CLOB
- Yes - may need to fetch as CLOB
* - BFILE data type (see :data:`~oracledb.DB_TYPE_BFILE`)
- No
- Yes
- Yes
.. _supporteddbtypes:
@ -402,198 +350,133 @@ values.
:header-rows: 1
:class: wy-table-responsive
:align: center
:summary: The first column displays the database data type. The second column displays the python-oracledb constant Name. The third column indicates if the type is supported in python-oracledb.
:summary: The first column displays the database data type. The second column displays the python-oracledb constant Name. The third column contains notes. The fourth column shows Python types that can be used.
* - Oracle Database Type
- python-oracledb Constant Name
- Supported in python-oracledb
- Notes
- Supported Python Types
* - VARCHAR2
- DB_TYPE_VARCHAR
- Yes
- :data:`~oracledb.DB_TYPE_VARCHAR`
-
- bytes, str
* - NVARCHAR2
- DB_TYPE_NVARCHAR
- Yes
- :data:`~oracledb.DB_TYPE_NVARCHAR`
-
- bytes, str
* - NUMBER, FLOAT
- DB_TYPE_NUMBER
- Yes
- :data:`~oracledb.DB_TYPE_NUMBER`
-
- bool, int, float, decimal.Decimal
* - DATE
- DB_TYPE_DATE
- Yes
- :data:`~oracledb.DB_TYPE_DATE`
-
- datetime.date, datetime.datetime
* - BOOLEAN (PL/SQL)
- DB_TYPE_BOOLEAN
- Yes
- ANY (converted to bool)
* - BOOLEAN (PL/SQL and Oracle Database 23c SQL)
- :data:`~oracledb.DB_TYPE_BOOLEAN`
-
- Any type convertible to bool
* - BINARY_DOUBLE
- DB_TYPE_BINARY_DOUBLE
- Yes
- :data:`~oracledb.DB_TYPE_BINARY_DOUBLE`
-
- bool, int, float, decimal.Decimal
* - BINARY_FLOAT
- DB_TYPE_BINARY_FLOAT
- Yes
- :data:`~oracledb.DB_TYPE_BINARY_FLOAT`
-
- bool, int, float, decimal.Decimal
* - TIMESTAMP
- DB_TYPE_TIMESTAMP
- Yes
- :data:`~oracledb.DB_TYPE_TIMESTAMP`
-
- datetime.date, datetime.datetime
* - TIMESTAMP WITH TIME ZONE
- DB_TYPE_TIMESTAMP_TZ
- Yes
- :data:`~oracledb.DB_TYPE_TIMESTAMP_TZ`
-
- datetime.date, datetime.datetime
* - TIMESTAMP WITH LOCAL TIME ZONE
- DB_TYPE_TIMESTAMP_LTZ
- Yes
- :data:`~oracledb.DB_TYPE_TIMESTAMP_LTZ`
-
- datetime.date, datetime.datetime
* - INTERVAL YEAR TO MONTH
- DB_TYPE_INTERVAL_YM
- Not supported in python-oracledb
- cannot be set
- :data:`~oracledb.DB_TYPE_INTERVAL_YM`
- Not supported in python-oracledb.
- Cannot be set
* - INTERVAL DAY TO SECOND
- DB_TYPE_INTERVAL_DS
- Yes
- :data:`~oracledb.DB_TYPE_INTERVAL_DS`
-
- datetime.timedelta
* - RAW
- DB_TYPE_RAW
- Yes
- :data:`~oracledb.DB_TYPE_RAW`
-
- bytes, str
* - LONG
- DB_TYPE_LONG
- Yes
- :data:`~oracledb.DB_TYPE_LONG`
-
- bytes, str
* - LONG RAW
- DB_TYPE_LONG_RAW
- Yes
- :data:`~oracledb.DB_TYPE_LONG_RAW`
-
- bytes, str
* - ROWID
- DB_TYPE_ROWID
- Yes
- :data:`~oracledb.DB_TYPE_ROWID`
-
- bytes, str
* - UROWID
- DB_TYPE_ROWID, DB_TYPE_UROWID (only supported in python-oracledb Thin mode)
- Yes. May show DB_TYPE_UROWID in metadata. See :ref:`Query Metadata Differences <querymetadatadiff>`.
- :data:`~oracledb.DB_TYPE_ROWID`, :data:`~oracledb.DB_TYPE_UROWID` (only supported in python-oracledb Thin mode)
- May show :data:`~oracledb.DB_TYPE_UROWID` in metadata. See :ref:`Query Metadata Differences <querymetadatadiff>`.
- bytes, str
* - CHAR
- DB_TYPE_CHAR
- Yes
- :data:`~oracledb.DB_TYPE_CHAR`
-
- bytes, str
* - BLOB
- DB_TYPE_BLOB
- Yes
- BLOB, bytes, str
- :data:`~oracledb.DB_TYPE_BLOB`
-
- :ref:`oracledb.LOB <lobobj>`, bytes, str
* - CLOB
- DB_TYPE_CLOB
- Yes
- CLOB, bytes, str
- :data:`~oracledb.DB_TYPE_CLOB`
-
- :ref:`oracledb.LOB <lobobj>`, bytes, str
* - NCHAR
- DB_TYPE_NCHAR
- Yes
- :data:`~oracledb.DB_TYPE_NCHAR`
-
- bytes, str
* - NCLOB
- DB_TYPE_NCLOB
- Yes
- NCLOB, bytes, str
- :data:`~oracledb.DB_TYPE_NCLOB`, :data:`~oracledb.DB_TYPE_LONG_NVARCHAR` (if fetching NCLOB as a string)
-
- :ref:`oracledb.LOB <lobobj>`, bytes, str
* - BFILE
- DB_TYPE_BFILE
- Not supported in python-oracledb Thin mode
- cannot be set
- :data:`~oracledb.DB_TYPE_BFILE`
- Not supported in python-oracledb Thin mode.
- Cannot be set
* - JSON
- DB_TYPE_JSON
- Yes
- ANY (converted)
- :data:`~oracledb.DB_TYPE_JSON`
-
- Any type convertible to Oracle JSON
* - REF CURSOR (PL/SQL OR nested cursor)
- DB_TYPE_CURSOR
- Yes
- CURSOR
- :data:`~oracledb.DB_TYPE_CURSOR`
-
- :ref:`oracledb.Cursor <cursorobj>`
* - PLS_INTEGER
- DB_TYPE_BINARY_INTEGER
- Yes
- :data:`~oracledb.DB_TYPE_BINARY_INTEGER`
-
- bool, int, float, decimal.Decimal
* - BINARY_INTEGER
- DB_TYPE_BINARY_INTEGER
- Yes
- :data:`~oracledb.DB_TYPE_BINARY_INTEGER`
-
- bool, int, float, decimal.Decimal
* - REF
- n/a
- Not supported in python-oracledb Thin mode
- n/a
* - XMLType
- DB_TYPE_XMLTYPE
- Yes. May need to use ``xmltype.getclobval()`` to fetch in python-oracledb Thick mode.
- :data:`~oracledb.DB_TYPE_XMLTYPE`
- May need to use ``xmltype.getclobval()`` to fetch in python-oracledb Thick mode. See :ref:`xmldatatype`
- bytes, str
* - User-defined types (object type, VARRAY, records, collections, SDO_*types)
- DB_TYPE_OBJECT
- Yes
- :data:`~oracledb.DB_TYPE_OBJECT`
-
- OBJECT of specific type
Binding of contiguous PL/SQL Index-by BINARY_INTEGER arrays of string, number, and date are
supported in python-oracledb Thin and Thick modes. Use :meth:`Cursor.arrayvar()` to build
these arrays.
.. Python Types supported for each Oracle Database Type are shown below... list-table-with-summary:: Oracle Database Types Supported
:header-rows: 1
:align: center
:summary: The first column displays the Oracle Database type. The second column displays the Python types that are supported for each of the database types.
* - Oracle Database Type
- Python Types supported
* - DB_TYPE_BFILE
- cannot be set
* - DB_TYPE_BINARY_DOUBLE
- bool, int, float, decimal.Decimal
* - DB_TYPE_BINARY_FLOAT
- bool, int, float, decimal.Decimal
* - DB_TYPE_BINARY_INTEGER
- bool, int, float, decimal.Decimal
* - DB_TYPE_BLOB
- BLOB, bytes, str
* - DB_TYPE_BOOLEAN
- ANY (converted to bool)
* - DB_TYPE_CHAR
- bytes, str
* - DB_TYPE_CLOB
- CLOB, bytes, str
* - DB_TYPE_CURSOR
- CURSOR
* - DB_TYPE_DATE
- datetime.date, datetime.datetime
* - DB_TYPE_INTERVAL_DS
- datetime.timedelta
* - DB_TYPE_INTERVAL_YM
- cannot be set
* - DB_TYPE_JSON
- ANY (converted)
* - DB_TYPE_LONG
- bytes, str
* - DB_TYPE_LONG_NVARCHAR
- bytes, str
* - DB_TYPE_LONG_RAW
- bytes, str
* - DB_TYPE_NCHAR
- bytes, str
* - DB_TYPE_NCLOB
- NCLOB, bytes, str
* - DB_TYPE_NUMBER
- bool, int, float, decimal.Decimal
* - DB_TYPE_NVARCHAR
- bytes, str
* - DB_TYPE_OBJECT
- OBJECT of specific type
* - DB_TYPE_RAW
- bytes, str
* - DB_TYPE_ROWID
- bytes, str
* - DB_TYPE_TIMESTAMP
- datetime.date, datetime.datetime
* - DB_TYPE_TIMESTAMP_LTZ
- datetime.date, datetime.datetime
* - DB_TYPE_TIMESTAMP_TZ
- datetime.date, datetime.datetime
* - DB_TYPE_UROWID
- bytes, str
* - DB_TYPE_VARCHAR
- bytes, str

View File

@ -348,8 +348,9 @@ to python-oracledb:
See :ref:`Features Supported <featuresummary>` for details.
- python-oracledb can be used in SQLAlchemy, Django, Pandas, and other
frameworks and Object-relational Mappers (ORMs). Until they add native
support, you can override the use of cx_Oracle with a few lines of code. See
frameworks and Object-relational Mappers (ORMs). To use python-oracledb in
versions of these libraries that don't have native support for the new name,
you can override the use of cx_Oracle with a few lines of code. See
:ref:`frameworks`.
- python-oracledb connection and pool creation calls require keyword arguments
@ -675,9 +676,9 @@ Python Frameworks, SQL Generators, and ORMs
The python-oracledb Thin mode features in the python-oracledb cover the needs
of frameworks that depend upon the Python Database API.
Until SQLAlchemy, Django, other frameworks, object-relational mappers (ORMs),
and libraries add native support for python-oracledb, you can add temporary
code like this to use python-oracledb in-place of cx_Oracle:
For versions of SQLAlchemy, Django, other frameworks, object-relational mappers
(ORMs), and libraries that don't have native support for python-oracledb, you
can add temporary code like this to use python-oracledb in-place of cx_Oracle:
.. code-block:: python
@ -691,3 +692,7 @@ code like this to use python-oracledb in-place of cx_Oracle:
The import of cx_Oracle occurs last. This code must be run before the
library code does its own import of cx_Oracle.
SQLAlchemy 2 and Django 5 have native support for python-oracledb so this code
snippet is not needed in those versions.

View File

@ -0,0 +1,292 @@
.. _asyncio:
***********************************
Concurrent Programming with asyncio
***********************************
The `Asynchronous I/O (asyncio) <https://docs.python.org/3/library/asyncio.
html>`__ Python library can be used with python-oracledb Thin mode for
concurrent programming. This library allows you to run operations in parallel,
for example to run a long-running operation in the background without blocking
the rest of the application. With asyncio, you can easily write concurrent code
with the ``async`` and ``await`` syntax. See Python's `Developing with asyncio
<https://docs.python.org/3/library/asyncio-dev.html>`__ documentation for
useful tips.
.. note::
The asyncio support in python-oracledb 2.0.0 is a pre-release and may
change in the next version.
The python-oracledb asynchronous API is a part of the standard python-oracledb
module. All the synchronous methods that require a round-trip to the database
now have corresponding asynchronous counterparts. You can choose whether to
use the synchronous API or the asynchronous API in your code. It is
recommended to *not* use both at the same time in your application.
The asynchronous API classes are :ref:`AsyncConnection <asyncconnobj>`,
:ref:`AsyncConnectionPool <asyncconnpool>`,
:ref:`AsyncCursor <asynccursorobj>`, and :ref:`AsyncLOB <asynclobobj>`.
.. note::
Concurrent programming with asyncio is only supported in the
python-oracledb Thin mode.
.. _connasync:
Connecting to Oracle Database Asynchronously
============================================
With python-oracledb, you can create an asynchronous connection to Oracle
Database using either :ref:`standalone connections <asyncstandalone>` or
:ref:`pooled connections <asyncconnpool>`. (For discussion of synchronous
programming, see :ref:`connhandling`.)
.. _asyncstandalone:
Standalone Connections
----------------------
Standalone connections are useful for applications that need only a single
connection to a database.
An asynchronous standalone connection can be created by calling the
asynchronous method :meth:`oracledb.connect_async()` which establishes a
connection to the database and returns an :ref:`AsyncConnection Object
<asyncconnobj>`. Once connections are created, all objects created by these
connections follow the asynchronous programming model. Subject to appropriate
use of ``await`` for calls that require a round-trip to the database,
asynchronous connections are used in the same way that synchronous programs use
:ref:`standaloneconnection`.
Asynchronous connections should be released when they are no longer needed to
ensure Oracle Database gracefully cleans up. A preferred method is to use an
asynchronous context manager. For example:
.. code-block:: python
import asyncio
import oracledb
async def main():
async with oracledb.connect_async(user="hr", password=userpwd,
dsn="localhost/orclpdb") as connection:
with connection.cursor() as cursor:
await cursor.execute("select user from dual")
async for result in cursor:
print(result)
asyncio.run(main())
This code ensures that once the block is completed, the connection is closed
and resources are reclaimed by the database. In addition, any attempt to use
the variable ``connection`` outside of the block will fail.
If you do not use a context manager, you should explicitly close connections
when they are no longer needed, for example:
.. code-block:: python
connection = await oracle.connect_async(user="hr", password=userpwd,
dsn="localhost/orclpdb")
cursor = connection.cursor()
await cursor.execute("select user from dual")
async for result in cursor:
print(result)
cursor.close()
await connection.close()
.. _asyncconnpool:
Connection Pools
----------------
Connection pooling allows applications to create and maintain a pool of open
connections to the database. Connection pooling is important for performance
and scalability when applications need to handle a large number of users who do
database work for short periods of time but have relatively long periods when
the connections are not needed. The high availability features of pools also
make small pools useful for applications that want a few connections available
for infrequent use and requires them to be immediately usable when acquired.
An asynchronous connection pool can be created by calling
:meth:`oracledb.create_pool_async()` which returns an :ref:`AsyncConnectionPool
Object <asyncconnpoolobj>`. Note that this method is *synchronous* and does not
use ``await``. Once the pool has been created, your application can get a
connection from it by calling :meth:`AsyncConnectionPool.acquire()`. After
your application has used a connection, it should be released back to the pool
to make it available for other users. This can be done by explicitly closing
the connection or by using an asynchronous context manager, for example:
.. code-block:: python
import asyncio
import oracledb
async def main():
pool = oracle.create_pool_async(user="hr", password=userpwd,
dsn="localhost/orclpdb",
min=1, max=4, increment=1)
async with pool.acquire() as connection:
with connection.cursor() as cursor:
await cursor.execute("select user from dual")
async for result in cursor:
print(result)
await pool.close()
asyncio.run(main())
.. _sqlexecuteasync:
Executing SQL Using Asynchronous Methods
========================================
This section covers executing SQL using the asynchronous programming model.
For discussion of synchronous programming, see :ref:`sqlexecution`.
Your application communicates with Oracle Database by executing SQL
statements. Statements such as queries (statements beginning with SELECT or
WITH), Data Manipulation Language (DML), and Data Definition Language (DDL) are
executed using the asynchronous methods :meth:`AsyncCursor.execute()` or
:meth:`AsyncCursor.executemany()`. Rows can be iterated over, or fetched using
one of the methods :meth:`AsyncCursor.fetchone()`,
:meth:`AsyncCursor.fetchone()`, :meth:`AsyncCursor.fetchmany()`, or
:meth:`AsyncCursor.fetchall()`.
You can also use shortcut methods on the :ref:`asyncconnobj` object such as
:meth:`AsyncConnection.execute()` or
:meth:`AsyncConnection.executemany()`. Rows can be fetched using one of the
shortcut methods :meth:`AsyncConnection.fetchone()`,
:meth:`AsyncConnection.fetchmany()`, or :meth:`AsyncConnection.fetchall()`.
An example of using :meth:`AsyncConnection.fetchall()`:
.. code-block:: python
import asyncio
import oracledb
async def main():
async with oracledb.connect_async(user="hr", password=userpwd,
dsn="localhost/orclpdb") as connection:
res = await connection.fetchall("select * from locations")
print(res)
asyncio.run(main())
An example that uses asyncio for parallelization and shows the execution of
multiple coroutines:
.. code-block:: python
import asyncio
import oracledb
# Number of coroutines to run
CONCURRENCY = 5
# Query the unique session identifier/serial number combination of a connection
SQL = """SELECT UNIQUE CURRENT_TIMESTAMP AS CT, sid||'-'||serial# AS SIDSER
FROM v$session_connect_info
WHERE sid = SYS_CONTEXT('USERENV', 'SID')"""
# Show the unique session identifier/serial number of each connection that the
# pool opens
async def init_session(connection, requested_tag):
res = await connection.fetchone(SQL)
print(res[0].strftime("%H:%M:%S.%f"), '- init_session with SID-SERIAL#', res[1])
# The coroutine simply shows the session identifier/serial number of the
# connection returned by the pool.acquire() call
async def query(pool):
async with pool.acquire() as connection:
await connection.callproc("dbms_session.sleep", [1])
res = await connection.fetchone(SQL)
print(res[0].strftime("%H:%M:%S.%f"), '- query with SID-SERIAL#', res[1])
async def main():
pool = oracledb.create_pool_async(user="hr", password=userpwd,
dsn="localhost/orclpdb",
min=1, max=CONCURRENCY,
session_callback=init_session)
coroutines = [ query(pool) for i in range(CONCURRENCY) ]
await asyncio.gather(*coroutines)
await pool.close()
asyncio.run(main())
When you run this, you will see that multiple connections (identified by the
unique Session Identifier and Serial Number combination) are opened and are
used by ``query()``. For example::
12:09:29.711525 - init_session with SID-SERIAL# 36-38096
12:09:29.909769 - init_session with SID-SERIAL# 33-56225
12:09:30.085537 - init_session with SID-SERIAL# 14-31431
12:09:30.257232 - init_session with SID-SERIAL# 285-40270
12:09:30.434538 - init_session with SID-SERIAL# 282-32608
12:09:30.730166 - query with SID-SERIAL# 36-38096
12:09:30.933957 - query with SID-SERIAL# 33-56225
12:09:31.115008 - query with SID-SERIAL# 14-31431
12:09:31.283593 - query with SID-SERIAL# 285-40270
12:09:31.457474 - query with SID-SERIAL# 282-32608
Your results may vary depending how fast your environment is.
See `async_gather.py <https://github.com/oracle/python-oracledb/tree/main/
samples/async_gather.py>`__ for a runnable example.
.. _txnasync:
Managing Transactions Using Asynchronous Methods
================================================
This section covers managing transactions using the asynchronous programming
model. For discussion of synchronous programming, see :ref:`txnmgmnt`.
When :meth:`AsyncCursor.execute()` or :meth:`AsyncCursor.executemany()`
executes a SQL statement, a transaction is started or continued. By default,
python-oracledb does not commit this transaction to the database. The methods
:meth:`AsyncConnection.commit()` and :meth:`AsyncConnection.rollback()`
methods can be used to explicitly commit or rollback a transaction:
.. code-block:: python
async def main():
async with oracledb.connect_async(user="hr", password=userpwd,
dsn="localhost/orclpdb") as connection:
with connection.cursor as cursor:
await cursor.execute("INSERT INTO mytab (name) VALUES ('John')")
await connection.commit()
When a database connection is closed, such as with
:meth:`AsyncConnection.close()`, or when variables referencing the connection
go out of scope, any uncommitted transaction will be rolled back.
An alternative way to commit is to set the attribute
:attr:`AsyncConnection.autocommit` of the connection to ``True``. This
ensures all :ref:`DML <dml>` statements (INSERT, UPDATE, and so on) are
committed as they are executed.
Note that irrespective of the autocommit value, Oracle Database will always
commit an open transaction when a DDL statement is executed.
When executing multiple DML statements that constitute a single transaction, it
is recommended to use autocommit mode only for the last DML statement in the
sequence of operations. Unnecessarily committing causes extra database load,
and can destroy transactional consistency.

View File

@ -102,6 +102,11 @@ since numeric data is already stored efficiently. Since python-oracledb
allocates memory for each row based on the supplied values, do not oversize
them.
If the size of the buffers allocated for any of the parameters exceeds 2 GB,
you will receive the error ``DPI-1015: array size of <n> is too large``, where
<n> varies with the size of each element being allocated in the buffer. If you
receive this error, decrease the number of elements in the sequence parameters.
Batch Execution of PL/SQL
=========================

View File

@ -16,6 +16,9 @@ Oracle Client libraries are used. See :ref:`enablingthick`. Both modes have
comprehensive functionality supporting the Python Database API v2.0
Specification.
This chapter covers python-oracledb's synchronous programming model. For
discussion of asynchronous programming, see :ref:`asyncio`.
If you intend to use the Thick mode, then you *must* call
:func:`~oracledb.init_oracle_client()` in the application before any standalone
connection or pool is created. The python-oracledb Thick mode loads Oracle

View File

@ -8,6 +8,7 @@ The python-oracledb driver is a Python extension module that enables access to
Oracle Database. It has comprehensive functionality supporting the `Python
Database API v2.0 Specification <https://www.python.org/dev/peps/pep-0249/>`__
with a considerable number of additions and a couple of exclusions.
Synchronous and :ref:`concurrent <asyncio>` coding styles are supported.
The python-oracledb driver is the renamed, major version successor to
`cx_Oracle 8.3 <https://oracle.github.io/python-cx_Oracle/>`__. For upgrade

View File

@ -170,7 +170,7 @@ will show more details. For example:
WRONG WRONG WRONG
end;""")
if cursor.warning.full_code == "DPY-7000":
if cursor.warning and cursor.warning.full_code == "DPY-7000":
print(cursor.warning)
# Get details
@ -178,14 +178,14 @@ will show more details. For example:
select line, position, text
from user_errors
where name = 'BADPROC' and type = 'PROCEDURE'
order by name, type, line, position""")
order by line, position""")
for info in cursor:
print("Error at line {} position {}:\n{}".format(*info))
The output would be::
DPY-7000: creation succeeded with compilation errors
Error at line 3 position 27:
Error at line 3 position 23:
PLS-00103: Encountered the symbol "WRONG" when expecting one of the following:
:= . ( @ % ;

View File

@ -63,6 +63,10 @@ Some general tuning tips are:
* Do not commit or rollback unnecessarily. Use :attr:`Connection.autocommit`
on the last of a sequence of DML statements.
* If Python's Global Interpreter Lock (GIL) is limiting
:ref:`concurrent program performance <asyncio>`, then explore using parallel
Python processes.
.. _tuningfetch:
Tuning Fetch Performance

View File

@ -7,11 +7,11 @@ Managing Transactions
A database transaction is a grouping of SQL statements that make a logical data
change to the database.
When :meth:`Cursor.execute()` executes a SQL statement, a transaction is
started or continued. By default, python-oracledb does not commit this transaction
to the database. The methods :meth:`Connection.commit()` and
:meth:`Connection.rollback()` methods can be used to explicitly commit
or rollback a transaction:
When :meth:`Cursor.execute()` or :meth:`Cursor.executemany()` executes a SQL
statement, a transaction is started or continued. By default, python-oracledb
does not commit this transaction to the database. The methods
:meth:`Connection.commit()` and :meth:`Connection.rollback()` methods can be
used to explicitly commit or rollback a transaction:
.. code-block:: python

View File

@ -1,5 +1,9 @@
## Python-oracledb Examples
This directory contains samples for python-oracledb.
### Basic Examples
1. The schemas and SQL objects that are referenced in the samples can be
created by running the Python script [create_schema.py][1]. The script
requires SYSDBA privileges and will prompt for these credentials as well as
@ -22,6 +26,16 @@ This directory contains samples for python-oracledb.
python drop_schema.py
### Examples in a Container
The [sample_container](./sample_container) directory has a Dockerfile that will
build a container with the samples and a running Oracle Database.
### Notebooks
The [sample_notebooks](./sample_notebooks) directory has Jupyter notebooks with
runnable examples.
[1]: https://github.com/oracle/python-oracledb/blob/main/samples/create_schema.py
[2]: https://github.com/oracle/python-oracledb/blob/main/samples/sample_env.py
[3]: https://github.com/oracle/python-oracledb/blob/main/samples/drop_schema.py

View File

@ -53,7 +53,7 @@ async def init_session(connection, requested_tag):
# The coroutine simply shows the session identifier/serial number of the
# conneciton returned by the pool.acquire() call
# connection returned by the pool.acquire() call
async def query(pool):
async with pool.acquire() as connection:
await connection.callproc("dbms_session.sleep", [1])

View File

@ -13,9 +13,9 @@
# USAGE
#
# Get an Oracle Database container (see
# https://hub.docker.com/r/gvenzl/oracle-xe):
# https://hub.docker.com/r/gvenzl/oracle-free):
#
# podman pull docker.io/gvenzl/oracle-xe:21-slim
# podman pull docker.io/gvenzl/oracle-free
#
# Create a container with the database, Python, python-oracledb and the
# samples. Choose a password for the sample schemas and pass it as an
@ -40,10 +40,12 @@
#
# python bind_insert.py
#
# Use `vim` to edit files, if required.
#
# The database will persist across container shutdowns, but will be deleted
# when the container is deleted.
FROM docker.io/gvenzl/oracle-xe:21-slim
FROM docker.io/gvenzl/oracle-free
USER root
@ -54,12 +56,14 @@ RUN microdnf module disable python36 && \
WORKDIR /samples/
COPY setup.py setup.py
RUN curl -LO https://github.com/oracle/python-oracledb/archive/refs/heads/main.zip && \
unzip main.zip && mv python-oracledb-main/samples/* . && \
unzip main.zip && \
cp python-oracledb-main/samples/sample_container/setup.py . && \
/bin/rm -rf python-oracledb-main/samples/sample_container/ python-oracledb-main/samples/sample_notebooks/ && \
mv python-oracledb-main/samples/* . && \
/bin/rm -rf python-oracledb-main samples main.zip && \
cat create_schema.py >> /samples/setup.py && chown -R oracle.oinstall /samples/
cat create_schema.py >> /samples/setup.py && \
chown -R oracle.oinstall /samples/
USER oracle
@ -72,8 +76,8 @@ ENV PYO_SAMPLES_MAIN_PASSWORD=${PYO_PASSWORD}
ENV PYO_SAMPLES_EDITION_USER=pythoneditions
ENV PYO_SAMPLES_EDITION_PASSWORD=${PYO_PASSWORD}
ENV PYO_SAMPLES_EDITION_NAME=python_e1
ENV PYO_SAMPLES_CONNECT_STRING="localhost/xepdb1"
ENV PYO_SAMPLES_DRCP_CONNECT_STRING="localhost/xepdb1:pooled"
ENV PYO_SAMPLES_CONNECT_STRING="localhost/freepdb1"
ENV PYO_SAMPLES_DRCP_CONNECT_STRING="localhost/freepdb1:pooled"
ENV PYO_SAMPLES_ADMIN_USER=system
# Run the samples using the default python-oracledb 'Thin' mode, if possible

View File

@ -9,10 +9,12 @@ It has been tested in an Oracle Linux 8 environment using 'podman', but
## Usage
- Get an Oracle Database container (see
https://hub.docker.com/r/gvenzl/oracle-xe):
https://hub.docker.com/r/gvenzl/oracle-free):
The steps below use 'podman', but 'docker' will also work.
```
podman pull docker.io/gvenzl/oracle-xe:21-slim
podman pull docker.io/gvenzl/oracle-free
```
- Create a container with the database, Python, python-oracledb and the

4
samples/sample_container/setup.py Normal file → Executable file
View File

@ -29,7 +29,7 @@ for i in range(30):
c = oracledb.connect(
user="system",
password=pw,
dsn="localhost/xepdb1",
dsn="localhost/freepdb1",
tcp_connect_timeout=5,
)
break
@ -63,7 +63,7 @@ cursor.execute("alter database mount")
cursor.execute("alter database open")
c = oracledb.connect(
user="sys", password=pw, dsn="localhost/xepdb1", mode=oracledb.SYSDBA
user="sys", password=pw, dsn="localhost/freepdb1", mode=oracledb.SYSDBA
)
cursor = c.cursor()
cursor.callproc("dbms_connection_pool.start_pool")

View File

@ -0,0 +1,383 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Banner](images/banner.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# python-oracledb Introduction"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This Jupyter Notebook shows how to use [python-oracledb](https://oracle.github.io/python-oracledb/) in its default 'Thin' mode that connects directly to Oracle Database."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# python-oracledb Connection"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Architecture\n",
"\n",
"Documentation reference link: [Introduction to python-oracledb](https://python-oracledb.readthedocs.io/en/latest/user_guide/introduction.html)\n",
"\n",
"![Architecture](images/architecture-thin.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Installation\n",
"\n",
"Documentation reference link: [python-oracledb Installation](https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### **Install python-oracledb**\n",
"\n",
"Install with a command like one of the following:\n",
"\n",
"```\n",
"$ python3 -m pip install oracledb --upgrade\n",
"$ python3 -m pip install oracledb --upgrade --user\n",
"$ python3 -m pip install oracledb --upgrade --user --proxy=http://proxy.example.com:80\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To use python-oracledb, your application code can import the module:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import oracledb"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Connecting to a Database\n",
"\n",
"**Connections are used for executing SQL and PL/SQL in an Oracle Database**\n",
"\n",
"Documentation reference link: [Connecting to Oracle Database](https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Credentials\n",
"un = \"pythondemo\"\n",
"pw = \"welcome\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Instead of hard coding the password, you could prompt for a value, pass it as an environment variable, or use Oracle \"external authentication\"."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Easy Connect Syntax: \"hostname/servicename\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"cs = \"localhost/orclpdb1\"\n",
"\n",
"connection = oracledb.connect(user=un, password=pw, dsn=cs)\n",
"print(connection)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Oracle Client 19c improved [Easy Connect Plus](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-8C85D289-6AF3-41BC-848B-BF39D32648BA) syntax with additional optional settings, for example:\n",
"\n",
"```\n",
"cs = \"tcps://my.cloud.com:1522/orclpdb1?connect_timeout=4&expire_time=10\"\n",
"```\n",
"\n",
"<!-- See the [technical brief](https://download.oracle.com/ocomdocs/global/Oracle-Net-19c-Easy-Connect-Plus.pdf). -->"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Oracle Network and Oracle Client Configuration Files\n",
"\n",
"Oracle Database's `tnsnames.ora` file can be used. This file maps a connect descriptor to an alias. \n",
"\n",
"Documentation reference link: [Optional configuration files](https://python-oracledb.readthedocs.io/en/latest/user_guide/initialization.html#optional-oracle-net-configuration-files)"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"\n",
"# tnsnames.ora in /opt/oracle/configdir\n",
" \n",
"highperfdb = (description= \n",
" (retry_count=5)(retry_delay=3)\n",
" (address=(protocol=tcps)(port=1522)(host=xxxxxx.oraclecloud.com))\n",
" (connect_data=(service_name=yyyyyyyyyy.oraclecloud.com))\n",
" (security=(ssl_server_cert_dn=\n",
" \"CN=zzzzzzzz.oraclecloud.com,OU=Oracle ADB,O=Oracle Corporation,L=Redwood City,ST=California,C=US\")))\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Your Python code could use the alias as the connection `dsn` value:\n",
"```\n",
"connection = oracledb.connect(user=un, password=pw, dsn=\"highperfdb\", config_dir=\"/opt/oracle/configdir\")\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Connection Types"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Standalone Connections\n",
"\n",
"Standalone connections are simple to create."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Stand-alone Connection](images/standalone-connection.png)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Stand-alone Connections\n",
"\n",
"connection = oracledb.connect(user=un, password=pw, dsn=cs)\n",
"\n",
"print(connection)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Pooled Connections"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Pools are highly recommended if you have:\n",
"- a lot of connections that will be used for short periods of time\n",
"- or a small number of connections that are idle for long periods of time\n",
"\n",
"#### Pool advantages\n",
"- Reduced cost of setting up and tearing down connections\n",
"- Dead connection detection and automatic re-establishment"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Pooled connection](images/pooled-connection.png)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Pooled Connections\n",
"\n",
"# Call once during application initization\n",
"pool = oracledb.create_pool(user=un, password=pw, dsn=cs,\n",
" min=1, max=10, increment=1)\n",
"\n",
"# Get a connection when needed in the application body\n",
"with pool.acquire() as connection:\n",
" # do_something_useful(connection)\n",
" print(f\"Got a connection to Oracle Database {connection.version}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Tip** Use a fixed size pool `min` = `max` and `increment = 0`. See [Guideline for Preventing Connection Storms: Use Static Pools](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-7DFBA826-7CC0-4D16-B19C-31D168069B54)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Closing Connections"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Close connections when not needed. This is important for pooled connections.\n",
"\n",
"```\n",
"connection.close()\n",
"```\n",
"\n",
"To avoid resource closing order issues, you may want to use `with` or let resources be closed at end of scope:\n",
"\n",
"```\n",
"with pool.acquire() as connection:\n",
" do_something(connection)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Database Resident Connection Pooling\n",
"\n",
"**Connection pooling on the database tier**\n",
"\n",
"Documentation reference link: [Database Resident Connection Pooling (DRCP)](https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#database-resident-connection-pooling-drcp)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dedicated server processes are the default in the database, but DRCP is an alternative when the database server is short of memory."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![DRCP architecture](images/drcp-architecture.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Use DRCP if and only if:\n",
"- The database computer doesn't have enough memory for all the server processes for all open application connections\n",
"- When you have thousands of users which need access to a database server session for a short period of time\n",
"- Applications mostly use same database credentials, and have identical session settings\n",
"\n",
"Using DRCP in conjunction with a python-oracledb connection pool is recommended."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Memory example with 5000 application users and a DRCP pool of size 100\n",
"![DRCP memory comparison](images/drcp-comparison.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In Python, the connect string must request a pooled server, for example with ':pooled'. For best efficiency to allow database server session re-use, set a connection class and use the purity 'PURITY_SELF'.\n",
"\n",
"```\n",
"pool = oracledb.create_pool(user=un, password=pw, dsn=\"dbhost.example.com/orclpdb1:pooled\",\n",
" cclass=\"MYCLASS\", purity=oracledb.PURITY_SELF)\n",
"\n",
"connection = pool.acquire()\n",
"```\n",
"\n",
"Don't forget to start the pool first!:\n",
"```\n",
"SQL> execute dbms_connection_pool.start_pool()\n",
"```\n",
"\n",
"Note DRCP is pre-enabled on Oracle Autonomous Database."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,527 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Banner](images/banner.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Queries - SELECT and WITH Statements"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import oracledb"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"un = \"pythondemo\"\n",
"pw = \"welcome\"\n",
"\n",
"adminun = \"system\"\n",
"adminpw = \"oracle\"\n",
"\n",
"cs = \"localhost/orclpdb1\"\n",
"\n",
"connection = oracledb.connect(user=un, password=pw, dsn=cs)\n",
"\n",
"systemconnection = oracledb.connect(user=adminun, password=adminpw, dsn=cs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Fetching Rows\n",
"\n",
"Documentation reference link: [Fetch Methods](https://python-oracledb.readthedocs.io/en/latest/user_guide/sql_execution.html#fetch-methods)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Fetching single rows\n",
"\n",
"with connection.cursor() as cursor:\n",
" \n",
" cursor.execute(\"select * from SampleQueryTab where id = :1\", [6])\n",
" \n",
" row = cursor.fetchone()\n",
" print(row)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Fetching all rows from a small table of not too big size\n",
"\n",
"with connection.cursor() as cursor:\n",
" \n",
" cursor.execute(\"select * from SampleQueryTab\")\n",
" \n",
" rows = cursor.fetchall()\n",
" \n",
" for r in rows:\n",
" print(r)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Fetching all rows from a big table\n",
"\n",
"with connection.cursor() as cursor:\n",
" \n",
" cursor.execute(\"select * from SampleQueryTab\") # pretend this is big\n",
" \n",
" while True:\n",
" rows = cursor.fetchmany() # get a batch rows (of size cursor.arraysize=100)\n",
" for r in rows:\n",
" print(r)\n",
" if len(rows) < cursor.arraysize:\n",
" break\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## A key tuning goal is to reduce \"round-trips\"\n",
"\n",
"\n",
"> \"A server round-trip is defined as the trip from the client to the server and back to the client.\"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"![Round-trip](images/roundtrip.png)\n",
"\n",
"Round-trips affect performance and system scalability.\n",
"\n",
"Make every round-trip useful:\n",
"\n",
"- fetch multiple rows at a time\n",
"- insert multiple rows at a time\n",
"- don't select more data than needed\n",
"- don't overuse commit or rollback"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Round-trip comparison - prefetchrows & arraysize\n",
"\n",
"Documentation reference link: [Tuning python-oracledb](https://python-oracledb.readthedocs.io/en/latest/user_guide/tuning.html)\n",
"\n",
"Regardless of which fetch method is used there are two tuning parameters that affect internal buffering of fetched rows. This alters the number of round-trips required to fetch all the query data from the database. These parameters do not affect how, or when, rows are returned to the application itself.\n",
"\n",
"Here's a demo counting round-trips:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"def get_session_id(connection):\n",
" sid, = connection.cursor().execute(\"SELECT sys_context('userenv', 'sid') FROM dual\").fetchone()\n",
" return sid\n",
"\n",
"def get_round_trips(systemconnection, sid):\n",
" sql = \"\"\"SELECT ss.value\n",
" FROM v$sesstat ss, v$statname sn\n",
" WHERE ss.sid = :sid\n",
" AND ss.statistic# = sn.statistic#\n",
" AND sn.name LIKE '%roundtrip%client%'\"\"\" \n",
" roundtrips, = systemconnection.cursor().execute(sql, [sid]).fetchone()\n",
" return roundtrips\n",
"\n",
"sid = get_session_id(connection)\n",
"\n",
"def do_query(connection, numrows, prefetchrows, arraysize):\n",
"\n",
" roundtrips = get_round_trips(systemconnection, sid)\n",
"\n",
" with connection.cursor() as cursor:\n",
" cursor.prefetchrows = prefetchrows\n",
" cursor.arraysize = arraysize\n",
" cursor.execute(\"select * from all_objects where rownum <= :nr\", [numrows])\n",
" rows = cursor.fetchall()\n",
"\n",
" roundtrips = get_round_trips(systemconnection, sid) - roundtrips\n",
"\n",
" print(\"Number of rows: {:5}, prefetchrows {:4} arraysize {:4} roundtrips {:3} \".format(len(rows), prefetchrows, arraysize, roundtrips))\n",
"\n",
"\n",
"# do_query(connection, number of rows, prefetchrows, arraysize)\n",
"\n",
"print(\"Default prefetch & arraysize values:\")\n",
"do_query(connection, 1, 2, 100)\n",
"do_query(connection, 100, 2, 100)\n",
"do_query(connection, 1000, 2, 100)\n",
"do_query(connection, 10000, 2, 100)\n",
"\n",
"print(\"\\n'Unknown' large number of rows:\")\n",
"do_query(connection, 10000, 2, 1000)\n",
"do_query(connection, 10000, 1000, 1000)\n",
"\n",
"print(\"\\n'Page' of rows:\")\n",
"do_query(connection, 20, 20, 20)\n",
"do_query(connection, 20, 21, 20)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Your tuning goal is to reduce round-trips (thus improving performance) without using too much internal buffer memory.\n",
"\n",
"When selecting a huge number of rows, tuning `arraysize` is important. In general there is no need to set `prefetchrows` in this scenario.\n",
"\n",
"When selecting a known, small number of rows such as for 'paging' through a result set, then set `prefetchrows` to be one larger than the number of rows returned."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Querying LOB Columns\n",
"\n",
"Documentation reference link: [Using CLOB and BLOB Data](https://python-oracledb.readthedocs.io/en/latest/user_guide/lob_data.html)\n",
"\n",
"When fetching LOBs there are two modes that can be used:\n",
"- Fetching as a \"locator\" for streaming use\n",
"- Fetching directly as a string or buffer"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Fetching LOBs for streaming\n",
"\n",
"- this is the default\n",
"- it requires more round trips and has more overhead\n",
"- it is useful when very long LOBs are being used"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
"\n",
" id = 1\n",
" textData = \"The quick brown fox jumps over the lazy dog\"\n",
" cursor.execute(\"truncate table TestClobs\")\n",
" cursor.execute(\"insert into TestClobs (IntCol, ClobCol) values (:1, :2)\", [id, textData])\n",
"\n",
" cursor.execute(\"select ClobCol from TestClobs where IntCol = :ic1\", [id])\n",
" c, = cursor.fetchone()\n",
" print(\"CLOB length:\", c.size())\n",
" print(\"CLOB data:\", c.read())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Fetching CLOBs as String\n",
"\n",
"- this is much faster than the streaming method\n",
"- LOBs are limited to 1 GB (and obviously must all be in memory at once)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"oracledb.defaults.fetch_lobs = False\n",
"\n",
"with connection.cursor() as cursor:\n",
" id = 1\n",
" cursor.execute(\"select ClobCol from TestClobs where IntCol = :ic2\", [id])\n",
" c, = cursor.fetchone()\n",
"\n",
" print(\"CLOB length:\", len(c))\n",
" print(\"CLOB data:\", c)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The same `defaults` setting will return BLOBs as buffers."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sample benchmark\n",
"A sample benchmark shows the performance benefit of querying using the direct String method compared with using the default locator stream method. \n",
"![LOB Benchmark](images/lob-benchmark.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Fetching Numbers as Decimals\n",
"\n",
"Oracle's NUMBER format is Decimal but Python uses floating point so there can be conversion artifacts.\n",
"\n",
"Documentation reference link: [Fetched Number Precision](https://python-oracledb.readthedocs.io/en/latest/user_guide/sql_execution.html#fetched-number-precision)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" cursor.execute(\"select 0.1 as d1 from dual\")\n",
" v, = cursor.fetchone()\n",
" print('Value =', v, '\\tValue * 3 =', v * 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Fetching as Decimal may be useful."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import decimal\n",
"\n",
"oracledb.defaults.fetch_decimals = True\n",
"\n",
"with connection.cursor() as cursor:\n",
" cursor.execute(\"select 0.1 as d2 from dual\")\n",
" v, = cursor.fetchone()\n",
" print('Value =', v, '\\tValue * 3 =', v * 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Multiple Values in \"WHERE ... IN\" Clauses\n",
"\n",
"Documentation reference link: [Binding Multiple Values to a SQL WHERE IN Clause](https://python-oracledb.readthedocs.io/en/latest/user_guide/bind.html#binding-multiple-values-to-a-sql-where-in-clause)\n",
"\n",
"### A fixed number of binds\n",
"\n",
"It's important to use bind variables for security. For small numbers of binds this is simple. One placeholder must be used for each value in the IN clause."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sql = \"\"\"select name\n",
" from SampleQueryTab\n",
" where id in (:id1, :id2, :id3, :id4, :id5)\n",
" order by id\"\"\"\n",
"\n",
"with connection.cursor() as cursor:\n",
" cursor.execute(sql,\n",
" id1=5, id2=7, id3=1, id4=3, id5=2)\n",
" for row, in cursor:\n",
" print(row) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If statements are re-executed but the number of values varies, then pass None (i.e. NULL) for 'missing' values. This lets the database execute the same statement text as before, which helps performance and scalability. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" cursor.execute(sql,\n",
" id1=2, id2=4, id3=1, id4=None, id5=None)\n",
" for row, in cursor:\n",
" print(row) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When using character data and the size of the values varies between statement execution, then using `setinputsizes()` with the column size or maximum expected input data size can help reduce the number of SQL 'versions' of the statement used by the database optimizer, although this is not often an issue and it's not uncommon to see SQL statements with tens or low hundreds of versions.\n",
"\n",
"For example with the query:\n",
"```\n",
"sql = \"\"\"select name\n",
" from SampleQueryTab\n",
" where name in (:n1, :n2, :n3, :n4, :n5)\n",
" order by id\"\"\"\n",
"```\n",
"the statement\n",
"```\n",
"cursor.setinputsizes(n1=20, n2=20, n3=20, n4=20, n5=20)\n",
"```\n",
"\n",
"indicates that each value bound will be a string of no more than 20 characters."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sql = \"\"\"select name\n",
" from SampleQueryTab\n",
" where name in (:n1, :n2, :n3, :n4, :n5)\n",
" order by id\"\"\"\n",
"\n",
"with connection.cursor() as cursor:\n",
" cursor.setinputsizes(n1=20, n2=20, n3=20, n4=20, n5=20)\n",
" cursor.execute(sql,\n",
" n1='Anthony', n2='Barbie', n3='Bogus', n4=None, n5=None)\n",
" for row, in cursor:\n",
" print(row) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dynamically creating the statement\n",
"\n",
"If the number of bind values is not known, and the statement is never rexecuted, you can consider dynamically creating a statement."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"bind_values = [5, 7, 1, 3, 2, 6]\n",
"bind_names = [\":\" + str(i + 1) for i in range(len(bind_values))]\n",
"sql = \"\"\"select name from SampleQueryTab where id in (%s)\"\"\" % (\",\".join(bind_names))\n",
"print(sql, \"\\n\")\n",
"with connection.cursor() as cursor:\n",
" cursor.execute(sql, bind_values)\n",
" for row, in cursor:\n",
" print(row)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Binding using an Oracle type \n",
"\n",
"One solution when matching a huge number of values is to use the SQL `table()` clause and an Oracle type. This works for up to 32K values."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"type_obj = connection.gettype(\"SYS.ODCINUMBERLIST\")\n",
"\n",
"with connection.cursor() as cursor:\n",
" obj = type_obj.newobject()\n",
" obj.extend([3, 4, 7])\n",
" cursor.execute(\"\"\"select name\n",
" from SampleQueryTab\n",
" where id in (select * from table(:1))\"\"\",\n",
" [obj])\n",
" for row, in cursor:\n",
" print(row)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The presupplied types `SYS.ODCIVARCHAR2LIST` or `SYS.ODCIDATELIST` can similarly be used for VARCHAR2 and DATE, respectively."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Accessing object type information takes a few round-trips so the earlier methods are better for small numbers of values. If you use this solution in a more complex statement, check the optimizer plan is still OK. "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,316 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Banner](images/banner.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# DML - INSERT, UPDATE, DELETE, and MERGE Statements"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import oracledb"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"un = \"pythondemo\"\n",
"pw = \"welcome\"\n",
"cs = \"localhost/orclpdb1\"\n",
"\n",
"connection = oracledb.connect(user=un, password=pw, dsn=cs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cursor = connection.cursor()\n",
"cursor.execute(\"drop table mytab\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cursor.execute(\"create table mytab (id number, data varchar2(1000))\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Binding for Insertion\n",
"\n",
"Documentation reference link: [Using Bind Variables](https://python-oracledb.readthedocs.io/en/latest/user_guide/bind.html)\n",
"\n",
"Binding is very, very important. It:\n",
"- eliminates escaping special characters and helps prevent SQL injection attacks\n",
"- is important for performance and scalability"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" cursor.execute(\"truncate table mytab\")\n",
"\n",
" sql = \"insert into mytab (id, data) values (:idVal, :dataVal)\"\n",
"\n",
" # bind by position using a sequence (list or tuple)\n",
" cursor.execute(sql, [1, \"String 1\"])\n",
" cursor.execute(sql, (2, \"String 2\"))\n",
"\n",
" # bind by name using a dictionary\n",
" cursor = connection.cursor()\n",
" cursor.execute(sql, {\"idVal\": 3, \"dataVal\": \"String 3\"})\n",
"\n",
" # bind by name using keyword arguments\n",
" cursor.execute(sql, idVal=4, dataVal=\"String 4\")\n",
"\n",
" print(\"Done\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Batch execution - Inserting multiple rows with executemany()\n",
"\n",
"Documentation reference link: [Executing Batch Statements and Bulk Loading](https://python-oracledb.readthedocs.io/en/latest/user_guide/batch_statement.html)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" cursor.execute(\"truncate table mytab\")\n",
"\n",
" rows = [ (1, \"First\" ),\n",
" (2, \"Second\" ),\n",
" (3, \"Third\" ),\n",
" (4, \"Fourth\" ),\n",
" (5, \"Fifth\" ),\n",
" (6, \"Sixth\" ),\n",
" (7, \"Seventh\" ) ]\n",
"\n",
" # Using setinputsizes helps avoid memory reallocations.\n",
" # The parameters correspond to the insert columns. \n",
" # The value None says use python-oracledb's default size for a NUMBER column. \n",
" # The second value is the maximum input data (or column) width for the VARCHAR2 column\n",
" cursor.setinputsizes(None, 7)\n",
"\n",
" cursor.executemany(\"insert into mytab(id, data) values (:1, :2)\", rows)\n",
"\n",
" # Now query the results back\n",
"\n",
" for row in cursor.execute('select * from mytab'):\n",
" print(row)\n",
"\n",
" connection.rollback()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Benchmark - executemany() vs execute()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import time\n",
"\n",
"cursor = connection.cursor()\n",
"cursor.execute(\"truncate table mytab\")\n",
"\n",
"# Row counts to test inserting\n",
"numrows = (1, 5, 10, 100, 1000)\n",
"\n",
"longstring = \"x\" * 1000\n",
"\n",
"def create_data(n):\n",
" d = []\n",
" for i in range(n):\n",
" d.append((i, longstring))\n",
" return d\n",
"\n",
"ex = [] # seconds for execute() loop\n",
"em = [] # seconds for executemany()\n",
"\n",
"for n in numrows:\n",
" \n",
" rows = create_data(n)\n",
" \n",
" ############################################################\n",
" #\n",
" # Loop over each row\n",
" #\n",
"\n",
" start=time.time()\n",
"\n",
" for r in rows:\n",
" cursor.execute(\"insert into mytab(id, data) values (:1, :2)\", r) # <==== Loop over execute()\n",
" \n",
" elapsed = time.time() - start\n",
" ex.append(elapsed)\n",
" \n",
" r, = cursor.execute(\"select count(*) from mytab\").fetchone()\n",
" print(\"execute() loop {:6d} rows in {:06.4f} seconds\".format(r, elapsed)) \n",
" connection.rollback()\n",
" \n",
" ############################################################# \n",
" #\n",
" # Insert all rows in one call\n",
" #\n",
"\n",
" start = time.time()\n",
"\n",
" cursor.executemany(\"insert into mytab(id, data) values (:1, :2)\", rows) # <==== One executemany()\n",
" \n",
" elapsed = time.time() - start\n",
" em.append(elapsed)\n",
" \n",
" r, = cursor.execute(\"select count(*) from mytab\").fetchone()\n",
" print(\"executemany() {:6d} rows in {:06.4f} seconds\".format(r, elapsed)) \n",
" connection.rollback()\n",
"\n",
"\n",
"print(\"Plot is:\")\n",
"plt.xticks(numrows)\n",
"plt.plot(numrows, ex, label=\"execute() loop\", marker=\"o\")\n",
"plt.plot(numrows, em, label=\"one executemany()\", marker=\"o\")\n",
"plt.xscale(\"log\")\n",
"plt.xlabel('Number of rows')\n",
"plt.ylabel('Seconds')\n",
"plt.legend(loc=\"upper left\")\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Noisy Data - Batch Errors\n",
"\n",
"Dealing with bad data is easy with the `batcherrors` parameter."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# Initial data\n",
"\n",
"with connection.cursor() as cursor:\n",
"\n",
" for row in cursor.execute(\"select * from ParentTable order by ParentId\"):\n",
" print(row)\n",
"\n",
" for row in cursor.execute(\"select * from ChildTable order by ChildId\"):\n",
" print(row)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"dataToInsert = [\n",
" (1016, 10, 'Child Red'),\n",
" (1018, 20, 'Child Blue'),\n",
" (1018, 30, 'Child Green'), # duplicate key\n",
" (1022, 40, 'Child Yellow'),\n",
" (1021, 75, 'Child Orange') # parent does not exist\n",
"]\n",
"\n",
"with connection.cursor() as cursor:\n",
" \n",
" cursor.executemany(\"insert into ChildTable values (:1, :2, :3)\", dataToInsert, batcherrors=True)\n",
" \n",
" print(\"\\nErrors in rows that were not inserted:\\n\")\n",
" for error in cursor.getbatcherrors():\n",
" print(\"Error\", error.message, \"at row offset\", error.offset) \n",
" \n",
" print(\"\\nRows that were successfully inserted:\\n\")\n",
" for row in cursor.execute(\"select * from ChildTable order by ChildId\"):\n",
" print(row)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now you can choose whether or not to fix failed records and reinsert them.\n",
"You can then rollback or commit.\n",
"\n",
"This is true even if you had enabled autocommit mode - no commit will occur if there are batch errors."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"connection.rollback()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,261 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "04c694cd",
"metadata": {},
"source": [
"![Banner](images/banner.png)"
]
},
{
"cell_type": "markdown",
"id": "b739ae65",
"metadata": {},
"source": [
"# Loading and Unloading Data: Working with Comma Separated Values (CSV) files"
]
},
{
"cell_type": "markdown",
"id": "2b027728",
"metadata": {},
"source": [
"CSV is not a well-defined standard! "
]
},
{
"cell_type": "markdown",
"id": "581e9486",
"metadata": {},
"source": [
"\"Unhelpful\" (that's a joke) suggestions for Python programmers:\n",
"- Don't use CSV files: Keep the data in the database.\n",
"- Don't use Excel - use Oracle APEX\n",
"- Use Oracle Data Pump to load CSV files into Oracle Database\n",
"<hr>"
]
},
{
"cell_type": "markdown",
"id": "6cb0a244",
"metadata": {},
"source": [
"Helpful suggestions:\n",
"- Python's [\"csv\" module](https://docs.python.org/3/library/csv.html) has extensive reading and writing support"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9cda404e",
"metadata": {},
"outputs": [],
"source": [
"import oracledb"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1d9c084a",
"metadata": {},
"outputs": [],
"source": [
"un = \"pythondemo\"\n",
"pw = \"welcome\"\n",
"cs = \"localhost/orclpdb1\"\n",
"\n",
"connection = oracledb.connect(user=un, password=pw, dsn=cs)"
]
},
{
"cell_type": "markdown",
"id": "3ae66a62",
"metadata": {},
"source": [
"## Reading CSV Files and Inserting Data into Oracle Database\n",
"\n",
"Set up the schema:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ed1517dd",
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" try:\n",
" cursor.execute(\"drop table t\")\n",
" except:\n",
" ;\n",
"\n",
" cursor.execute(\"\"\"create table t (k number, \n",
" first_name varchar2(30), \n",
" last_name varchar2(30), \n",
" country varchar2(30))\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "8dba72c1",
"metadata": {},
"source": [
"Data in the external CSV file looks like:\n",
"```\n",
"1,Fred,Nurke,UK\n",
"2,Henry,Crun,UK\n",
"```\n",
"\n",
"The Python csv module has extensive functionality. One sample is shown below. For python-oracledb users the important points are to use `executemany()` and send batches of rows to the database. Tuning in your environment will determine the best batch size."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bace97d8",
"metadata": {},
"outputs": [],
"source": [
"import csv\n",
"\n",
"# The batch size determines how many records are inserted at a time.\n",
"# Adjust the size to meet your memory and performance requirements.\n",
"batch_size = 10000\n",
"\n",
"with connection.cursor() as cursor:\n",
" \n",
" sql = \"insert into t (k, first_name, last_name, country) values (:1, :2, :3, :4)\"\n",
" \n",
" # Predefine memory areas to match the table definition (or max data) to avoid memory re-allocs\n",
" cursor.setinputsizes(None, 30, 30, 30)\n",
"\n",
" with open(\"csv/data1.csv\", \"r\") as csv_file:\n",
" csv_reader = csv.reader(csv_file, delimiter=',')\n",
" data = []\n",
" for line in csv_reader:\n",
" data.append((line[0], line[1], line[2], line[3])) # e.g [('1', 'Fred', 'Nurke', 'UK')]\n",
" if len(data) % batch_size == 0:\n",
" cursor.executemany(sql, data)\n",
" data = []\n",
" if data:\n",
" cursor.executemany(sql, data)\n",
" connection.commit()\n",
"\n",
"print(\"Done\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4a0b4215",
"metadata": {},
"outputs": [],
"source": [
"# Check the results\n",
"\n",
"with connection.cursor() as cursor:\n",
" sql = \"select * from t order by k\"\n",
" for r in cursor.execute(sql):\n",
" print(r)"
]
},
{
"cell_type": "markdown",
"id": "7ff76bf5",
"metadata": {},
"source": [
"Tuning database features may also be beneficial. For example, disabling logging and/or indexes."
]
},
{
"cell_type": "markdown",
"id": "08f9bc4e",
"metadata": {},
"source": [
"## Writing CSV Files from Queried Data\n",
"\n",
"This example shows just one way to write CSV files. The important point for python-oracledb users is to tune `cursor.arraysize` for your data and network."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bbc9db48",
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"\n",
"sql = \"select * from all_objects where rownum <= 10000\"\n",
"\n",
"with connection.cursor() as cursor:\n",
"\n",
" start = time.time()\n",
"\n",
" cursor.arraysize = 1000\n",
"\n",
" with open(\"testwrite.csv\", \"w\", encoding=\"utf-8\") as outputfile:\n",
" writer = csv.writer(outputfile, lineterminator=\"\\n\")\n",
" results = cursor.execute(sql)\n",
" writer.writerows(results)\n",
"\n",
" elapsed = time.time() - start\n",
" print(\"Writing CSV: 10000 rows in {:06.4f} seconds\".format(elapsed)) "
]
},
{
"cell_type": "markdown",
"id": "7b3ddf5e",
"metadata": {},
"source": [
"If you change the arraysize and rerun the cell, the time taken may vary."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "62a7ecfe",
"metadata": {},
"outputs": [],
"source": [
"# Confirm the number of lines in the output file is correct\n",
"\n",
"import os\n",
"\n",
"r = os.system(\"wc -l testwrite.csv\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e812be8c-339e-4f14-9b5b-1ea5163cee93",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -0,0 +1,206 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "a9825ce4",
"metadata": {},
"source": [
"![Banner](images/banner.png)"
]
},
{
"cell_type": "markdown",
"id": "7b00c7cf",
"metadata": {},
"source": [
"# Working with JSON Data\n",
"\n",
"Documentation reference link: [Using JSON Data](https://python-oracledb.readthedocs.io/en/latest/user_guide/json_data_type.html)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4933ee95",
"metadata": {},
"outputs": [],
"source": [
"import oracledb"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1e091ca8",
"metadata": {},
"outputs": [],
"source": [
"un = \"pythondemo\"\n",
"pw = \"welcome\"\n",
"cs = \"localhost/orclpdb1\"\n",
"\n",
"connection = oracledb.connect(user=un, password=pw, dsn=cs)"
]
},
{
"cell_type": "markdown",
"id": "a81ba26a",
"metadata": {},
"source": [
"### JSON Storage:\n",
"\n",
"- Oracle Database 12c introduced JSON stored as a LOB or VARCHAR2\n",
"\n",
"- Oracle Database 21c a new optimized native binary format and a dedicated JSON type\n",
"\n",
"**Careful coding is required for apps that run in a mixed version environment**\n",
"\n",
"The first JSON example assumes you are Oracle Database 21c or later.\n",
"\n",
"Setup the schema:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "caa04809",
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" try:\n",
" cursor.execute(\"drop table customers\")\n",
" except:\n",
" ;\n",
" cursor.execute(\"create table customers (k number, json_data json)\")"
]
},
{
"cell_type": "markdown",
"id": "a84dfaeb",
"metadata": {},
"source": [
"With Oracle Database 21c or later, you can bind Python objects directly to the JSON column:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e14826f4",
"metadata": {},
"outputs": [],
"source": [
"import datetime\n",
"\n",
"json_data = [\n",
" 2.78,\n",
" True,\n",
" 'Ocean Beach',\n",
" b'Some bytes',\n",
" {'keyA': 1, 'KeyB': 'Melbourne'},\n",
" datetime.date.today()\n",
"]\n",
"\n",
"with connection.cursor() as cursor:\n",
" cursor.setinputsizes(oracledb.DB_TYPE_JSON)\n",
" cursor.execute(\"insert into customers (k, json_data) values (1, :jbv)\", [json_data])\n",
" \n",
"print(\"Done\")"
]
},
{
"cell_type": "markdown",
"id": "c21d71e3",
"metadata": {},
"source": [
"Querying returns the JSON in a familiar Python data structure:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d84a494d",
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" for row, in cursor.execute(\"select c.json_data from customers c where k = 1\"):\n",
" print(row)\n",
" \n",
"# With Oracle Database 21c or later, this gives:\n",
"# [Decimal('2.78'), True, 'Ocean Beach', b'Some bytes', {'keyA': Decimal('1'), 'KeyB': 'Melbourne'}, datetime.datetime(2022, 3, 4, 0, 0)]"
]
},
{
"cell_type": "markdown",
"id": "e18fc3b4",
"metadata": {},
"source": [
"If you don't have a recent database, then you can still easily work with JSON. Store it using BLOB and work with JSON strings. The Python \"json\" package can be used with many Python types:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a54a7bca-c8d9-4f47-89a2-edaf6ada0a44",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"with connection.cursor() as cursor:\n",
" try:\n",
" cursor.execute(\"drop table customersblob\")\n",
" except:\n",
" ;\n",
" cursor.execute(\"\"\"create table customersblob (k number, \n",
" json_data blob check (json_data is json)) \n",
" lob (json_data) store as (cache)\"\"\")\n",
" \n",
"# INSERT\n",
"\n",
"with connection.cursor() as cursor:\n",
" data = json_data = [\n",
" 2.78,\n",
" True,\n",
" 'Ocean Beach',\n",
" {'keyA': 1, 'KeyB': 'Melbourne'},\n",
" ]\n",
" cursor.execute(\"insert into customersblob (k, json_data) values (2, :jbv)\", [json.dumps(data)])\n",
" \n",
"# FETCH\n",
"\n",
"# Allow the BLOB column to be automatically recognized as storing JSON and not as storing arbitrary binary data.\n",
"# Without this, you would need to use the Python json package on the returned row.\n",
"oracledb.__future__.old_json_col_as_obj = True\n",
"\n",
"with connection.cursor() as cursor:\n",
" for row, in cursor.execute(\"SELECT c.json_data FROM customersblob c where k = 2\"):\n",
" print(row)\n",
" \n",
"connection.rollback() "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -0,0 +1,293 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Banner](images/banner.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Calling PL/SQL\n",
"\n",
"Documentation reference link: [Executing PL/SQL](https://python-oracledb.readthedocs.io/en/latest/user_guide/plsql_execution.html)\n",
"\n",
"PL/SQL is a 'stored' procedural language that is stored and run inside the database itself. PL/SQL lets you capture business logic for reuse across all your applications. You can call stored procedures and functions easily from python-oracledb."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import oracledb"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"un = \"pythondemo\"\n",
"pw = \"welcome\"\n",
"cs = \"localhost/orclpdb1\"\n",
"\n",
"connection = oracledb.connect(user=un, password=pw, dsn=cs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## PL/SQL Procedures\n",
"\n",
"This shows the PL/SQL procedure `MYPROC` used in this demo:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" \n",
" cursor.execute(\"select dbms_metadata.get_ddl('PROCEDURE', 'MYPROC') from dual\")\n",
" ddl, = cursor.fetchone()\n",
" print(ddl.read())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can use `callproc()` to call the procedure. Bind variables are passed by position:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
"\n",
" myinvar = 22\n",
" myoutvar = cursor.var(int) # allocate a 'variable' of integer type to hold the OUT bind parameter\n",
"\n",
" cursor.callproc('myproc', [myinvar, myoutvar])\n",
" print(myoutvar.getvalue())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can also call PL/SQL procedures via an 'anonymous' PL/SQL block. This can be useful if you want to use named bind placeholders:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
"\n",
" myinvar = 33\n",
" myoutvar = cursor.var(int)\n",
"\n",
" cursor.execute(' begin myproc(:v1, :v2); end;', {\"v1\": myinvar, \"v2\": myoutvar})\n",
" print(myoutvar.getvalue())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## PL/SQL Functions\n",
"\n",
"This shows the PL/SQL function `MYFUNC` used in this demo:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" \n",
" cursor.execute(\"select dbms_metadata.get_ddl('FUNCTION', 'MYFUNC') from dual\")\n",
" ddl, = cursor.fetchone()\n",
" print(ddl.read())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can use `callfunc()` to call the function. Bind variables are passed by position. The second argument to `callfunc()` is the type of the PL/SQL function return value. Here it is an integer:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
"\n",
" data = \"abc\"\n",
" id = 3\n",
" res = cursor.callfunc('myfunc', int, (data, id))\n",
" print(res)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Similar to calling PL/SQL procedures, you can also invoke PL/SQL procedures via an anonymous block, and optionally used named bind placeholders:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
"\n",
" data = \"def\"\n",
" id = 4\n",
" ret = cursor.var(int)\n",
"\n",
" cursor.execute(' begin :ret := myfunc(:data, :id); end;', {\"ret\": ret, \"data\": data, \"id\": id})\n",
" print(ret.getvalue())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## REF CURSORS\n",
"\n",
"REF CURSORS let result sets be returned to python-oracledb, commonly from PL/SQL.\n",
"\n",
"Here is the PL/SQL procedure used in this example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
"\n",
" cursor.execute(\"\"\"select text from all_source \n",
" where name = 'MYREFCURSORPROC' and type = 'PROCEDURE' \n",
" order by line\"\"\")\n",
" rows = cursor.fetchall()\n",
" for r, in rows:\n",
" print(r, end=\"\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Use `callproc()` as shown before to call the PL/SQL procedure. The `ref_cursor` variable needs to be defined as a cursor so it can hold the returned REF CURSOR. This second cursor is then simply iterated over exactly like a cursor for simple SELECT would be:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" ref_cursor = connection.cursor()\n",
"\n",
" cursor.callproc(\"myrefcursorproc\", (2, 6, ref_cursor))\n",
"\n",
" print(\"Rows between 2 and 6:\")\n",
" for row in ref_cursor:\n",
" print(row)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Implicit Cursors\n",
"\n",
"Instead of binding a cursor to get a REF CURSOR, the `dbms_sql.return_result()` procedure can alternatively return a result set back which is fetched in python-oracledb using `getimplicitresults()`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
"\n",
" cursor.execute(\"\"\"\n",
" declare\n",
" c1 sys_refcursor;\n",
" c2 sys_refcursor;\n",
" begin\n",
" open c1 for\n",
" select * from ParentTable;\n",
" dbms_sql.return_result(c1);\n",
"\n",
" open c2 for\n",
" select * from ChildTable;\n",
" dbms_sql.return_result(c2);\n",
" end;\"\"\")\n",
"\n",
" for resultSet in cursor.getimplicitresults():\n",
" print(\"Result Set:\")\n",
" for row in resultSet:\n",
" print(row)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,392 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Banner](images/banner.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Oracle Database Objects and Collections\n",
"\n",
"Documentation reference link: [Fetching Oracle Database Objects and Collections](https://python-oracledb.readthedocs.io/en/latest/user_guide/sql_execution.html#fetching-oracle-database-objects-and-collections)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import oracledb"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"un = \"pythondemo\"\n",
"pw = \"welcome\"\n",
"cs = \"localhost/orclpdb1\"\n",
"\n",
"connection = oracledb.connect(user=un, password=pw, dsn=cs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Binding Named Objects\n",
"\n",
"Create a demonstration table. This table uses the predefined SDO_GEOMETRY object which stores spatial information:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" try:\n",
" cursor.execute(\"drop table TestGeometry\")\n",
" except:\n",
" ;\n",
" \n",
" cursor.execute(\"\"\"create table TestGeometry (\n",
" IntCol number(9) not null,\n",
" Geometry sdo_geometry not null)\"\"\")\n",
" \n",
"print(\"Done\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using python-oracledb functions like `gettype()` and `extend()` you can create a Python representation of the database object:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" \n",
" typeObj = connection.gettype(\"SDO_GEOMETRY\")\n",
" elementInfoTypeObj = connection.gettype(\"SDO_ELEM_INFO_ARRAY\")\n",
" ordinateTypeObj = connection.gettype(\"SDO_ORDINATE_ARRAY\")\n",
"\n",
" obj = typeObj() # Alternatively use 'obj = typeObj.newobject()''\n",
" obj.SDO_GTYPE = 2003\n",
" obj.SDO_ELEM_INFO = elementInfoTypeObj()\n",
" obj.SDO_ELEM_INFO.extend([1, 1003, 3])\n",
" obj.SDO_ORDINATES = ordinateTypeObj()\n",
" obj.SDO_ORDINATES.extend([1, 1, 5, 7])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Calling `gettype()` requires multiple round-trips to the database, so avoid calling it unnecessarily."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The new object can be bound directly for insertion:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" cursor.execute(\"insert into TestGeometry values (1, :objbv)\", {\"objbv\": obj})\n",
" \n",
"print(\"Done\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And then fetched back:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" for (id, obj) in cursor.execute(\"select IntCol, Geometry from testgeometry\"):\n",
" print(id, obj)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Simple attribute access is easy:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" for (id, obj) in cursor.execute(\"select IntCol, Geometry from testgeometry\"):\n",
" print(\"SDO_GTYPE is\", obj.SDO_GTYPE)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" To display all attributes, create a helper function:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Oracle Database object dumper\n",
"\n",
"def dumpobject(obj, prefix = \" \"):\n",
" if obj.type.iscollection:\n",
" print(prefix, \"[\")\n",
" for value in obj.aslist():\n",
" if isinstance(value, oracledb.Object):\n",
" dumpobject(value, prefix + \" \")\n",
" else:\n",
" print(prefix + \" \", repr(value))\n",
" print(prefix, \"]\")\n",
" else:\n",
" print(prefix, \"{\")\n",
" for attr in obj.type.attributes:\n",
" value = getattr(obj, attr.name)\n",
" if isinstance(value, oracledb.Object):\n",
" print(prefix + \" \" + attr.name + \" :\")\n",
" dumpobject(value, prefix + \" \")\n",
" else:\n",
" print(prefix + \" \" + attr.name + \" :\", repr(value))\n",
" print(prefix, \"}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using the helper function shows the full object structure:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" for (id, obj) in cursor.execute(\"select IntCol, Geometry from testgeometry\"):\n",
" print(\"Id: \", id)\n",
" dumpobject(obj)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# PL/SQL Collections\n",
"\n",
"The sample schema uses PL/SQL collections"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cursor = connection.cursor()\n",
"\n",
"cursor.execute(\"select dbms_metadata.get_ddl('PACKAGE', 'PKG_DEMO') from dual\")\n",
"ddl, = cursor.fetchone()\n",
"print(ddl.read())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To get a collection, create a Python variable with the database object type:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"typeObj = connection.gettype(\"PKG_DEMO.UDT_STRINGLIST\")\n",
"obj = typeObj()\n",
"\n",
"# call the stored procedure which will populate the object\n",
"cursor = connection.cursor()\n",
"cursor.callproc(\"pkg_Demo.DemoCollectionOut\", (obj,))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To show the collection indexes and values:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ix = obj.first()\n",
"while ix is not None:\n",
" print(ix, \"->\", obj.getelement(ix))\n",
" ix = obj.next(ix)\n",
"print()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Show the values as a simple list:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(obj.aslist())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Show the values as a simple dictionary:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(obj.asdict())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Binding PL/SQL Records\n",
"\n",
"Create a new Python object of the correct type and set attribute values:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import datetime\n",
"\n",
"typeObj = connection.gettype(\"PKG_DEMO.UDT_DEMORECORD\")\n",
"obj = typeObj()\n",
"\n",
"obj.NUMBERVALUE = 6\n",
"obj.STRINGVALUE = \"Test String\"\n",
"obj.DATEVALUE = datetime.datetime(2016, 5, 28)\n",
"obj.BOOLEANVALUE = False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Call the stored procedure which will modify the object:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with connection.cursor() as cursor:\n",
" cursor.callproc(\"pkg_Demo.DemoRecordsInOut\", (obj,))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Show the modified values:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"NUMBERVALUE ->\", obj.NUMBERVALUE)\n",
"print(\"STRINGVALUE ->\", obj.STRINGVALUE)\n",
"print(\"DATEVALUE ->\", obj.DATEVALUE)\n",
"print(\"BOOLEANVALUE ->\", obj.BOOLEANVALUE)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,82 @@
# Python python-oracledb Notebooks
This directory contains Jupyter notebooks showing best practices for using
python-oracledb. The notebooks cover:
- Connecting
- Queries
- DML
- Data loading and unloading (CSV Files)
- JSON
- PL/SQL
- Objects
Python-oracledb's default 'Thin' mode is used.
Jupyter notebooks let you easily step through, modify, and execute Python code:
![A screenshot of a notebook running in a browser](./images/jupyter-notebook-screenshot.png)
# Setup
An existing Oracle Database is required. The JSON demo assumes that Oracle
Database 21c or later is being used.
### Install Python 3
See https://www.python.org/downloads/
### Install Jupyter
See https://jupyter.org/install:
python3 -m pip install notebook
### Install the python-oracledb driver
python3 -m pip install oracledb
### Install some libraries used by the examples
python3 -m pip install numpy matplotlib
### Create the python-oracledb sample schema
Clone the python-oracledb repository, for example in a terminal window:
git clone https://github.com/oracle/python-oracledb.git
cd python-oracledb/samples
Review README.md and sample_env.py
In the terminal, set desired credentials, for example:
export PYO_SAMPLES_ADMIN_USER=system
export PYO_SAMPLES_ADMIN_PASSWORD=oracle
export PYO_SAMPLES_CONNECT_STRING=localhost/orclpdb1
export PYO_SAMPLES_MAIN_USER=pythondemo
export PYO_SAMPLES_MAIN_PASSWORD=welcome
export PYO_SAMPLES_EDITION_USER=pythoneditions
export PYO_SAMPLES_EDITION_PASSWORD=welcome
export PYO_SAMPLES_EDITION_NAME=python_e1
Install the schema:
python3 create_schema.py
### Start Jupyter
cd sample_notebooks
jupyter notebook
If Jupyter is not in your path, you may need to find it on your computer and
invoke it with an absolute path, for example on macOS:
$HOME/Library/Python/3.9/bin/jupyter notebook
Load each notebook *.ipynb file and step through the cells.
Before running the notebooks cells, edit the credentials and connect string
near the top of each notebook to match those used when installing the sample
schema.

View File

@ -0,0 +1,2 @@
1,Fred,Nurke,UK
2,Henry,Crun,UK
1 1 Fred Nurke UK
2 2 Henry Crun UK

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -58,16 +58,16 @@ STMT_TYPE_SQLPLUS = 3 # Like SET or DESC
# Simple regexps for statement type identification
SQL_PATTERN = re.compile(
r"^(administer|alter|analyze|associate|audit|call|comment|commit|create"
"^(administer|alter|analyze|associate|audit|call|comment|commit|create"
"|delete|disassociate|drop|explain|flashback|grant|insert|lock|merge"
"|noaudit|purge|rename|revoke|rollback|savepoint|select|truncate|update"
"|with|set\s+constraint[s*]|set\s+role|set\s+transaction)(\s|$|;)",
r"|with|set\s+constraint[s*]|set\s+role|set\s+transaction)(\s|$|;)",
re.IGNORECASE,
)
PLSQL_PATTERN = re.compile(
r"^(begin|declare|create\s+or\s+replace|create\s+function"
"|create\s+procedure|create\s+package|create\s+type)(\s|$)",
r"|create\s+procedure|create\s+package|create\s+type)(\s|$)",
re.IGNORECASE,
)
@ -83,7 +83,7 @@ SQLPLUS_PATTERN = re.compile(
"|(repf(o?|oo?|oot?|oote?|ooter?))|(reph(e?|ea?|ead?|eade?|eader?))"
"|(r(u?|un?))|(sav(e?))|set|(sho(w?))|shutdown|(spo(o?|ol?))|(sta(r?|rt?))"
"|startup|store|(timi(n?|ng?))|(tti(t?|tl?|tle?))|(undef(i?|in?|ine?))"
"|(var(i?|ia?|iab?|iabl?|iable?))|whenever|xquery|--.*)(\s|$)",
r"|(var(i?|ia?|iab?|iabl?|iable?))|whenever|xquery|--.*)(\s|$)",
re.IGNORECASE,
)
@ -161,10 +161,13 @@ def execute_db_statement(connection, statement, statement_type):
with connection.cursor() as cursor:
try:
cursor.execute(statement)
if cursor.warning:
print(cursor.warning)
if statement_type == STMT_TYPE_SQL and QUERY_PATTERN.match(
statement
):
fetch_rows(cursor)
except oracledb.Error as e:
(error,) = e.args
print(statement)