Skip to content

Commit 3962459

Browse files
Added support for implicit pooling with DRCP.
1 parent a70bb00 commit 3962459

26 files changed

+210
-25
lines changed

doc/src/release_notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ oracledb 2.1.0 (TBD)
1313
Thin Mode Changes
1414
+++++++++++++++++
1515

16+
#) Added support for Oracle Database 23c feature: implicit pooling with DRCP.
1617
#) Added support for Oracle Database 23c feature that can improve the
1718
performance of connection creation by reducing the number of round trips
1819
required for all connections created.

src/oracledb/base_impl.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ cdef class Description(ConnectParamsNode):
346346
public str sid
347347
public str cclass
348348
public str connection_id_prefix
349+
public str pool_boundary
349350
public uint32_t purity
350351
public bint ssl_server_dn_match
351352
public str ssl_server_cert_dn

src/oracledb/connect_params.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -----------------------------------------------------------------------------
2-
# Copyright (c) 2021, 2023, Oracle and/or its affiliates.
2+
# Copyright (c) 2021, 2024, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -94,6 +94,7 @@ def __init__(
9494
connection_id_prefix: str = None,
9595
ssl_context: Any = None,
9696
sdu: int = 8192,
97+
pool_boundary: str = None,
9798
handle: int = 0,
9899
):
99100
"""
@@ -255,6 +256,11 @@ def __init__(
255256
actually be used is negotiated down to the lower of this value and
256257
the database network SDU configuration value (default: 8192)
257258
259+
- pool_boundary: one of the values "statement" or "transaction"
260+
indicating when pooled DRCP connections can be returned to the pool.
261+
This requires the use of DRCP with Oracle Database 23.4 or higher
262+
(default: None)
263+
258264
- handle: an integer representing a pointer to a valid service context
259265
handle. This value is only used in thick mode. It should be used with
260266
extreme caution (default: 0)
@@ -299,7 +305,8 @@ def __repr__(self):
299305
+ f"debug_jdwp={self.debug_jdwp!r}, "
300306
+ f"connection_id_prefix={self.connection_id_prefix!r}, "
301307
+ f"ssl_context={self.ssl_context!r}, "
302-
+ f"sdu={self.sdu!r}"
308+
+ f"sdu={self.sdu!r}, "
309+
+ f"pool_boundary={self.pool_boundary!r}"
303310
+ ")"
304311
)
305312

@@ -461,6 +468,16 @@ def mode(self) -> int:
461468
"""
462469
return self._impl.mode
463470

471+
@property
472+
@_description_attr
473+
def pool_boundary(self) -> Union[list, str]:
474+
"""
475+
One of the values "statement" or "transaction" indicating when pooled
476+
DRCP connections can be returned to the pool. This requires the use of
477+
DRCP with Oracle Database 23.4 or higher.
478+
"""
479+
return self._impl.pool_boundary
480+
464481
@property
465482
@_address_attr
466483
def port(self) -> Union[list, int]:
@@ -719,6 +736,7 @@ def set(
719736
connection_id_prefix: str = None,
720737
ssl_context: Any = None,
721738
sdu: int = None,
739+
pool_boundary: str = None,
722740
handle: int = None,
723741
):
724742
"""
@@ -871,6 +889,10 @@ def set(
871889
actually be used is negotiated down to the lower of this value and
872890
the database network SDU configuration value
873891
892+
- pool_boundary: one of the values "statement" or "transaction"
893+
indicating when pooled DRCP connections can be returned to the pool.
894+
This requires the use of DRCP with Oracle Database 23.4 or higher
895+
874896
- handle: an integer representing a pointer to a valid service context
875897
handle. This value is only used in thick mode. It should be used with
876898
extreme caution

src/oracledb/connection.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,7 @@ def connect(
11971197
connection_id_prefix: str = None,
11981198
ssl_context: Any = None,
11991199
sdu: int = 8192,
1200+
pool_boundary: str = None,
12001201
handle: int = 0,
12011202
) -> Connection:
12021203
"""
@@ -1379,6 +1380,10 @@ def connect(
13791380
actually be used is negotiated down to the lower of this value and the
13801381
database network SDU configuration value (default: 8192)
13811382
1383+
- pool_boundary: one of the values "statement" or "transaction" indicating
1384+
when pooled DRCP connections can be returned to the pool. This requires
1385+
the use of DRCP with Oracle Database 23.4 or higher (default: None)
1386+
13821387
- handle: an integer representing a pointer to a valid service context
13831388
handle. This value is only used in thick mode. It should be used with
13841389
extreme caution (default: 0)
@@ -1762,6 +1767,7 @@ def connect_async(
17621767
connection_id_prefix: str = None,
17631768
ssl_context: Any = None,
17641769
sdu: int = 8192,
1770+
pool_boundary: str = None,
17651771
handle: int = 0,
17661772
) -> AsyncConnection:
17671773
"""
@@ -1944,6 +1950,10 @@ def connect_async(
19441950
actually be used is negotiated down to the lower of this value and the
19451951
database network SDU configuration value (default: 8192)
19461952
1953+
- pool_boundary: one of the values "statement" or "transaction" indicating
1954+
when pooled DRCP connections can be returned to the pool. This requires
1955+
the use of DRCP with Oracle Database 23.4 or higher (default: None)
1956+
19471957
- handle: an integer representing a pointer to a valid service context
19481958
handle. This value is only used in thick mode. It should be used with
19491959
extreme caution (default: 0)

src/oracledb/errors.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ def _raise_err(
239239
ERR_DBOBJECT_ATTR_MAX_SIZE_VIOLATED = 2043
240240
ERR_DBOBJECT_ELEMENT_MAX_SIZE_VIOLATED = 2044
241241
ERR_INVALID_ARRAYSIZE = 2045
242+
ERR_CURSOR_HAS_BEEN_CLOSED = 2046
242243

243244
# error numbers that result in NotSupportedError
244245
ERR_TIME_NOT_SUPPORTED = 3000
@@ -293,6 +294,7 @@ def _raise_err(
293294
ERR_NO_CONFIG_DIR = 4027
294295
ERR_INVALID_SERVER_TYPE = 4028
295296
ERR_TOO_MANY_BATCH_ERRORS = 4029
297+
ERR_INVALID_POOL_BOUNDARY = 4030
296298

297299
# error numbers that result in InternalError
298300
ERR_MESSAGE_TYPE_UNKNOWN = 5000
@@ -448,6 +450,7 @@ def _raise_err(
448450
),
449451
ERR_CONTENT_INVALID_AFTER_NUMBER: "invalid number (content after number)",
450452
ERR_CURSOR_NOT_OPEN: "cursor is not open",
453+
ERR_CURSOR_HAS_BEEN_CLOSED: "cursor has been closed by the database",
451454
ERR_DBOBJECT_ATTR_MAX_SIZE_VIOLATED: (
452455
"attribute {attr_name} of type {type_name} exceeds its maximum size "
453456
"(actual: {actual_size}, maximum: {max_size})"
@@ -520,6 +523,7 @@ def _raise_err(
520523
ERR_INVALID_NUMBER: "invalid number",
521524
ERR_INVALID_OBJECT_TYPE_NAME: 'invalid object type name: "{name}"',
522525
ERR_INVALID_OCI_ATTR_TYPE: "invalid OCI attribute type {attr_type}",
526+
ERR_INVALID_POOL_BOUNDARY: "invalid DRCP pool boundary {boundary}",
523527
ERR_INVALID_POOL_CLASS: "invalid connection pool class",
524528
ERR_INVALID_POOL_PARAMS: "invalid pool params",
525529
ERR_INVALID_POOL_PURITY: "invalid DRCP purity {purity}",

src/oracledb/impl/base/connect_params.pyx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,8 @@ cdef class Description(ConnectParamsNode):
871871
temp_parts.append(f"(SID={self.sid})")
872872
if self.server_type is not None:
873873
temp_parts.append(f"(SERVER={self.server_type})")
874+
if self.pool_boundary is not None:
875+
temp_parts.append(f"(POOL_BOUNDARY={self.pool_boundary})")
874876
if cid is not None:
875877
temp_parts.append(f"(CID={cid})")
876878
else:
@@ -935,6 +937,7 @@ cdef class Description(ConnectParamsNode):
935937
_set_server_type_param(args, "server_type", self)
936938
_set_str_param(args, "cclass", self)
937939
_set_purity_param(args, "purity", &self.purity)
940+
_set_pool_boundary_param(args, "pool_boundary", self)
938941
_set_str_param(args, "connection_id_prefix", self)
939942

940943
def set_from_description_args(self, dict args):

src/oracledb/impl/base/utils.pyx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2022, 2023, Oracle and/or its affiliates.
2+
# Copyright (c) 2022, 2024, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -91,6 +91,23 @@ cdef int _set_uint_param(dict args, str name, uint32_t* out_val) except -1:
9191
out_val[0] = int(in_val)
9292

9393

94+
cdef int _set_pool_boundary_param(dict args, str name,
95+
object target) except -1:
96+
"""
97+
Sets a pool boundary parameter to the value specified. This must be one of
98+
the values "statement" or "transaction". If it is not one of these values
99+
an error is raised. If a value is specified and meets the criteria it is
100+
set directly on the target (since strings are treated as Python objects).
101+
"""
102+
in_val = args.get(name)
103+
if in_val is not None:
104+
in_val = in_val.lower()
105+
if in_val not in ("statement", "transaction"):
106+
errors._raise_err(errors.ERR_INVALID_POOL_BOUNDARY,
107+
boundary=in_val)
108+
setattr(target, name, in_val)
109+
110+
94111
cdef int _set_protocol_param(dict args, str name, object target) except -1:
95112
"""
96113
Sets a protocol parameter to the value provided in the dictionary. This

src/oracledb/impl/thin/capabilities.pyx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ cdef class Capabilities:
8484
TNS_CCAP_O5LOGON | TNS_CCAP_O5LOGON_NP | \
8585
TNS_CCAP_O7LOGON | TNS_CCAP_O8LOGON_LONG_IDENTIFIER | \
8686
TNS_CCAP_O9LOGON_LONG_PASSWORD
87+
self.compile_caps[TNS_CCAP_FEATURE_BACKPORT] = \
88+
TNS_CCAP_CTB_IMPLICIT_POOL
8789
self.compile_caps[TNS_CCAP_FIELD_VERSION] = self.ttc_field_version
8890
self.compile_caps[TNS_CCAP_SERVER_DEFINE_CONV] = 1
8991
self.compile_caps[TNS_CCAP_TTC1] = \

src/oracledb/impl/thin/constants.pxi

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ cdef enum:
631631
cdef enum:
632632
TNS_CCAP_SQL_VERSION = 0
633633
TNS_CCAP_LOGON_TYPES = 4
634+
TNS_CCAP_FEATURE_BACKPORT = 5
634635
TNS_CCAP_FIELD_VERSION = 7
635636
TNS_CCAP_SERVER_DEFINE_CONV = 8
636637
TNS_CCAP_TTC1 = 15
@@ -676,6 +677,7 @@ cdef enum:
676677
TNS_CCAP_O7LOGON = 32
677678
TNS_CCAP_O8LOGON_LONG_IDENTIFIER = 64
678679
TNS_CCAP_O9LOGON_LONG_PASSWORD = 0x80
680+
TNS_CCAP_CTB_IMPLICIT_POOL = 0x08
679681
TNS_CCAP_END_OF_CALL_STATUS = 0x01
680682
TNS_CCAP_IND_RCD = 0x08
681683
TNS_CCAP_FAST_BVEC = 0x20
@@ -724,6 +726,11 @@ cdef enum:
724726
TNS_UDS_FLAGS_IS_JSON = 0x00000100
725727
TNS_UDS_FLAGS_IS_OSON = 0x00000800
726728

729+
# end of call status flags
730+
cdef enum:
731+
TNS_EOCS_FLAGS_TXN_IN_PROGRESS = 0x00000002
732+
TNS_EOCS_FLAGS_SESS_RELEASE = 0x00008000
733+
727734
# accept flags
728735
cdef enum:
729736
TNS_ACCEPT_FLAG_FAST_AUTH = 0x10000000
@@ -735,7 +742,6 @@ cdef enum:
735742
TNS_DURATION_SESSION = 10
736743
TNS_MAX_LONG_LENGTH = 0x7fffffff
737744
TNS_MAX_CURSORS_TO_CLOSE = 500
738-
TNS_TXN_IN_PROGRESS = 0x00000002
739745
TNS_MAX_CONNECT_DATA = 230
740746
TNS_MAX_UROWID_LENGTH = 5267
741747
TNS_SERVER_CONVERTS_CHARS = 0x01

src/oracledb/impl/thin/cursor.pyx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,18 @@ cdef class ThinCursorImpl(BaseThinCursorImpl):
204204
# if a PL/SQL statement requires a full execute, perform only a single
205205
# iteration in order to allow the determination of input/output binds
206206
# to be completed; after that, an execution of the remaining iterations
207-
# can be performed
207+
# can be performed (but only if the cursor remains intact)
208208
if stmt._is_plsql and (stmt._cursor_id == 0 or stmt._binds_changed):
209209
message.num_execs = 1
210+
while num_execs > 0:
211+
num_execs -= 1
212+
protocol._process_single_message(message)
213+
message.offset += 1
214+
if stmt._cursor_id != 0:
215+
break
216+
if num_execs > 0:
217+
message.num_execs = num_execs
210218
protocol._process_single_message(message)
211-
if num_execs == 1:
212-
return
213-
message.offset = 1
214-
message.num_execs = num_execs - 1
215-
protocol._process_single_message(message)
216219
self.warning = message.warning
217220

218221
def parse(self, cursor):
@@ -289,15 +292,18 @@ cdef class AsyncThinCursorImpl(BaseThinCursorImpl):
289292
# if a PL/SQL statement requires a full execute, perform only a single
290293
# iteration in order to allow the determination of input/output binds
291294
# to be completed; after that, an execution of the remaining iterations
292-
# can be performed
295+
# can be performed (but only if the cursor remains intact)
293296
if stmt._is_plsql and (stmt._cursor_id == 0 or stmt._binds_changed):
294297
message.num_execs = 1
298+
while num_execs > 0:
299+
num_execs -= 1
300+
await protocol._process_single_message(message)
301+
message.offset += 1
302+
if stmt._cursor_id != 0:
303+
break
304+
if num_execs > 0:
305+
message.num_execs = num_execs
295306
await protocol._process_single_message(message)
296-
if num_execs == 1:
297-
return
298-
message.offset = 1
299-
message.num_execs = num_execs - 1
300-
await protocol._process_single_message(message)
301307
self.warning = message.warning
302308

303309
async def fetch_next_row(self, cursor):

0 commit comments

Comments
 (0)