Skip to content

Commit 3c47f61

Browse files
Added support for parsing the FAILOVER clause in full connect
descriptors; also made internal changes to improve the handling of multiple address and description lists in full connect descriptors.
1 parent 2266892 commit 3c47f61

File tree

7 files changed

+130
-105
lines changed

7 files changed

+130
-105
lines changed

doc/src/release_notes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Thin Mode Changes
1515

1616
#) Internal changes to improve handling of protocol between database and
1717
client.
18+
#) Internal changes to improve handling of multiple address and description
19+
lists in full connect descriptors.
1820

1921
Thick Mode Changes
2022
++++++++++++++++++
@@ -31,6 +33,7 @@ Common Changes
3133
associated with columns that are being fetched. SQL domains and annotations
3234
require Oracle Database 23c. If using python-oracledb Thick mode, Oracle
3335
Client 23c is also required.
36+
#) Added support for parsing the FAILOVER clause in full connect descriptors.
3437

3538

3639
oracledb 1.4.1 (September 2023)

src/oracledb/base_impl.pxd

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,20 @@ cdef class DbType:
9898
cdef DbType _from_ora_type_and_csfrm(uint8_t ora_type_num, uint8_t csfrm)
9999

100100

101-
cdef class Address:
101+
cdef class ConnectParamsNode:
102+
cdef:
103+
public bint source_route
104+
public bint load_balance
105+
public bint failover
106+
public bint must_have_children
107+
public list children
108+
public list active_children
109+
110+
cdef int _copy(self, ConnectParamsNode source) except -1
111+
cdef int _set_active_children(self) except -1
112+
113+
114+
cdef class Address(ConnectParamsNode):
102115
cdef:
103116
public str host
104117
public uint32_t port
@@ -109,23 +122,14 @@ cdef class Address:
109122
cdef str build_connect_string(self)
110123

111124

112-
cdef class AddressList:
113-
cdef:
114-
public list addresses
115-
bint source_route
116-
bint load_balance
117-
int lru_index
125+
cdef class AddressList(ConnectParamsNode):
118126

119127
cdef bint _uses_tcps(self)
120128
cdef str build_connect_string(self)
121129

122130

123-
cdef class Description:
131+
cdef class Description(ConnectParamsNode):
124132
cdef:
125-
public list address_lists
126-
public bint source_route
127-
public bint load_balance
128-
public int lru_index
129133
public uint32_t expire_time
130134
public uint32_t retry_count
131135
public uint32_t retry_delay
@@ -145,12 +149,7 @@ cdef class Description:
145149
cdef str build_connect_string(self, str cid=*)
146150

147151

148-
cdef class DescriptionList:
149-
cdef:
150-
public list descriptions
151-
bint source_route
152-
bint load_balance
153-
int lru_index
152+
cdef class DescriptionList(ConnectParamsNode):
154153

155154
cdef str build_connect_string(self)
156155

src/oracledb/base_impl.pyx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import decimal
4444
import inspect
4545
import json
4646
import os
47+
import random
4748
import re
4849
import secrets
4950
import sys

src/oracledb/connect_params.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def _description_attr(f):
300300
@functools.wraps(f)
301301
def wrapped(self):
302302
values = [getattr(d, f.__name__) \
303-
for d in self._impl.description_list.descriptions]
303+
for d in self._impl.description_list.children]
304304
return values if len(values) > 1 else values[0]
305305
return wrapped
306306

src/oracledb/impl/base/connect_params.pyx

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,11 @@ cdef class ConnectParamsImpl:
132132
self._default_description = Description()
133133
self._default_address = Address()
134134
self.description_list = DescriptionList()
135-
self.description_list.descriptions.append(self._default_description)
135+
self.description_list.children.append(self._default_description)
136136
self.debug_jdwp = os.getenv("ORA_DEBUG_JDWP")
137137
address_list = AddressList()
138-
address_list.addresses.append(self._default_address)
139-
self._default_description.address_lists.append(address_list)
138+
address_list.children.append(self._default_address)
139+
self._default_description.children.append(address_list)
140140

141141
def set(self, dict args):
142142
"""
@@ -369,10 +369,10 @@ cdef class ConnectParamsImpl:
369369
description.set_from_connect_data_args(args)
370370
description.set_from_description_args(args)
371371
description.set_from_security_args(args)
372-
description.address_lists = [AddressList()]
373-
description.address_lists[0].addresses.append(address)
372+
description.children = [AddressList()]
373+
description.children[0].children.append(address)
374374
self.description_list = DescriptionList()
375-
self.description_list.descriptions.append(description)
375+
self.description_list.children.append(description)
376376

377377
# otherwise, see if the name is a connect alias in a tnsnames.ora
378378
# configuration file
@@ -405,7 +405,7 @@ cdef class ConnectParamsImpl:
405405
for desc_args in list_args.get("description", [list_args]):
406406
description = self._default_description.copy()
407407
description.set_from_description_args(desc_args)
408-
self.description_list.descriptions.append(description)
408+
self.description_list.children.append(description)
409409
sub_args = desc_args.get("connect_data")
410410
if sub_args is not None:
411411
description.set_from_connect_data_args(sub_args)
@@ -415,11 +415,11 @@ cdef class ConnectParamsImpl:
415415
for list_args in desc_args.get("address_list", [desc_args]):
416416
address_list = AddressList()
417417
address_list.set_from_args(list_args)
418-
description.address_lists.append(address_list)
418+
description.children.append(address_list)
419419
for addr_args in list_args.get("address", []):
420420
address = self._default_address.copy()
421421
address.set_from_args(addr_args)
422-
address_list.addresses.append(address)
422+
address_list.children.append(address)
423423

424424
cdef int _set_access_token(self, object val, int error_num) except -1:
425425
"""
@@ -521,9 +521,9 @@ cdef class ConnectParamsImpl:
521521
AddressList addr_list
522522
Description desc
523523
Address addr
524-
return [addr for desc in self.description_list.descriptions \
525-
for addr_list in desc.address_lists \
526-
for addr in addr_list.addresses]
524+
return [addr for desc in self.description_list.children \
525+
for addr_list in desc.children \
526+
for addr in addr_list.children]
527527

528528
def get_connect_string(self):
529529
"""
@@ -618,13 +618,74 @@ cdef class ConnectParamsImpl:
618618
return dsn
619619

620620

621-
cdef class Address:
621+
cdef class ConnectParamsNode:
622+
623+
def __init__(self, bint must_have_children):
624+
self.must_have_children = must_have_children
625+
self.failover = True
626+
if must_have_children:
627+
self.children = []
628+
629+
cdef int _copy(self, ConnectParamsNode source) except -1:
630+
"""
631+
Copies data from the source to this node.
632+
"""
633+
self.must_have_children = source.must_have_children
634+
if self.must_have_children:
635+
self.children = []
636+
self.failover = source.failover
637+
self.load_balance = source.load_balance
638+
self.source_route = source.source_route
639+
640+
cdef int _set_active_children(self) except -1:
641+
"""
642+
Set the active children to process when connecting to the database.
643+
This call is recursive and will set the active children of each of its
644+
children.
645+
"""
646+
cdef ConnectParamsNode child
647+
648+
# if only one child is present, that child is considered active
649+
if len(self.children) == 1:
650+
self.active_children = self.children
651+
652+
# for source route, only the first child is considered active
653+
elif self.source_route:
654+
self.active_children = self.children[:1]
655+
656+
# for failover with load balance, all of the children are active but
657+
# are processed in a random order
658+
elif self.failover and self.load_balance:
659+
self.active_children = random.sample(self.children,
660+
k=len(self.children))
661+
662+
# for failover without load balance, all of the children are active and
663+
# are processed in the same order
664+
elif self.failover:
665+
self.active_children = self.children
666+
667+
# without failover, load balance indicates that only one of the
668+
# children is considered active and which one is selected randomly
669+
elif self.load_balance:
670+
self.active_children = random.sample(self.children, k=1)
671+
672+
# without failover or load balance, just the first child is navigated
673+
else:
674+
self.active_children = self.children[:1]
675+
676+
for child in self.children:
677+
if child.must_have_children:
678+
child._set_active_children()
679+
680+
681+
cdef class Address(ConnectParamsNode):
622682
"""
623683
Internal class used to hold parameters for an address used to create a
624684
connection to the database.
625685
"""
626686

627687
def __init__(self):
688+
ConnectParamsNode.__init__(self, False)
628689
self.protocol = DEFAULT_PROTOCOL
629690
self.port = DEFAULT_PORT
630691

@@ -648,6 +709,7 @@ cdef class Address:
648709
Creates a copy of the address and returns it.
649710
"""
650711
cdef Address address = Address.__new__(Address)
712+
address._copy(self)
651713
address.host = self.host
652714
address.port = self.port
653715
address.protocol = self.protocol
@@ -677,22 +739,22 @@ cdef class Address:
677739
_set_uint_param(args, "https_proxy_port", &self.https_proxy_port)
678740

679741

680-
cdef class AddressList:
742+
cdef class AddressList(ConnectParamsNode):
681743
"""
682744
Internal class used to hold address list parameters and a list of addresses
683745
used to create connections to the database.
684746
"""
685747

686748
def __init__(self):
687-
self.addresses = []
749+
ConnectParamsNode.__init__(self, True)
688750

689751
cdef bint _uses_tcps(self):
690752
"""
691753
Returns a boolean indicating if any of the addresses in the address
692754
list use the protocol TCPS.
693755
"""
694756
cdef Address address
695-
for address in self.addresses:
757+
for address in self.children:
696758
if address.protocol == "tcps":
697759
return True
698760
return False
@@ -702,7 +764,7 @@ cdef class AddressList:
702764
Build a connect string from the components.
703765
"""
704766
cdef Address a
705-
parts = [a.build_connect_string() for a in self.addresses]
767+
parts = [a.build_connect_string() for a in self.children]
706768
if len(parts) == 1:
707769
return parts[0]
708770
return f'(ADDRESS_LIST={"".join(parts)})'
@@ -712,17 +774,18 @@ cdef class AddressList:
712774
Set paramter values from an argument dictionary or an (ADDRESS_LIST)
713775
node in a connect descriptor.
714776
"""
777+
_set_bool_param(args, "failover", &self.failover)
715778
_set_bool_param(args, "load_balance", &self.load_balance)
716779
_set_bool_param(args, "source_route", &self.source_route)
717780

718781

719-
cdef class Description:
782+
cdef class Description(ConnectParamsNode):
720783
"""
721784
Internal class used to hold description parameters.
722785
"""
723786

724787
def __init__(self):
725-
self.address_lists = []
788+
ConnectParamsNode.__init__(self, True)
726789
self.tcp_connect_timeout = DEFAULT_TCP_CONNECT_TIMEOUT
727790
self.ssl_server_dn_match = True
728791

@@ -769,7 +832,7 @@ cdef class Description:
769832
# add address lists, but if the address list contains only a single
770833
# entry and that entry does not have a host, the other parts aren't
771834
# relevant anyway!
772-
for address_list in self.address_lists:
835+
for address_list in self.children:
773836
temp = address_list.build_connect_string()
774837
if temp is None:
775838
return None
@@ -818,7 +881,7 @@ cdef class Description:
818881
returns it.
819882
"""
820883
cdef Description description = Description.__new__(Description)
821-
description.address_lists = []
884+
description._copy(self)
822885
description.service_name = self.service_name
823886
description.sid = self.sid
824887
description.server_type = self.server_type
@@ -855,6 +918,7 @@ cdef class Description:
855918
"""
856919
cdef Address address
857920
_set_uint_param(args, "expire_time", &self.expire_time)
921+
_set_bool_param(args, "failover", &self.failover)
858922
_set_bool_param(args, "load_balance", &self.load_balance)
859923
_set_bool_param(args, "source_route", &self.source_route)
860924
_set_uint_param(args, "retry_count", &self.retry_count)
@@ -872,14 +936,14 @@ cdef class Description:
872936
_set_str_param(args, "wallet_location", self)
873937

874938

875-
cdef class DescriptionList:
939+
cdef class DescriptionList(ConnectParamsNode):
876940
"""
877941
Internal class used to hold description list parameters and a list of
878942
descriptions.
879943
"""
880944

881945
def __init__(self):
882-
self.descriptions = []
946+
ConnectParamsNode.__init__(self, True)
883947

884948
cdef str build_connect_string(self):
885949
"""
@@ -888,7 +952,7 @@ cdef class DescriptionList:
888952
cdef:
889953
Description d
890954
list parts
891-
parts = [d.build_connect_string() for d in self.descriptions]
955+
parts = [d.build_connect_string() for d in self.children]
892956
if len(parts) == 1:
893957
return parts[0]
894958
return f'(DESCIPTION_LIST={"".join(parts)})'
@@ -898,6 +962,7 @@ cdef class DescriptionList:
898962
Set paramter values from an argument dictionary or a (DESCRIPTION_LIST)
899963
node in a connect descriptor.
900964
"""
965+
_set_bool_param(args, "failover", &self.failover)
901966
_set_bool_param(args, "load_balance", &self.load_balance)
902967
_set_bool_param(args, "source_route", &self.source_route)
903968

0 commit comments

Comments
 (0)