Skip to content

Commit fcbdd60

Browse files
Added support for connecting to CMAN using "(SOURCE_ROUTE=YES)" in the
connect string (#81).
1 parent 229b5b6 commit fcbdd60

File tree

6 files changed

+72
-13
lines changed

6 files changed

+72
-13
lines changed

doc/src/release_notes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ Thin Mode Changes
2424
#) Added support for using a wallet for one-way TLS connections, rather than
2525
requiring OS recognition of certificates
2626
(`issue 65 <https://github.com/oracle/python-oracledb/issues/65>`__).
27+
#) Added support for connecting to CMAN using `(SOURCE_ROUTE=YES)` in the
28+
connect string
29+
(`issue 81 <https://github.com/oracle/python-oracledb/issues/81>`__).
2730
#) Fixed bug when fetching nested cursors with more columns than the parent
2831
cursor.
2932
#) Fixed bug preventing a cursor from being reused after it was bound as a

src/oracledb/base_impl.pxd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ cdef class Address:
111111
cdef class AddressList:
112112
cdef:
113113
public list addresses
114+
bint source_route
114115
bint load_balance
115116
int lru_index
116117

@@ -120,6 +121,7 @@ cdef class AddressList:
120121
cdef class Description:
121122
cdef:
122123
public list address_lists
124+
public bint source_route
123125
public bint load_balance
124126
public int lru_index
125127
public uint32_t expire_time
@@ -142,6 +144,7 @@ cdef class Description:
142144
cdef class DescriptionList:
143145
cdef:
144146
public list descriptions
147+
bint source_route
145148
bint load_balance
146149
int lru_index
147150

src/oracledb/impl/base/connect_params.pyx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ cdef class AddressList:
681681
node in a connect descriptor.
682682
"""
683683
_set_bool_param(args, "load_balance", &self.load_balance)
684+
_set_bool_param(args, "source_route", &self.source_route)
684685

685686

686687
cdef class Description:
@@ -746,6 +747,8 @@ cdef class Description:
746747
parts = []
747748
if self.load_balance:
748749
parts.append("(LOAD_BALANCE=ON)")
750+
if self.source_route:
751+
parts.append("(SOURCE_ROUTE=ON)")
749752
if self.retry_count != 0:
750753
parts.append(f"(RETRY_COUNT={self.retry_count})")
751754
if self.retry_delay != 0:
@@ -775,6 +778,7 @@ cdef class Description:
775778
description.purity = self.purity
776779
description.expire_time = self.expire_time
777780
description.load_balance = self.load_balance
781+
description.source_route = self.source_route
778782
description.retry_count = self.retry_count
779783
description.retry_delay = self.retry_delay
780784
description.tcp_connect_timeout = self.tcp_connect_timeout
@@ -802,6 +806,7 @@ cdef class Description:
802806
cdef Address address
803807
_set_uint_param(args, "expire_time", &self.expire_time)
804808
_set_bool_param(args, "load_balance", &self.load_balance)
809+
_set_bool_param(args, "source_route", &self.source_route)
805810
_set_uint_param(args, "retry_count", &self.retry_count)
806811
_set_uint_param(args, "retry_delay", &self.retry_delay)
807812
_set_duration_param(args, "tcp_connect_timeout",
@@ -844,6 +849,7 @@ cdef class DescriptionList:
844849
node in a connect descriptor.
845850
"""
846851
_set_bool_param(args, "load_balance", &self.load_balance)
852+
_set_bool_param(args, "source_route", &self.source_route)
847853

848854

849855
cdef class TnsnamesFile:

src/oracledb/impl/thin/connection.pyx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,26 +133,37 @@ cdef class ThinConnImpl(BaseConnImpl):
133133
AddressList address_list
134134
str connect_string
135135
Address address
136+
136137
# Retry connecting to the socket if an attempt fails and retry_count
137138
# is specified in the connect string. If an attempt succeeds, return
138139
# the socket and the valid address object.
139140
connect_string = _get_connect_data(description)
140141
for i in range(num_attempts):
141-
# Iterate through each address_list in the description. If the
142+
143+
# iterate through each address_list in the description; if the
142144
# description level load_balance is on, keep track of the least
143-
# recently used address for subsequent connections. If not,
144-
# iterate through the list in order.
145+
# recently used address for subsequent connections; otherwise,
146+
# iterate through the list in order; note that if source_route is
147+
# enabled that only the first address list is examined as the rest
148+
# are used for routing on the server
149+
if description.source_route and num_lists > 0:
150+
num_lists = 1
145151
for j in range(num_lists):
146152
if load_balance:
147153
idx1 = (j + description.lru_index) % num_lists
148154
else:
149155
idx1 = j
156+
157+
# iterate through each address in an address_list; if the
158+
# address_list level load_balance is on, keep track of the
159+
# least recently used address for subsequent connections;
160+
# otherwise, iterate through the list in order; note that if
161+
# source_route is enabled that only the first address is
162+
# examined and the rest are used for routing on the server
150163
address_list = address_lists[idx1]
151164
num_addresses = len(address_list.addresses)
152-
# Iterate through each address in an address_list. If the
153-
# address_list level load_balance is on, keep track of the
154-
# least recently used address for subsequent connections. If
155-
# not, iterate through the list in order.
165+
if address_list.source_route and num_addresses > 0:
166+
num_addresses = 1
156167
for k in range(num_addresses):
157168
if address_list.load_balance:
158169
idx2 = (k + address_list.lru_index) % num_addresses
@@ -182,6 +193,8 @@ cdef class ThinConnImpl(BaseConnImpl):
182193
ssize_t i, idx, num_descriptions = len(descriptions)
183194
Description description
184195
bint final_desc = False
196+
if description_list.source_route and num_descriptions > 0:
197+
num_descriptions = 1
185198
for i in range(num_descriptions):
186199
if i == num_descriptions - 1:
187200
final_desc = True

src/oracledb/impl/thin/messages.pyx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,12 +1655,13 @@ cdef class ConnectMessage(Message):
16551655
response = self.error_info.message
16561656
error_code = "unknown"
16571657
error_code_int = 0
1658-
pos = response.find("(ERR=")
1659-
if pos > 0:
1660-
end_pos = response.find(")", pos)
1661-
if end_pos > 0:
1662-
error_code = response[pos + 5:end_pos]
1663-
error_code_int = int(error_code)
1658+
if response is not None:
1659+
pos = response.find("(ERR=")
1660+
if pos > 0:
1661+
end_pos = response.find(")", pos)
1662+
if end_pos > 0:
1663+
error_code = response[pos + 5:end_pos]
1664+
error_code_int = int(error_code)
16641665
if error_code_int == 0:
16651666
errors._raise_err(errors.ERR_UNEXPECTED_REFUSE)
16661667
if error_code_int == TNS_ERR_INVALID_SERVICE_NAME:

tests/test_4500_connect_params.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,5 +553,38 @@ def test_4535_connect_descriptor_small_container_second(self):
553553
self.assertEqual(params.host,
554554
["host1a", "host1b", "host2", "host3a", "host3b"])
555555

556+
def test_4536_build_connect_string_with_source_route(self):
557+
"4536 - test building connect string with source route designation"
558+
host = "my_host4536"
559+
service_name = "my_service4536"
560+
options = [
561+
("on", True),
562+
("off", False),
563+
("true", True),
564+
("false", False),
565+
("yes", True),
566+
("no", False)
567+
]
568+
569+
for in_val, has_section in options:
570+
connect_string = f"""
571+
(DESCRIPTION=
572+
(SOURCE_ROUTE={in_val})
573+
(ADDRESS=(PROTOCOL=tcp)(HOST=host1)(PORT=1521))
574+
(ADDRESS=(PROTOCOL=tcp)(HOST=host2)(PORT=1522))
575+
(CONNECT_DATA=(SERVICE_NAME=my_service_35))
576+
)"""
577+
params = oracledb.ConnectParams()
578+
params.parse_connect_string(connect_string)
579+
source_route_clause = "(SOURCE_ROUTE=ON)" if has_section else ""
580+
connect_string = \
581+
f"(DESCRIPTION={source_route_clause}" + \
582+
f"(ADDRESS_LIST=" + \
583+
f"(ADDRESS=(PROTOCOL=tcp)(HOST=host1)(PORT=1521))" + \
584+
f"(ADDRESS=(PROTOCOL=tcp)(HOST=host2)(PORT=1522)))" + \
585+
f"(CONNECT_DATA=(SERVICE_NAME=my_service_35))" + \
586+
f"(SECURITY=(SSL_SERVER_DN_MATCH=ON)))"
587+
self.assertEqual(params.get_connect_string(), connect_string)
588+
556589
if __name__ == "__main__":
557590
test_env.run_test_cases()

0 commit comments

Comments
 (0)