From fda52fff10a406fa09b52f31125b4faf7517c7ec Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Tue, 13 May 2025 16:23:02 +0530 Subject: [PATCH 1/6] FEAT: Access Token Login --- mssql_python/connection.py | 112 +++++++++++++++++++------- mssql_python/constants.py | 2 + mssql_python/db_connection.py | 4 +- mssql_python/pybind/ddbc_bindings.cpp | 48 ++++++++++- 4 files changed, 129 insertions(+), 37 deletions(-) diff --git a/mssql_python/connection.py b/mssql_python/connection.py index c41d71bd..fc3d6844 100644 --- a/mssql_python/connection.py +++ b/mssql_python/connection.py @@ -33,7 +33,7 @@ class Connection: close() -> None: """ - def __init__(self, connection_str: str, autocommit: bool = False, **kwargs) -> None: + def __init__(self, connection_str: str, autocommit: bool = False, attrs_before: dict = {}, **kwargs) -> None: """ Initialize the connection object with the specified connection string and parameters. @@ -58,8 +58,9 @@ def __init__(self, connection_str: str, autocommit: bool = False, **kwargs) -> N self.connection_str = self._construct_connection_string( connection_str, **kwargs ) + self._attrs_before = attrs_before + self._autocommit = autocommit # Initialize _autocommit before calling _initializer self._initializer() - self._autocommit = autocommit self.setautocommit(autocommit) def _construct_connection_string(self, connection_str: str, **kwargs) -> str: @@ -76,23 +77,34 @@ def _construct_connection_string(self, connection_str: str, **kwargs) -> str: """ # Add the driver attribute to the connection string conn_str = add_driver_to_connection_str(connection_str) - # Add additional key-value pairs to the connection string - for key, value in kwargs.items(): - if key.lower() == "host": - key = "Server" - elif key.lower() == "user": - key = "Uid" - elif key.lower() == "password": - key = "Pwd" - elif key.lower() == "database": - key = "Database" - elif key.lower() == "encrypt": - key = "Encrypt" - elif key.lower() == "trust_server_certificate": - key = "TrustServerCertificate" - else: - continue - conn_str += f"{key}={value};" + + # Check if access token authentication is being used + if "attrs_before" in kwargs: + # Skip adding Uid and Pwd for access token authentication + if ENABLE_LOGGING: + logger.info("Using access token authentication. Skipping Uid and Pwd.") + else: + # Add additional key-value pairs to the connection string + for key, value in kwargs.items(): + if key.lower() == "host": + key = "Server" + elif key.lower() == "user": + key = "Uid" + elif key.lower() == "password": + key = "Pwd" + elif key.lower() == "database": + key = "Database" + elif key.lower() == "encrypt": + key = "Encrypt" + elif key.lower() == "trust_server_certificate": + key = "TrustServerCertificate" + else: + continue + conn_str += f"{key}={value};" + + if ENABLE_LOGGING: + logger.info("Final connection string: %s", conn_str) + return conn_str def _is_closed(self) -> bool: @@ -103,7 +115,7 @@ def _is_closed(self) -> bool: bool: True if the connection is closed, False otherwise. """ return self.hdbc is None - + def _initializer(self) -> None: """ Initialize the environment and connection handles. @@ -115,9 +127,41 @@ def _initializer(self) -> None: self._allocate_environment_handle() self._set_environment_attributes() self._allocate_connection_handle() - self._set_connection_attributes() + if self._attrs_before != {}: + self._apply_attrs_before() # Apply pre-connection attributes + if self._autocommit: + self._set_connection_attributes( + ddbc_sql_const.SQL_ATTR_AUTOCOMMIT.value, + ddbc_sql_const.SQL_AUTOCOMMIT_ON.value, + ) self._connect_to_db() + + def _apply_attrs_before(self): + """ + Apply a dictionary of attributes to the database connection before connecting. + + Returns: + bool: True if all attributes were successfully applied, False otherwise. + """ + strencoding = "utf-16le" + + if ENABLE_LOGGING: + logger.info("Applying attrs_before: %s", self._attrs_before) + + for key, value in self._attrs_before.items(): + if isinstance(key, int): + ikey = key + elif isinstance(key, str) and key.isdigit(): + ikey = int(key) + else: + raise TypeError(f"Unsupported key type: {type(key).__name__}") + + if not self._set_connection_attributes(ikey, value): + return False + + return True + def _allocate_environment_handle(self): """ Allocate the environment handle. @@ -152,18 +196,25 @@ def _allocate_connection_handle(self): check_error(ddbc_sql_const.SQL_HANDLE_DBC.value, handle, ret) self.hdbc = handle - def _set_connection_attributes(self): + def _set_connection_attributes(self, ikey: int, ivalue: any) -> None: """ Set the connection attributes before connecting. + + Args: + ikey (int): The attribute key to set. + ivalue (Any): The value to set for the attribute. Can be bytes, bytearray, int, or unicode. + vallen (int): The length of the value. + + Raises: + DatabaseError: If there is an error while setting the connection attribute. """ - if self.autocommit: - ret = ddbc_bindings.DDBCSQLSetConnectAttr( - self.hdbc, # Using the wrapper class - ddbc_sql_const.SQL_ATTR_AUTOCOMMIT.value, - ddbc_sql_const.SQL_AUTOCOMMIT_ON.value, - 0 - ) - check_error(ddbc_sql_const.SQL_HANDLE_DBC.value, self.hdbc, ret) + + ret = ddbc_bindings.DDBCSQLSetConnectAttr( + self.hdbc, # Connection handle + ikey, # Attribute + ivalue, # Value + ) + check_error(ddbc_sql_const.SQL_HANDLE_DBC.value, self.hdbc, ret) def _connect_to_db(self) -> None: """ @@ -224,7 +275,6 @@ def autocommit(self, value: bool) -> None: if value else ddbc_sql_const.SQL_AUTOCOMMIT_OFF.value ), # Value - 0, # String length ) check_error(ddbc_sql_const.SQL_HANDLE_DBC.value, self.hdbc, ret) self._autocommit = value diff --git a/mssql_python/constants.py b/mssql_python/constants.py index aade503c..7fd15d36 100644 --- a/mssql_python/constants.py +++ b/mssql_python/constants.py @@ -116,3 +116,5 @@ class ConstantsDDBC(Enum): SQL_C_WCHAR = -8 SQL_NULLABLE = 1 SQL_MAX_NUMERIC_LEN = 16 + SQL_IS_POINTER = -4 + \ No newline at end of file diff --git a/mssql_python/db_connection.py b/mssql_python/db_connection.py index e02bafce..085027c2 100644 --- a/mssql_python/db_connection.py +++ b/mssql_python/db_connection.py @@ -6,7 +6,7 @@ from mssql_python.connection import Connection -def connect(connection_str: str, autocommit: bool = True, **kwargs) -> Connection: +def connect(connection_str: str, autocommit: bool = True, attrs_before: dict = {}, **kwargs) -> Connection: """ Constructor for creating a connection to the database. @@ -34,5 +34,5 @@ def connect(connection_str: str, autocommit: bool = True, **kwargs) -> Connectio be used to perform database operations such as executing queries, committing transactions, and closing the connection. """ - conn = Connection(connection_str, autocommit=autocommit, **kwargs) + conn = Connection(connection_str, autocommit=autocommit, attrs_before=attrs_before, **kwargs) return conn diff --git a/mssql_python/pybind/ddbc_bindings.cpp b/mssql_python/pybind/ddbc_bindings.cpp index d7dbfa22..2b4dab8d 100644 --- a/mssql_python/pybind/ddbc_bindings.cpp +++ b/mssql_python/pybind/ddbc_bindings.cpp @@ -692,18 +692,58 @@ SQLRETURN SQLSetEnvAttr_wrap(SqlHandlePtr EnvHandle, SQLINTEGER Attribute, intpt } // Wrap SQLSetConnectAttr -SQLRETURN SQLSetConnectAttr_wrap(SqlHandlePtr ConnectionHandle, SQLINTEGER Attribute, intptr_t ValuePtr, - SQLINTEGER StringLength) { +SQLRETURN SQLSetConnectAttr_wrap(SqlHandlePtr ConnectionHandle, SQLINTEGER Attribute, + py::object ValuePtr) { LOG("Set SQL Connection Attribute"); if (!SQLSetConnectAttr_ptr) { LoadDriverOrThrowException(); } - // TODO: Does ValuePtr need to be converted from Python to C++ object? - SQLRETURN ret = SQLSetConnectAttr_ptr(ConnectionHandle->get(), Attribute, reinterpret_cast(ValuePtr), StringLength); + // Print the type of ValuePtr and attribute value - helpful for debugging + LOG("Type of ValuePtr: {}, Attribute: {}", py::type::of(ValuePtr).attr("__name__").cast(), Attribute); + + SQLPOINTER value = 0; + SQLINTEGER length = 0; + + if (py::isinstance(ValuePtr)) { + // Handle integer values + int intValue = ValuePtr.cast(); + value = reinterpret_cast(intValue); + length = SQL_IS_INTEGER; // Integer values don't require a length + } else if (py::isinstance(ValuePtr)) { + // Handle Unicode string values + static std::wstring unicodeValueBuffer; + unicodeValueBuffer = ValuePtr.cast(); + value = const_cast(unicodeValueBuffer.c_str()); + length = SQL_NTS; // Indicates null-terminated string + } else if (py::isinstance(ValuePtr) || py::isinstance(ValuePtr)) { + // Handle byte or bytearray values (like access tokens) + // Store in static buffer to ensure memory remains valid during connection + static std::vector bytesBuffers; + bytesBuffers.push_back(ValuePtr.cast()); + value = const_cast(bytesBuffers.back().c_str()); + length = SQL_IS_POINTER; // Indicates we're passing a pointer (required for token) + } else if (py::isinstance(ValuePtr) || py::isinstance(ValuePtr)) { + // Handle list or tuple values + LOG("ValuePtr is a sequence (list or tuple)"); + for (py::handle item : ValuePtr) { + LOG("Processing item in sequence"); + SQLRETURN ret = SQLSetConnectAttr_wrap(ConnectionHandle, Attribute, py::reinterpret_borrow(item)); + if (!SQL_SUCCEEDED(ret)) { + LOG("Failed to set attribute for item in sequence"); + return ret; + } + } + } else { + LOG("Unsupported ValuePtr type"); + return SQL_ERROR; + } + + SQLRETURN ret = SQLSetConnectAttr_ptr(ConnectionHandle->get(), Attribute, value, length); if (!SQL_SUCCEEDED(ret)) { LOG("Failed to set Connection attribute"); } + LOG("Set Connection attribute successfully"); return ret; } From 00a7c353292d587bc3e86093d17f5ef9217fdc9b Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Wed, 14 May 2025 10:05:25 +0530 Subject: [PATCH 2/6] FEAT: Access Token Login --- mssql_python/connection.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mssql_python/connection.py b/mssql_python/connection.py index fc3d6844..7e9e5969 100644 --- a/mssql_python/connection.py +++ b/mssql_python/connection.py @@ -157,8 +157,7 @@ def _apply_attrs_before(self): else: raise TypeError(f"Unsupported key type: {type(key).__name__}") - if not self._set_connection_attributes(ikey, value): - return False + self._set_connection_attributes(ikey, value) return True From 1c78d74c8b515b30f954e1903968aecc8aea991a Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Wed, 14 May 2025 14:45:12 +0530 Subject: [PATCH 3/6] FEAT: Access Token Login --- mssql_python/connection.py | 101 +++++++++++++++++--------- mssql_python/constants.py | 2 +- mssql_python/db_connection.py | 2 +- mssql_python/pybind/ddbc_bindings.cpp | 34 ++++----- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/mssql_python/connection.py b/mssql_python/connection.py index 7e9e5969..2711199e 100644 --- a/mssql_python/connection.py +++ b/mssql_python/connection.py @@ -33,7 +33,7 @@ class Connection: close() -> None: """ - def __init__(self, connection_str: str, autocommit: bool = False, attrs_before: dict = {}, **kwargs) -> None: + def __init__(self, connection_str: str, autocommit: bool = False, attrs_before: dict = None, **kwargs) -> None: """ Initialize the connection object with the specified connection string and parameters. @@ -78,29 +78,23 @@ def _construct_connection_string(self, connection_str: str, **kwargs) -> str: # Add the driver attribute to the connection string conn_str = add_driver_to_connection_str(connection_str) - # Check if access token authentication is being used - if "attrs_before" in kwargs: - # Skip adding Uid and Pwd for access token authentication - if ENABLE_LOGGING: - logger.info("Using access token authentication. Skipping Uid and Pwd.") - else: - # Add additional key-value pairs to the connection string - for key, value in kwargs.items(): - if key.lower() == "host": - key = "Server" - elif key.lower() == "user": - key = "Uid" - elif key.lower() == "password": - key = "Pwd" - elif key.lower() == "database": - key = "Database" - elif key.lower() == "encrypt": - key = "Encrypt" - elif key.lower() == "trust_server_certificate": - key = "TrustServerCertificate" - else: - continue - conn_str += f"{key}={value};" + # Add additional key-value pairs to the connection string + for key, value in kwargs.items(): + if key.lower() == "host": + key = "Server" + elif key.lower() == "user": + key = "Uid" + elif key.lower() == "password": + key = "Pwd" + elif key.lower() == "database": + key = "Database" + elif key.lower() == "encrypt": + key = "Encrypt" + elif key.lower() == "trust_server_certificate": + key = "TrustServerCertificate" + else: + continue + conn_str += f"{key}={value};" if ENABLE_LOGGING: logger.info("Final connection string: %s", conn_str) @@ -136,29 +130,68 @@ def _initializer(self) -> None: ) self._connect_to_db() - def _apply_attrs_before(self): """ - Apply a dictionary of attributes to the database connection before connecting. + Apply specific pre-connection attributes. + Currently, this method only processes an attribute with key 1256 (e.g., SQL_COPT_SS_ACCESS_TOKEN) + if present in `self._attrs_before`. Other attributes are ignored. Returns: - bool: True if all attributes were successfully applied, False otherwise. + bool: True. """ - strencoding = "utf-16le" if ENABLE_LOGGING: - logger.info("Applying attrs_before: %s", self._attrs_before) + logger.info("Attempting to apply pre-connection attributes (attrs_before): %s", self._attrs_before) + + if not isinstance(self._attrs_before, dict): + if self._attrs_before is not None and ENABLE_LOGGING: + logger.warning( + f"_attrs_before is of type {type(self._attrs_before).__name__}, " + f"expected dict. Skipping attribute application." + ) + elif self._attrs_before is None and ENABLE_LOGGING: + logger.debug("_attrs_before is None. No pre-connection attributes to apply.") + return True # Exit if _attrs_before is not a dictionary or is None for key, value in self._attrs_before.items(): + ikey = None if isinstance(key, int): ikey = key elif isinstance(key, str) and key.isdigit(): - ikey = int(key) + try: + ikey = int(key) + except ValueError: + if ENABLE_LOGGING: + logger.debug( + f"Skipping attribute with key '{key}' in attrs_before: " + f"could not convert string to int." + ) + continue # Skip if string key is not a valid integer else: - raise TypeError(f"Unsupported key type: {type(key).__name__}") - - self._set_connection_attributes(ikey, value) - + if ENABLE_LOGGING: + logger.debug( + f"Skipping attribute with key '{key}' in attrs_before due to " + f"unsupported key type: {type(key).__name__}. Expected int or string representation of an int." + ) + continue # Skip keys that are not int or string representation of an int + + if ikey == ddbc_sql_const.SQL_COPT_SS_ACCESS_TOKEN.value: + if ENABLE_LOGGING: + logger.info( + f"Found attribute {ddbc_sql_const.SQL_COPT_SS_ACCESS_TOKEN.value}. Attempting to set it." + ) + self._set_connection_attributes(ikey, value) + if ENABLE_LOGGING: + logger.info( + f"Call to set attribute {ddbc_sql_const.SQL_COPT_SS_ACCESS_TOKEN.value} with value '{value}' completed." + ) + # If you expect only one such key, you could add 'break' here. + else: + if ENABLE_LOGGING: + logger.debug( + f"Ignoring attribute with key '{key}' (resolved to {ikey}) in attrs_before " + f"as it is not the target attribute ({ddbc_sql_const.SQL_COPT_SS_ACCESS_TOKEN.value})." + ) return True def _allocate_environment_handle(self): diff --git a/mssql_python/constants.py b/mssql_python/constants.py index 7fd15d36..1f479ebd 100644 --- a/mssql_python/constants.py +++ b/mssql_python/constants.py @@ -117,4 +117,4 @@ class ConstantsDDBC(Enum): SQL_NULLABLE = 1 SQL_MAX_NUMERIC_LEN = 16 SQL_IS_POINTER = -4 - \ No newline at end of file + SQL_COPT_SS_ACCESS_TOKEN = 1256 \ No newline at end of file diff --git a/mssql_python/db_connection.py b/mssql_python/db_connection.py index 085027c2..47f1f600 100644 --- a/mssql_python/db_connection.py +++ b/mssql_python/db_connection.py @@ -6,7 +6,7 @@ from mssql_python.connection import Connection -def connect(connection_str: str, autocommit: bool = True, attrs_before: dict = {}, **kwargs) -> Connection: +def connect(connection_str: str, autocommit: bool = True, attrs_before: dict = None, **kwargs) -> Connection: """ Constructor for creating a connection to the database. diff --git a/mssql_python/pybind/ddbc_bindings.cpp b/mssql_python/pybind/ddbc_bindings.cpp index 2b4dab8d..c237e2f0 100644 --- a/mssql_python/pybind/ddbc_bindings.cpp +++ b/mssql_python/pybind/ddbc_bindings.cpp @@ -710,12 +710,12 @@ SQLRETURN SQLSetConnectAttr_wrap(SqlHandlePtr ConnectionHandle, SQLINTEGER Attri int intValue = ValuePtr.cast(); value = reinterpret_cast(intValue); length = SQL_IS_INTEGER; // Integer values don't require a length - } else if (py::isinstance(ValuePtr)) { - // Handle Unicode string values - static std::wstring unicodeValueBuffer; - unicodeValueBuffer = ValuePtr.cast(); - value = const_cast(unicodeValueBuffer.c_str()); - length = SQL_NTS; // Indicates null-terminated string + // } else if (py::isinstance(ValuePtr)) { + // // Handle Unicode string values + // static std::wstring unicodeValueBuffer; + // unicodeValueBuffer = ValuePtr.cast(); + // value = const_cast(unicodeValueBuffer.c_str()); + // length = SQL_NTS; // Indicates null-terminated string } else if (py::isinstance(ValuePtr) || py::isinstance(ValuePtr)) { // Handle byte or bytearray values (like access tokens) // Store in static buffer to ensure memory remains valid during connection @@ -723,17 +723,17 @@ SQLRETURN SQLSetConnectAttr_wrap(SqlHandlePtr ConnectionHandle, SQLINTEGER Attri bytesBuffers.push_back(ValuePtr.cast()); value = const_cast(bytesBuffers.back().c_str()); length = SQL_IS_POINTER; // Indicates we're passing a pointer (required for token) - } else if (py::isinstance(ValuePtr) || py::isinstance(ValuePtr)) { - // Handle list or tuple values - LOG("ValuePtr is a sequence (list or tuple)"); - for (py::handle item : ValuePtr) { - LOG("Processing item in sequence"); - SQLRETURN ret = SQLSetConnectAttr_wrap(ConnectionHandle, Attribute, py::reinterpret_borrow(item)); - if (!SQL_SUCCEEDED(ret)) { - LOG("Failed to set attribute for item in sequence"); - return ret; - } - } + // } else if (py::isinstance(ValuePtr) || py::isinstance(ValuePtr)) { + // // Handle list or tuple values + // LOG("ValuePtr is a sequence (list or tuple)"); + // for (py::handle item : ValuePtr) { + // LOG("Processing item in sequence"); + // SQLRETURN ret = SQLSetConnectAttr_wrap(ConnectionHandle, Attribute, py::reinterpret_borrow(item)); + // if (!SQL_SUCCEEDED(ret)) { + // LOG("Failed to set attribute for item in sequence"); + // return ret; + // } + // } } else { LOG("Unsupported ValuePtr type"); return SQL_ERROR; From 3671bb8ee0724991ce0b13c375904f36818bd257 Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Wed, 14 May 2025 15:08:04 +0530 Subject: [PATCH 4/6] FEAT: Access Token Login --- mssql_python/connection.py | 7 ++++--- mssql_python/db_connection.py | 2 +- mssql_python/helpers.py | 1 + tests/test_003_connection.py | 28 +++++++++++++++++++++++++++- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/mssql_python/connection.py b/mssql_python/connection.py index 2711199e..65e5776c 100644 --- a/mssql_python/connection.py +++ b/mssql_python/connection.py @@ -80,11 +80,11 @@ def _construct_connection_string(self, connection_str: str, **kwargs) -> str: # Add additional key-value pairs to the connection string for key, value in kwargs.items(): - if key.lower() == "host": + if key.lower() == "host" or key.lower() == "server": key = "Server" - elif key.lower() == "user": + elif key.lower() == "user" or key.lower() == "uid": key = "Uid" - elif key.lower() == "password": + elif key.lower() == "password" or key.lower() == "pwd": key = "Pwd" elif key.lower() == "database": key = "Database" @@ -95,6 +95,7 @@ def _construct_connection_string(self, connection_str: str, **kwargs) -> str: else: continue conn_str += f"{key}={value};" + print(f"Connection string after adding driver: {conn_str}") if ENABLE_LOGGING: logger.info("Final connection string: %s", conn_str) diff --git a/mssql_python/db_connection.py b/mssql_python/db_connection.py index 47f1f600..4d1a311e 100644 --- a/mssql_python/db_connection.py +++ b/mssql_python/db_connection.py @@ -6,7 +6,7 @@ from mssql_python.connection import Connection -def connect(connection_str: str, autocommit: bool = True, attrs_before: dict = None, **kwargs) -> Connection: +def connect(connection_str: str = "", autocommit: bool = True, attrs_before: dict = None, **kwargs) -> Connection: """ Constructor for creating a connection to the database. diff --git a/mssql_python/helpers.py b/mssql_python/helpers.py index a9bfeaf5..c60a3f3e 100644 --- a/mssql_python/helpers.py +++ b/mssql_python/helpers.py @@ -46,6 +46,7 @@ def add_driver_to_connection_str(connection_str): # Insert the driver attribute at the beginning of the connection string final_connection_attributes.insert(0, driver_name) connection_str = ";".join(final_connection_attributes) + print(f"Connection string after adding driver: {connection_str}") except Exception as e: raise Exception( "Invalid connection string, Please follow the format: " diff --git a/tests/test_003_connection.py b/tests/test_003_connection.py index 2d63a1d8..c192da68 100644 --- a/tests/test_003_connection.py +++ b/tests/test_003_connection.py @@ -33,7 +33,33 @@ def test_connection(db_connection): def test_construct_connection_string(db_connection): # Check if the connection string is constructed correctly with kwargs - conn_str = db_connection._construct_connection_string("",host="localhost", user="me", password="mypwd", database="mydb", encrypt="yes", trust_server_certificate="yes") + conn_str = db_connection._construct_connection_string(host="localhost", user="me", password="mypwd", database="mydb", encrypt="yes", trust_server_certificate="yes") + assert "Server=localhost;" in conn_str, "Connection string should contain 'Server=localhost;'" + assert "Uid=me;" in conn_str, "Connection string should contain 'Uid=me;'" + assert "Pwd=mypwd;" in conn_str, "Connection string should contain 'Pwd=mypwd;'" + assert "Database=mydb;" in conn_str, "Connection string should contain 'Database=mydb;'" + assert "Encrypt=yes;" in conn_str, "Connection string should contain 'Encrypt=yes;'" + assert "TrustServerCertificate=yes;" in conn_str, "Connection string should contain 'TrustServerCertificate=yes;'" + assert "APP=MSSQL-Python" in conn_str, "Connection string should contain 'APP=MSSQL-Python'" + assert "Driver={ODBC Driver 18 for SQL Server}" in conn_str, "Connection string should contain 'Driver={ODBC Driver 18 for SQL Server}'" + assert "Driver={ODBC Driver 18 for SQL Server};;APP=MSSQL-Python;Server=localhost;Uid=me;Pwd=mypwd;Database=mydb;Encrypt=yes;TrustServerCertificate=yes;" == conn_str, "Connection string is incorrect" + +def test_connection_string_with_attrs_before(db_connection): + # Check if the connection string is constructed correctly with attrs_before + conn_str = db_connection._construct_connection_string(host="localhost", user="me", password="mypwd", database="mydb", encrypt="yes", trust_server_certificate="yes", attrs_before={1256: "token"}) + assert "Server=localhost;" in conn_str, "Connection string should contain 'Server=localhost;'" + assert "Uid=me;" in conn_str, "Connection string should contain 'Uid=me;'" + assert "Pwd=mypwd;" in conn_str, "Connection string should contain 'Pwd=mypwd;'" + assert "Database=mydb;" in conn_str, "Connection string should contain 'Database=mydb;'" + assert "Encrypt=yes;" in conn_str, "Connection string should contain 'Encrypt=yes;'" + assert "TrustServerCertificate=yes;" in conn_str, "Connection string should contain 'TrustServerCertificate=yes;'" + assert "APP=MSSQL-Python" in conn_str, "Connection string should contain 'APP=MSSQL-Python'" + assert "Driver={ODBC Driver 18 for SQL Server}" in conn_str, "Connection string should contain 'Driver={ODBC Driver 18 for SQL Server}'" + assert "{1256: token}" in conn_str, "Connection string should contain '{1256: token}'" + +def test_connection_string_with_odbc_param(db_connection): + # Check if the connection string is constructed correctly with ODBC parameters + conn_str = db_connection._construct_connection_string(server="localhost", uid="me", pwd="mypwd", database="mydb", encrypt="yes", trust_server_certificate="yes") assert "Server=localhost;" in conn_str, "Connection string should contain 'Server=localhost;'" assert "Uid=me;" in conn_str, "Connection string should contain 'Uid=me;'" assert "Pwd=mypwd;" in conn_str, "Connection string should contain 'Pwd=mypwd;'" From a7282d9faca5304fccf0cbbbb8173c21b04a5dfd Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Wed, 14 May 2025 15:19:16 +0530 Subject: [PATCH 5/6] FEAT: Access Token Login --- mssql_python/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mssql_python/connection.py b/mssql_python/connection.py index 65e5776c..08574b0a 100644 --- a/mssql_python/connection.py +++ b/mssql_python/connection.py @@ -33,7 +33,7 @@ class Connection: close() -> None: """ - def __init__(self, connection_str: str, autocommit: bool = False, attrs_before: dict = None, **kwargs) -> None: + def __init__(self, connection_str: str = "", autocommit: bool = False, attrs_before: dict = None, **kwargs) -> None: """ Initialize the connection object with the specified connection string and parameters. From a10cb00b995ee697dd83ab4ed2b69b6892c7d2fc Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Wed, 14 May 2025 15:32:00 +0530 Subject: [PATCH 6/6] FEAT: Access Token Login --- mssql_python/connection.py | 2 +- tests/test_003_connection.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mssql_python/connection.py b/mssql_python/connection.py index 08574b0a..bab00e43 100644 --- a/mssql_python/connection.py +++ b/mssql_python/connection.py @@ -63,7 +63,7 @@ def __init__(self, connection_str: str = "", autocommit: bool = False, attrs_bef self._initializer() self.setautocommit(autocommit) - def _construct_connection_string(self, connection_str: str, **kwargs) -> str: + def _construct_connection_string(self, connection_str: str = "", **kwargs) -> str: """ Construct the connection string by concatenating the connection string with key/value pairs from kwargs. diff --git a/tests/test_003_connection.py b/tests/test_003_connection.py index c192da68..bd37a468 100644 --- a/tests/test_003_connection.py +++ b/tests/test_003_connection.py @@ -55,7 +55,7 @@ def test_connection_string_with_attrs_before(db_connection): assert "TrustServerCertificate=yes;" in conn_str, "Connection string should contain 'TrustServerCertificate=yes;'" assert "APP=MSSQL-Python" in conn_str, "Connection string should contain 'APP=MSSQL-Python'" assert "Driver={ODBC Driver 18 for SQL Server}" in conn_str, "Connection string should contain 'Driver={ODBC Driver 18 for SQL Server}'" - assert "{1256: token}" in conn_str, "Connection string should contain '{1256: token}'" + assert "{1256: token}" not in conn_str, "Connection string should not contain '{1256: token}'" def test_connection_string_with_odbc_param(db_connection): # Check if the connection string is constructed correctly with ODBC parameters