Skip to content

Commit a43a812

Browse files
Doc and sample improvements.
1 parent fc40c52 commit a43a812

File tree

7 files changed

+226
-60
lines changed

7 files changed

+226
-60
lines changed

doc/src/api_manual/async_connection.rst

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@ AsyncConnection Methods
4444
.. method:: AsyncConnection.begin_sessionless_transaction(transaction_id=None, \
4545
timeout=60, defer_round_trip=False)
4646

47-
Begins a new sessionless transaction using the specified transaction
48-
identifier. This method returns the transaction identifier specified by the
49-
user or generated by python-oracledb.
47+
Begins a new sessionless transaction. This method returns the transaction
48+
identifier specified by the user or generated by python-oracledb.
5049

5150
The ``transaction_id`` parameter should be of type string or bytes. If
5251
specified, it represents a unique identifier for the transaction. If a
@@ -59,7 +58,11 @@ AsyncConnection Methods
5958
exceed 64 bytes in length.
6059

6160
The ``timeout`` parameter is the number of seconds that this transaction
62-
can be resumed by a connection the next time that it is suspended. The
61+
can stay suspended when
62+
:meth:`AsyncConnection.suspend_sessionless_transaction()` is later called,
63+
or if the transaction is automatically suspended when the
64+
``suspend_on_success`` parameter is set to to *True* in
65+
:meth:`AsyncCursor.execute()` or :meth:`AsyncCursor.executemany()`. The
6366
default value is *60* seconds. If a transaction is not resumed within this
6467
specified duration, the transaction will be rolled back.
6568

@@ -337,24 +340,20 @@ AsyncConnection Methods
337340

338341
The ``timeout`` parameter is the number of seconds that the current
339342
connection waits to resume a transaction if another connection is using it.
340-
This timeout is only effective when the transaction is in use by another
341-
connection. In this case, the current connection waits for the transaction
342-
to be suspended within this timeout period. When ``defer_round_trip`` is
343-
set to *False*, the wait happens in the
343+
When ``defer_round_trip`` is set to *False*, the wait happens in the
344344
``resume_sessionless_transaction()`` call itself, and the function blocks
345-
until the transaction becomes available or the timeout expires.
346-
When ``defer_round_trip`` is set to *True*, the resume is deferred and the
347-
wait occurs at the time of the next database operation instead. At the
348-
start of the wait period, if the transaction is not in use by any other
349-
connection, the resume happens immediately. If the transaction remains in
350-
use by the other connection after the timeout period, the error `ORA-25351
345+
until the transaction becomes available or the timeout expires. When
346+
``defer_round_trip`` is set to *True*, the resume is deferred and the wait
347+
occurs at the time of the next database operation instead. At the start of
348+
the wait period, if the transaction is not in use by any other connection,
349+
the resume happens immediately. If the transaction remains in use by the
350+
other connection after the timeout period, the error `ORA-25351
351351
<https://docs.oracle.com/en/error-help/db/ora-25351>`__ is raised. If
352352
another connection completes the transaction, the error `ORA-24756
353353
<https://docs.oracle.com/en/error-help/db/ora-24756>`__ is raised. These
354354
error messages are only thrown for non-RAC instances. For information on
355-
using Oracle RAC, see
356-
:ref:`Sessionless Transactions with Oracle RAC <sessionlesstxnswithrac>`.
357-
The default value is *60* seconds.
355+
using Oracle RAC, see :ref:`Sessionless Transactions with Oracle RAC
356+
<sessionlesstxnswithrac>`. The default value is *60* seconds.
358357

359358
The ``defer_round_trip`` parameter is a boolean that determines whether
360359
the request to resume a transaction is to be sent immediately or with the
@@ -404,9 +403,10 @@ AsyncConnection Methods
404403

405404
This detaches the transaction from the connection, allowing it to be
406405
resumed later with the transaction identifier that was specified during
407-
creation of the sessionless transaction. Also, the timeout value defined in
408-
:meth:`AsyncConnection.begin_sessionless_transaction()` comes into effect
409-
and determines how long the transaction can stay suspended.
406+
creation of the sessionless transaction. The ``timeout`` previously passed
407+
to :meth:`AsyncConnection.begin_sessionless_transaction()` determines how
408+
long the transaction can stay suspended before it is automatically rolled
409+
back.
410410

411411
See :ref:`sessionlesstxns`.
412412

doc/src/api_manual/connection.rst

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,8 @@ Connection Methods
4949
.. method:: Connection.begin_sessionless_transaction(transaction_id=None, \
5050
timeout=60, defer_round_trip=False)
5151

52-
Begins a new sessionless transaction using the specified transaction
53-
identifier. This method returns the transaction identifier specified by the
54-
user or generated by python-oracledb.
52+
Begins a new sessionless transaction. This method returns the transaction
53+
identifier specified by the user or generated by python-oracledb.
5554

5655
The ``transaction_id`` parameter should be of type string or bytes. If
5756
specified, it represents a unique identifier for the transaction. If a
@@ -64,9 +63,13 @@ Connection Methods
6463
64 bytes in length.
6564

6665
The ``timeout`` parameter is the number of seconds that this transaction
67-
can be resumed by a connection the next time that it is suspended. The
68-
default value is *60* seconds. If a transaction is not resumed within this
69-
specified duration, the transaction will be rolled back.
66+
can stay suspended when
67+
:meth:`Connection.suspend_sessionless_transaction()` is later called, or if
68+
the transaction is automatically suspended when the ``suspend_on_success``
69+
parameter is set to to *True* in :meth:`Cursor.execute()` or
70+
:meth:`Cursor.executemany()`. The default value is *60* seconds. If a
71+
transaction is not resumed within this specified duration, the transaction
72+
will be rolled back.
7073

7174
The ``defer_round_trip`` parameter is a boolean that determines whether
7275
the request to start a transaction is to be sent immediately or with the
@@ -322,24 +325,20 @@ Connection Methods
322325

323326
The ``timeout`` parameter is the number of seconds that the current
324327
connection waits to resume a transaction if another connection is using it.
325-
This timeout is only effective when the transaction is in use by another
326-
connection. In this case, the current connection waits for the transaction
327-
to be suspended within this timeout period. When ``defer_round_trip`` is
328-
set to *False*, the wait happens in the
328+
When ``defer_round_trip`` is set to *False*, the wait happens in the
329329
``resume_sessionless_transaction()`` call itself, and the function blocks
330-
until the transaction becomes available or the timeout expires.
331-
When ``defer_round_trip`` is set to *True*, the resume is deferred and the
332-
wait occurs at the time of the next database operation instead. At the
333-
start of the wait period, if the transaction is not in use by any other
334-
connection, the resume happens immediately. If the transaction remains in
335-
use by the other connection after the timeout period, the error `ORA-25351
330+
until the transaction becomes available or the timeout expires. When
331+
``defer_round_trip`` is set to *True*, the resume is deferred and the wait
332+
occurs at the time of the next database operation instead. At the start of
333+
the wait period, if the transaction is not in use by any other connection,
334+
the resume happens immediately. If the transaction remains in use by the
335+
other connection after the timeout period, the error `ORA-25351
336336
<https://docs.oracle.com/en/error-help/db/ora-25351>`__ is raised. If
337337
another connection completes the transaction, the error `ORA-24756
338338
<https://docs.oracle.com/en/error-help/db/ora-24756>`__ is raised. These
339339
error messages are only thrown for non-RAC instances. For information on
340-
using Oracle RAC, see
341-
:ref:`Sessionless Transactions with Oracle RAC <sessionlesstxnswithrac>`.
342-
The default value is *60* seconds.
340+
using Oracle RAC, see :ref:`Sessionless Transactions with Oracle RAC
341+
<sessionlesstxnswithrac>`. The default value is *60* seconds.
343342

344343
The ``defer_round_trip`` parameter is a boolean that determines whether
345344
the request to resume a transaction is to be sent immediately or with the
@@ -499,9 +498,9 @@ Connection Methods
499498

500499
This detaches the transaction from the connection, allowing it to be
501500
resumed later with the transaction identifier that was specified during
502-
creation of the sessionless transaction. Also, the timeout value defined in
503-
:meth:`Connection.begin_sessionless_transaction()` comes into effect and
504-
determines how long the transaction can stay suspended.
501+
creation of the sessionless transaction. The ``timeout`` previously passed
502+
to :meth:`Connection.begin_sessionless_transaction()` determines how long
503+
the transaction can stay suspended before it is automatically rolled back.
505504

506505
See :ref:`sessionlesstxns`.
507506

doc/src/api_manual/module.rst

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2322,14 +2322,15 @@ Oracledb Methods
23222322

23232323
.. function:: is_thin_mode()
23242324

2325-
Returns a boolean indicating if Thin mode is in use.
2325+
Returns a boolean indicating if python-oracledb is in Thin mode.
23262326

23272327
Immediately after python-oracledb is imported, this function will return
2328-
*True* indicating that python-oracledb defaults to Thin mode. If
2329-
:func:`oracledb.init_oracle_client()` is called, then a subsequent call to
2330-
``is_thin_mode()`` will return False indicating that Thick mode is
2331-
enabled. Once the first standalone connection or connection pool is
2332-
created, or a call to ``oracledb.init_oracle_client()`` is made, then
2328+
*True* indicating that python-oracledb defaults to Thin mode. If a call to
2329+
:func:`oracledb.init_oracle_client()` returns successfully, then a
2330+
subsequent call to ``is_thin_mode()`` will return False indicating that
2331+
Thick mode is enabled. Once the first standalone connection or connection
2332+
pool is created, or a successful call to ``oracledb.init_oracle_client()``
2333+
is made, or :meth:`oracledb.enable_thin_mode()` is called, then
23332334
python-oracledb’s mode is fixed and the value returned by
23342335
``is_thin_mode()`` will never change for the lifetime of the process.
23352336

doc/src/release_notes.rst

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Thin Mode Changes
3030
bind variable immediately following a query that returned multiple
3131
duplicate rows.
3232
#) Fixed bug with connect strings containing ``SOURCE_ROUTE=YES`` where the
33-
second host is unresolvable by the client.
33+
second host is unresolvable by the host running python-oracledb.
3434

3535
Thick Mode Changes
3636
++++++++++++++++++
@@ -53,22 +53,27 @@ Common Changes
5353
:ref:`dfinsert`.
5454
- Added internal support for the ArrowArrayStream PyCapsule interface to
5555
simplify :ref:`DataFrame <oracledataframeobj>` use.
56-
- Remove use of the DataFrame Interchange Protocol in python-oracledb
56+
- Removed use of the DataFrame Interchange Protocol in python-oracledb
5757
:ref:`DataFrame <oracledataframeobj>` objects.
58+
- Removed the prefix "Oracle" from the data frame object names. They are
59+
now called :ref:`DataFrame <oracledataframeobj>` and :ref:`ArrowArray
60+
<oraclearrowarrayobj>`.
5861
- Documentation on methods and attributes of the :ref:`DataFrame
5962
<oracledataframeobj>` and :ref:`ArrowArray <oraclearrowarrayobj>` objects
6063
is now available when using IDE introspection.
6164
- Upgraded Arrow C Data (nanoarrow) API version to 0.7.0.
62-
- Ensure that the GIL is held when releasing references to :ref:`ArrowArray
65+
- Ensured that the `Python GIL
66+
<https://docs.python.org/3/glossary.html#term-global-interpreter-lock>`__
67+
is held when releasing references to :ref:`ArrowArray
6368
<oraclearrowarrayobj>` objects when exported Arrow buffers are released
6469
by the consumer. This avoids a segfault seen in some circumstances.
65-
- Fixed bug when deciding Arrow datatype for numeric expressions
70+
- Fixed bug when deciding Arrow datatype for numeric expressions.
6671
(`issue 510 <https://github.com/oracle/python-oracledb/issues/510>`__)
6772
- Fixed bug when fetching numeric data that has no decimal point but the
68-
Arrow array has scale > 0
69-
- Fixed bug when fetching dates that are in the year 2038 or later
73+
Arrow array has scale > 0.
74+
- Fixed bug when fetching dates that are in the year 2038 or later.
7075
- Fixed bug when fetching numeric data with precision that exceeds 38 as
71-
decimal data
76+
decimal data.
7277

7378
Note the data frame support in python-oracledb 3.3 is a pre-release, and
7479
may change in a future version.

doc/src/user_guide/dataframes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,8 @@ For general information about fast data ingestion, and discussion of
664664
:meth:`Cursor.executemany()` and :meth:`AsyncCursor.executemany()` options, see
665665
:ref:`batchstmnt`.
666666

667-
**Explicit Conversion to DataFrame or ArrowArray**
667+
Explicit Conversion to DataFrame or ArrowArray
668+
==============================================
668669

669670
Data frames that support the Apache Arrow PyCapsule Interface can be explicitly
670671
converted to :ref:`DataFrame <oracledataframeobj>` and :ref:`ArrowArray

doc/src/user_guide/txn_management.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,13 @@ You can pass the following parameters to
196196
:meth:`~Connection.begin_sessionless_transaction`. An example is
197197
"36b8f84d-df4e-4d49-b662-bcde71a8764f".
198198

199-
- ``timeout``: This parameter determines the duration that this transaction
200-
can be resumed by a connection the next time that it is suspended. The
201-
default value is *60* seconds. If the transaction is not resumed within
202-
the specified duration, the transaction will be rolled back.
199+
- ``timeout``: This parameter is the number of seconds this transaction can
200+
stay suspended when :meth:`Connection.suspend_sessionless_transaction()` is
201+
later called, or if the transaction is automatically suspended when the
202+
``suspend_on_success`` parameter is set to to *True* in
203+
:meth:`Cursor.execute()` or :meth:`Cursor.executemany()`. The default value
204+
is *60* seconds. If the transaction is not resumed within the specified
205+
duration, the transaction will be rolled back.
203206

204207
- ``defer_round_trip``: This parameter determines whether the request to start
205208
a sessionless transaction should be sent immediately or with the next

samples/sessionless_transactions.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# -----------------------------------------------------------------------------
2+
# Copyright (c) 2025 Oracle and/or its affiliates.
3+
#
4+
# This software is dual-licensed to you under the Universal Permissive License
5+
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
6+
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
7+
# either license.
8+
#
9+
# If you elect to accept the software under the Apache License, Version 2.0,
10+
# the following applies:
11+
#
12+
# Licensed under the Apache License, Version 2.0 (the "License");
13+
# you may not use this file except in compliance with the License.
14+
# You may obtain a copy of the License at
15+
#
16+
# https://www.apache.org/licenses/LICENSE-2.0
17+
#
18+
# Unless required by applicable law or agreed to in writing, software
19+
# distributed under the License is distributed on an "AS IS" BASIS,
20+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21+
# See the License for the specific language governing permissions and
22+
# limitations under the License.
23+
# -----------------------------------------------------------------------------
24+
25+
# -----------------------------------------------------------------------------
26+
# sessionless_transactions.py
27+
#
28+
# Show Oracle Database 23ai Sessionless Transactions
29+
# -----------------------------------------------------------------------------
30+
31+
import sys
32+
33+
import oracledb
34+
import sample_env
35+
36+
# determine whether to use python-oracledb thin mode or thick mode
37+
if not sample_env.get_is_thin():
38+
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
39+
40+
# this script only works with Oracle Database 23.6 or later
41+
if sample_env.get_server_version() < (23, 6):
42+
sys.exit("This example requires Oracle Database 23.6 or later.")
43+
44+
# this script works with thin mode, or with thick mode using Oracle Client
45+
# 23.6 or later
46+
if not oracledb.is_thin_mode() and oracledb.clientversion()[:2] < (23, 6):
47+
sys.exit(
48+
"This example requires python-oracledb thin mode, or Oracle Client"
49+
" 23.6 or later"
50+
)
51+
52+
TXN_ID = b"my_transaction_id"
53+
54+
pool = oracledb.create_pool(
55+
user=sample_env.get_main_user(),
56+
password=sample_env.get_main_password(),
57+
dsn=sample_env.get_connect_string(),
58+
params=sample_env.get_pool_params(),
59+
)
60+
61+
# -----------------------------------------------------------------------------
62+
# Basic Sessionless Transaction example
63+
64+
print("Example 1:")
65+
66+
# Start and suspend a transaction
67+
with pool.acquire() as connection1:
68+
69+
# Immediately begin the transaction
70+
connection1.begin_sessionless_transaction(transaction_id=TXN_ID)
71+
72+
with connection1.cursor() as cursor1:
73+
cursor1.execute(
74+
"insert into mytab(id, data) values (:i, :d)", [1, "Sessionless 1"]
75+
)
76+
connection1.suspend_sessionless_transaction()
77+
78+
# Since the transaction is suspended, there will be no rows
79+
print("1st query")
80+
with connection1.cursor() as cursor1b:
81+
for r in cursor1b.execute("select * from mytab"):
82+
print(r)
83+
84+
# Resume and complete the transaction in a different connection
85+
with pool.acquire() as connection2:
86+
87+
# Immediately resume the transaction
88+
connection2.resume_sessionless_transaction(transaction_id=TXN_ID)
89+
90+
with connection2.cursor() as cursor2:
91+
cursor2.execute(
92+
"insert into mytab(id, data) values (:i, :d)", [2, "Sessionless 2"]
93+
)
94+
95+
# The query will show both rows inserted
96+
print("2nd query")
97+
for r in cursor2.execute("select * from mytab order by id"):
98+
print(r)
99+
100+
# Rollback so the example can be run multiple times.
101+
# This concludes the Sessionless Transaction
102+
connection2.rollback()
103+
104+
# -----------------------------------------------------------------------------
105+
# Sessionless Transaction example with custom timeouts and round-trip
106+
# optimizations
107+
108+
print("Example 2:")
109+
110+
# Start and suspend a transaction
111+
with pool.acquire() as connection3:
112+
113+
connection3.begin_sessionless_transaction(
114+
transaction_id=TXN_ID,
115+
# The transaction can only ever be suspended for 15 seconds before it
116+
# is automatically rolled back
117+
timeout=15,
118+
# Only start the transaction when the next DB operation is performed
119+
defer_round_trip=True,
120+
)
121+
with connection3.cursor() as cursor3:
122+
cursor3.execute(
123+
"insert into mytab(id, data) values (:i, :d)",
124+
[3, "Sessionless 3"],
125+
suspend_on_success=True, # automatically suspend on success
126+
)
127+
128+
# Since the transaction is suspended, there will be no rows
129+
print("1st query")
130+
with connection3.cursor() as cursor3b:
131+
for r in cursor3b.execute("select * from mytab"):
132+
print(r)
133+
134+
# Resume and complete the transaction in a different connection
135+
with pool.acquire() as connection4:
136+
connection4.resume_sessionless_transaction(
137+
transaction_id=TXN_ID,
138+
# Only wait 20 seconds if someone else is using the transaction
139+
timeout=20,
140+
# Only initiate resuming the transaction when the next DB operation is
141+
# performed
142+
defer_round_trip=True,
143+
)
144+
145+
with connection4.cursor() as cursor4:
146+
cursor4.execute(
147+
"insert into mytab(id, data) values (:i, :d)", [4, "Sessionless 4"]
148+
)
149+
150+
# The query will show both rows inserted
151+
print("2nd query")
152+
for r in cursor4.execute("select * from mytab order by id"):
153+
print(r)
154+
155+
# Rollback so the example can be run multiple times.
156+
# This concludes the Sessionless Transaction
157+
connection4.rollback()

0 commit comments

Comments
 (0)