Skip to content

Commit d73e43a

Browse files
Fixed bug when attempting to create bequeath connections to a local
database (#114).
1 parent cc52daf commit d73e43a

File tree

4 files changed

+91
-61
lines changed

4 files changed

+91
-61
lines changed

doc/src/release_notes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ Thin Mode Changes
2222
Thick Mode Changes
2323
++++++++++++++++++
2424

25+
#) Fixed bug when attempting to create bequeath connections to a local
26+
database
27+
(`issue 114 <https://github.com/oracle/python-oracledb/issues/114>`__).
28+
2529
Common Changes
2630
++++++++++++++
2731

src/oracledb/base_impl.pxd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
2+
# Copyright (c) 2020, 2023, 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
@@ -115,6 +115,7 @@ cdef class AddressList:
115115
bint load_balance
116116
int lru_index
117117

118+
cdef bint _uses_tcps(self)
118119
cdef str build_connect_string(self)
119120

120121

@@ -191,7 +192,6 @@ cdef class ConnectParamsImpl:
191192
bytearray _token_obfuscator
192193
bytearray _private_key
193194
bytearray _private_key_obfuscator
194-
bint _has_components
195195

196196
cdef int _check_credentials(self) except -1
197197
cdef int _copy(self, ConnectParamsImpl other_params) except -1

src/oracledb/impl/base/connect_params.pyx

Lines changed: 73 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2022, Oracle and/or its affiliates.
2+
# Copyright (c) 2022, 2023, 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
@@ -169,8 +169,6 @@ cdef class ConnectParamsImpl:
169169
self._default_address.set_from_args(args)
170170
_set_bool_param(args, "externalauth", &self.externalauth)
171171
self._set_access_token_param(args.get("access_token"))
172-
if args:
173-
self._has_components = True
174172

175173
cdef int _check_credentials(self) except -1:
176174
"""
@@ -344,7 +342,6 @@ cdef class ConnectParamsImpl:
344342
# to be a full connect descriptor
345343
if connect_string.startswith("("):
346344
_parse_connect_descriptor(connect_string, args)
347-
self._has_components = True
348345
return self._process_connect_descriptor(args)
349346

350347
# otherwise, see if the connect string is an EasyConnect string
@@ -389,9 +386,6 @@ cdef class ConnectParamsImpl:
389386
_parse_connect_descriptor(connect_string, args)
390387
self._process_connect_descriptor(args)
391388

392-
# mark that object has components
393-
self._has_components = True
394-
395389
cdef int _process_connect_descriptor(self, dict args) except -1:
396390
"""
397391
Internal method used for processing the parsed connect descriptor into
@@ -538,8 +532,7 @@ cdef class ConnectParamsImpl:
538532
will be a connect string built up from the components supplied when the
539533
object was built.
540534
"""
541-
if self._has_components:
542-
return self.description_list.build_connect_string()
535+
return self.description_list.build_connect_string()
543536

544537
def get_full_user(self):
545538
"""
@@ -613,16 +606,18 @@ cdef class Address:
613606

614607
cdef str build_connect_string(self):
615608
"""
616-
Build a connect string from the components.
609+
Build a connect string from the components. If no host is specified,
610+
None is returned (used for bequeath connections).
617611
"""
618-
parts = [f"(PROTOCOL={self.protocol})",
619-
f"(HOST={self.host})",
620-
f"(PORT={self.port})"]
621-
if self.https_proxy is not None:
622-
parts.append(f"(HTTPS_PROXY={self.https_proxy})")
623-
if self.https_proxy_port != 0:
624-
parts.append(f"(HTTPS_PROXY_PORT={self.https_proxy_port})")
625-
return f'(ADDRESS={"".join(parts)})'
612+
if self.host is not None:
613+
parts = [f"(PROTOCOL={self.protocol})",
614+
f"(HOST={self.host})",
615+
f"(PORT={self.port})"]
616+
if self.https_proxy is not None:
617+
parts.append(f"(HTTPS_PROXY={self.https_proxy})")
618+
if self.https_proxy_port != 0:
619+
parts.append(f"(HTTPS_PROXY_PORT={self.https_proxy_port})")
620+
return f'(ADDRESS={"".join(parts)})'
626621

627622
def copy(self):
628623
"""
@@ -667,12 +662,25 @@ cdef class AddressList:
667662
def __init__(self):
668663
self.addresses = []
669664

665+
cdef bint _uses_tcps(self):
666+
"""
667+
Returns a boolean indicating if any of the addresses in the address
668+
list use the protocol TCPS.
669+
"""
670+
cdef Address address
671+
for address in self.addresses:
672+
if address.protocol == "tcps":
673+
return True
674+
return False
675+
670676
cdef str build_connect_string(self):
671677
"""
672678
Build a connect string from the components.
673679
"""
674680
cdef Address a
675681
parts = [a.build_connect_string() for a in self.addresses]
682+
if len(parts) == 1:
683+
return parts[0]
676684
return f'(ADDRESS_LIST={"".join(parts)})'
677685

678686
def set_from_args(self, dict args):
@@ -713,37 +721,12 @@ cdef class Description:
713721
Build a connect string from the components.
714722
"""
715723
cdef:
716-
str connect_data, security, temp
717-
list parts, address_lists
718-
AddressList a
719-
720-
# build connect data segment
721-
parts = []
722-
if self.service_name is not None:
723-
parts.append(f"(SERVICE_NAME={self.service_name})")
724-
elif self.sid is not None:
725-
parts.append(f"(SID={self.sid})")
726-
if self.server_type is not None:
727-
parts.append(f"(SERVER={self.server_type})")
728-
if self.cclass is not None:
729-
parts.append(f"(POOL_CONNECTION_CLASS={self.cclass})")
730-
if self.purity != 0:
731-
parts.append(f"(POOL_PURITY={self.purity})")
732-
if cid is not None:
733-
parts.append(f"(CID={cid})")
734-
connect_data = f'(CONNECT_DATA={"".join(parts)})'
724+
AddressList address_list
725+
list parts, temp_parts
726+
bint uses_tcps = False
727+
str temp
735728

736-
# build security segment, if applicable
737-
parts = []
738-
if self.ssl_server_dn_match:
739-
parts.append("(SSL_SERVER_DN_MATCH=ON)")
740-
if self.ssl_server_cert_dn is not None:
741-
parts.append(f"(SSL_SERVER_CERT_DN={self.ssl_server_cert_dn})")
742-
if self.wallet_location is not None:
743-
parts.append(f"(MY_WALLET_DIRECTORY={self.wallet_location})")
744-
security = f'(SECURITY={"".join(parts)})'
745-
746-
# build connect string
729+
# build top-level description parts
747730
parts = []
748731
if self.load_balance:
749732
parts.append("(LOAD_BALANCE=ON)")
@@ -758,10 +741,48 @@ cdef class Description:
758741
if self.tcp_connect_timeout != DEFAULT_TCP_CONNECT_TIMEOUT:
759742
temp = self._build_duration_str(self.tcp_connect_timeout)
760743
parts.append(f"(TRANSPORT_CONNECT_TIMEOUT={temp})")
761-
address_lists = [a.build_connect_string() for a in self.address_lists]
762-
parts.extend(address_lists)
763-
parts.append(connect_data)
764-
parts.append(security)
744+
745+
# add address lists, but if the address list contains only a single
746+
# entry and that entry does not have a host, the other parts aren't
747+
# relevant anyway!
748+
for address_list in self.address_lists:
749+
temp = address_list.build_connect_string()
750+
if temp is None:
751+
return None
752+
parts.append(temp)
753+
if not uses_tcps:
754+
uses_tcps = address_list._uses_tcps()
755+
756+
# build connect data segment
757+
temp_parts = []
758+
if self.service_name is not None:
759+
temp_parts.append(f"(SERVICE_NAME={self.service_name})")
760+
elif self.sid is not None:
761+
temp_parts.append(f"(SID={self.sid})")
762+
if self.server_type is not None:
763+
temp_parts.append(f"(SERVER={self.server_type})")
764+
if self.cclass is not None:
765+
temp_parts.append(f"(POOL_CONNECTION_CLASS={self.cclass})")
766+
if self.purity != 0:
767+
temp_parts.append(f"(POOL_PURITY={self.purity})")
768+
if cid is not None:
769+
temp_parts.append(f"(CID={cid})")
770+
if temp_parts:
771+
parts.append(f'(CONNECT_DATA={"".join(temp_parts)})')
772+
773+
# build security segment, if applicable
774+
if uses_tcps:
775+
temp_parts = []
776+
if self.ssl_server_dn_match:
777+
temp_parts.append("(SSL_SERVER_DN_MATCH=ON)")
778+
if self.ssl_server_cert_dn is not None:
779+
temp = f"(SSL_SERVER_CERT_DN={self.ssl_server_cert_dn})"
780+
temp_parts.append(temp)
781+
if self.wallet_location is not None:
782+
temp = f"(MY_WALLET_DIRECTORY={self.wallet_location})"
783+
temp_parts.append(temp)
784+
parts.append(f'(SECURITY={"".join(temp_parts)})')
785+
765786
return f'(DESCRIPTION={"".join(parts)})'
766787

767788
def copy(self):

tests/test_4500_connect_params.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2021, 2022, Oracle and/or its affiliates.
2+
# Copyright (c) 2021, 2023, 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
@@ -478,10 +478,9 @@ def test_4531_build_connect_string_with_tcp_connect_timeout(self):
478478
tcp_connect_timeout=in_val)
479479
tcp_timeout_val = f"(TRANSPORT_CONNECT_TIMEOUT={out_val})"
480480
connect_string = f"(DESCRIPTION={tcp_timeout_val}" + \
481-
f"(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)" + \
482-
f"(HOST={host})(PORT=1521)))(CONNECT_DATA=" + \
483-
f"(SERVICE_NAME={service_name}))" + \
484-
f"(SECURITY=(SSL_SERVER_DN_MATCH=ON)))"
481+
f"(ADDRESS=(PROTOCOL=tcp)" + \
482+
f"(HOST={host})(PORT=1521))(CONNECT_DATA=" + \
483+
f"(SERVICE_NAME={service_name})))"
485484
self.assertEqual(params.get_connect_string(), connect_string)
486485

487486
def test_4532_multiple_alias_entry_tnsnames(self):
@@ -582,9 +581,15 @@ def test_4536_build_connect_string_with_source_route(self):
582581
f"(ADDRESS_LIST=" + \
583582
f"(ADDRESS=(PROTOCOL=tcp)(HOST=host1)(PORT=1521))" + \
584583
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)))"
584+
f"(CONNECT_DATA=(SERVICE_NAME=my_service_35)))"
587585
self.assertEqual(params.get_connect_string(), connect_string)
588586

587+
def test_4537_no_connect_string(self):
588+
"4537 - test connect parameters which generate no connect string"
589+
params = oracledb.ConnectParams()
590+
self.assertEqual(params.get_connect_string(), None)
591+
params.set(mode=oracledb.SYSDBA)
592+
self.assertEqual(params.get_connect_string(), None)
593+
589594
if __name__ == "__main__":
590595
test_env.run_test_cases()

0 commit comments

Comments
 (0)