@@ -33,7 +33,7 @@ class Connection:
3333 close() -> None:
3434 """
3535
36- def __init__ (self , connection_str : str , autocommit : bool = False , ** kwargs ) -> None :
36+ def __init__ (self , connection_str : str = "" , autocommit : bool = False , attrs_before : dict = None , ** kwargs ) -> None :
3737 """
3838 Initialize the connection object with the specified connection string and parameters.
3939
@@ -58,11 +58,12 @@ def __init__(self, connection_str: str, autocommit: bool = False, **kwargs) -> N
5858 self .connection_str = self ._construct_connection_string (
5959 connection_str , ** kwargs
6060 )
61+ self ._attrs_before = attrs_before
62+ self ._autocommit = autocommit # Initialize _autocommit before calling _initializer
6163 self ._initializer ()
62- self ._autocommit = autocommit
6364 self .setautocommit (autocommit )
6465
65- def _construct_connection_string (self , connection_str : str , ** kwargs ) -> str :
66+ def _construct_connection_string (self , connection_str : str = "" , ** kwargs ) -> str :
6667 """
6768 Construct the connection string by concatenating the connection string
6869 with key/value pairs from kwargs.
@@ -76,13 +77,14 @@ def _construct_connection_string(self, connection_str: str, **kwargs) -> str:
7677 """
7778 # Add the driver attribute to the connection string
7879 conn_str = add_driver_to_connection_str (connection_str )
80+
7981 # Add additional key-value pairs to the connection string
8082 for key , value in kwargs .items ():
81- if key .lower () == "host" :
83+ if key .lower () == "host" or key . lower () == "server" :
8284 key = "Server"
83- elif key .lower () == "user" :
85+ elif key .lower () == "user" or key . lower () == "uid" :
8486 key = "Uid"
85- elif key .lower () == "password" :
87+ elif key .lower () == "password" or key . lower () == "pwd" :
8688 key = "Pwd"
8789 elif key .lower () == "database" :
8890 key = "Database"
@@ -93,6 +95,11 @@ def _construct_connection_string(self, connection_str: str, **kwargs) -> str:
9395 else :
9496 continue
9597 conn_str += f"{ key } ={ value } ;"
98+ print (f"Connection string after adding driver: { conn_str } " )
99+
100+ if ENABLE_LOGGING :
101+ logger .info ("Final connection string: %s" , conn_str )
102+
96103 return conn_str
97104
98105 def _is_closed (self ) -> bool :
@@ -103,7 +110,7 @@ def _is_closed(self) -> bool:
103110 bool: True if the connection is closed, False otherwise.
104111 """
105112 return self .hdbc is None
106-
113+
107114 def _initializer (self ) -> None :
108115 """
109116 Initialize the environment and connection handles.
@@ -115,9 +122,79 @@ def _initializer(self) -> None:
115122 self ._allocate_environment_handle ()
116123 self ._set_environment_attributes ()
117124 self ._allocate_connection_handle ()
118- self ._set_connection_attributes ()
125+ if self ._attrs_before != {}:
126+ self ._apply_attrs_before () # Apply pre-connection attributes
127+ if self ._autocommit :
128+ self ._set_connection_attributes (
129+ ddbc_sql_const .SQL_ATTR_AUTOCOMMIT .value ,
130+ ddbc_sql_const .SQL_AUTOCOMMIT_ON .value ,
131+ )
119132 self ._connect_to_db ()
120133
134+ def _apply_attrs_before (self ):
135+ """
136+ Apply specific pre-connection attributes.
137+ Currently, this method only processes an attribute with key 1256 (e.g., SQL_COPT_SS_ACCESS_TOKEN)
138+ if present in `self._attrs_before`. Other attributes are ignored.
139+
140+ Returns:
141+ bool: True.
142+ """
143+
144+ if ENABLE_LOGGING :
145+ logger .info ("Attempting to apply pre-connection attributes (attrs_before): %s" , self ._attrs_before )
146+
147+ if not isinstance (self ._attrs_before , dict ):
148+ if self ._attrs_before is not None and ENABLE_LOGGING :
149+ logger .warning (
150+ f"_attrs_before is of type { type (self ._attrs_before ).__name__ } , "
151+ f"expected dict. Skipping attribute application."
152+ )
153+ elif self ._attrs_before is None and ENABLE_LOGGING :
154+ logger .debug ("_attrs_before is None. No pre-connection attributes to apply." )
155+ return True # Exit if _attrs_before is not a dictionary or is None
156+
157+ for key , value in self ._attrs_before .items ():
158+ ikey = None
159+ if isinstance (key , int ):
160+ ikey = key
161+ elif isinstance (key , str ) and key .isdigit ():
162+ try :
163+ ikey = int (key )
164+ except ValueError :
165+ if ENABLE_LOGGING :
166+ logger .debug (
167+ f"Skipping attribute with key '{ key } ' in attrs_before: "
168+ f"could not convert string to int."
169+ )
170+ continue # Skip if string key is not a valid integer
171+ else :
172+ if ENABLE_LOGGING :
173+ logger .debug (
174+ f"Skipping attribute with key '{ key } ' in attrs_before due to "
175+ f"unsupported key type: { type (key ).__name__ } . Expected int or string representation of an int."
176+ )
177+ continue # Skip keys that are not int or string representation of an int
178+
179+ if ikey == ddbc_sql_const .SQL_COPT_SS_ACCESS_TOKEN .value :
180+ if ENABLE_LOGGING :
181+ logger .info (
182+ f"Found attribute { ddbc_sql_const .SQL_COPT_SS_ACCESS_TOKEN .value } . Attempting to set it."
183+ )
184+ self ._set_connection_attributes (ikey , value )
185+ if ENABLE_LOGGING :
186+ logger .info (
187+ f"Call to set attribute { ddbc_sql_const .SQL_COPT_SS_ACCESS_TOKEN .value } with value '{ value } ' completed."
188+ )
189+ # If you expect only one such key, you could add 'break' here.
190+ else :
191+ if ENABLE_LOGGING :
192+ logger .debug (
193+ f"Ignoring attribute with key '{ key } ' (resolved to { ikey } ) in attrs_before "
194+ f"as it is not the target attribute ({ ddbc_sql_const .SQL_COPT_SS_ACCESS_TOKEN .value } )."
195+ )
196+ return True
197+
121198 def _allocate_environment_handle (self ):
122199 """
123200 Allocate the environment handle.
@@ -152,18 +229,25 @@ def _allocate_connection_handle(self):
152229 check_error (ddbc_sql_const .SQL_HANDLE_DBC .value , handle , ret )
153230 self .hdbc = handle
154231
155- def _set_connection_attributes (self ) :
232+ def _set_connection_attributes (self , ikey : int , ivalue : any ) -> None :
156233 """
157234 Set the connection attributes before connecting.
235+
236+ Args:
237+ ikey (int): The attribute key to set.
238+ ivalue (Any): The value to set for the attribute. Can be bytes, bytearray, int, or unicode.
239+ vallen (int): The length of the value.
240+
241+ Raises:
242+ DatabaseError: If there is an error while setting the connection attribute.
158243 """
159- if self .autocommit :
160- ret = ddbc_bindings .DDBCSQLSetConnectAttr (
161- self .hdbc , # Using the wrapper class
162- ddbc_sql_const .SQL_ATTR_AUTOCOMMIT .value ,
163- ddbc_sql_const .SQL_AUTOCOMMIT_ON .value ,
164- 0
165- )
166- check_error (ddbc_sql_const .SQL_HANDLE_DBC .value , self .hdbc , ret )
244+
245+ ret = ddbc_bindings .DDBCSQLSetConnectAttr (
246+ self .hdbc , # Connection handle
247+ ikey , # Attribute
248+ ivalue , # Value
249+ )
250+ check_error (ddbc_sql_const .SQL_HANDLE_DBC .value , self .hdbc , ret )
167251
168252 def _connect_to_db (self ) -> None :
169253 """
@@ -224,7 +308,6 @@ def autocommit(self, value: bool) -> None:
224308 if value
225309 else ddbc_sql_const .SQL_AUTOCOMMIT_OFF .value
226310 ), # Value
227- 0 , # String length
228311 )
229312 check_error (ddbc_sql_const .SQL_HANDLE_DBC .value , self .hdbc , ret )
230313 self ._autocommit = value
0 commit comments