From c8fbd4543b66ded752ce0aabe51d5152f48cbb82 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Fri, 3 Oct 2025 12:14:06 +0530 Subject: [PATCH 1/3] fix string parsing for uuid --- mssql_python/cursor.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 764e101f..2a6684e4 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -363,19 +363,6 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): False, ) - try: - val = uuid.UUID(param) - parameters_list[i] = val.bytes_le - return ( - ddbc_sql_const.SQL_GUID.value, - ddbc_sql_const.SQL_C_GUID.value, - 16, - 0, - False - ) - except ValueError: - pass - # String mapping logic here is_unicode = self._is_unicode_string(param) From 94ae55ef09d529393ddb458cb80374c02ccf1867 Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Tue, 7 Oct 2025 11:49:34 +0530 Subject: [PATCH 2/3] FEAT: Native UUID --- mssql_python/__init__.py | 5 ++- mssql_python/row.py | 16 +++++++- tests/test_004_cursor.py | 85 ++++++++++++++++++++++++++++++++++------ 3 files changed, 93 insertions(+), 13 deletions(-) diff --git a/mssql_python/__init__.py b/mssql_python/__init__.py index 875150c5..384ecb15 100644 --- a/mssql_python/__init__.py +++ b/mssql_python/__init__.py @@ -5,6 +5,7 @@ """ import threading import locale +import sys # Exceptions # https://www.python.org/dev/peps/pep-0249/#exceptions @@ -31,6 +32,7 @@ def __init__(self): self.lowercase = False # Use the pre-determined separator - no locale access here self.decimal_separator = _DEFAULT_DECIMAL_SEPARATOR + self.native_uuid = False # Default to False for backwards compatibility # Global settings instance _settings = Settings() @@ -40,9 +42,11 @@ def get_settings(): """Return the global settings object""" with _settings_lock: _settings.lowercase = lowercase + _settings.native_uuid = native_uuid return _settings lowercase = _settings.lowercase # Default is False +native_uuid = _settings.native_uuid # Default is False # Set the initial decimal separator in C++ from .ddbc_bindings import DDBCSetDecimalSeparator @@ -166,7 +170,6 @@ def pooling(max_size=100, idle_timeout=600, enabled=True): else: PoolingManager.enable(max_size, idle_timeout) -import sys _original_module_setattr = sys.modules[__name__].__setattr__ def _custom_setattr(name, value): diff --git a/mssql_python/row.py b/mssql_python/row.py index 5e749a67..3cc88b8d 100644 --- a/mssql_python/row.py +++ b/mssql_python/row.py @@ -1,3 +1,6 @@ +from mssql_python import get_settings +import uuid + class Row: """ A row of data from a cursor fetch operation. Provides both tuple-like indexing @@ -30,7 +33,18 @@ def __init__(self, cursor, description, values, column_map=None): if hasattr(cursor.connection, '_output_converters') and cursor.connection._output_converters: self._values = self._apply_output_converters(values) else: - self._values = values + # Check for native_uuid setting + if not get_settings().native_uuid: + # Convert UUID objects to strings when native_uuid is False + processed_values = [] + for i, value in enumerate(values): + if i < len(description) and description[i] and isinstance(value, uuid.UUID): + processed_values.append(str(value)) + else: + processed_values.append(value) + self._values = processed_values + else: + self._values = values # TODO: ADO task - Optimize memory usage by sharing column map across rows # Instead of storing the full cursor_description in each Row object: diff --git a/tests/test_004_cursor.py b/tests/test_004_cursor.py index 97600c17..34e0b859 100644 --- a/tests/test_004_cursor.py +++ b/tests/test_004_cursor.py @@ -16,7 +16,6 @@ import mssql_python import uuid - # Setup test table TEST_TABLE = """ CREATE TABLE #pytest_all_data_types ( @@ -7187,8 +7186,16 @@ def test_uuid_insert_and_select_none(cursor, db_connection): def test_insert_multiple_uuids(cursor, db_connection): """Test inserting multiple UUIDs and verifying retrieval.""" - table_name = "#pytest_uuid_multiple" + import uuid + + # Save original setting + original_value = mssql_python.native_uuid + try: + # Set native_uuid to True for this test + mssql_python.native_uuid = True + + table_name = "#pytest_uuid_multiple" cursor.execute(f"DROP TABLE IF EXISTS {table_name}") cursor.execute(f""" CREATE TABLE {table_name} ( @@ -7203,7 +7210,7 @@ def test_insert_multiple_uuids(cursor, db_connection): # Insert UUIDs and descriptions for desc, uid in uuids_to_insert.items(): - cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc]) + cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc]) db_connection.commit() # Fetch all rows @@ -7215,17 +7222,24 @@ def test_insert_multiple_uuids(cursor, db_connection): for retrieved_uuid, retrieved_desc in rows: assert isinstance(retrieved_uuid, uuid.UUID), f"Expected uuid.UUID, got {type(retrieved_uuid)}" - expected_uuid = uuids_to_insert[retrieved_desc] - assert retrieved_uuid == expected_uuid, f"UUID mismatch for '{retrieved_desc}': expected {expected_uuid}, got {retrieved_uuid}" finally: + # Reset to original value + mssql_python.native_uuid = original_value cursor.execute(f"DROP TABLE IF EXISTS {table_name}") db_connection.commit() - def test_fetchmany_uuids(cursor, db_connection): """Test fetching multiple UUID rows with fetchmany().""" - table_name = "#pytest_uuid_fetchmany" + import uuid + + # Save original setting + original_value = mssql_python.native_uuid + try: + # Set native_uuid to True for this test + mssql_python.native_uuid = True + + table_name = "#pytest_uuid_fetchmany" cursor.execute(f"DROP TABLE IF EXISTS {table_name}") cursor.execute(f""" CREATE TABLE {table_name} ( @@ -7238,7 +7252,7 @@ def test_fetchmany_uuids(cursor, db_connection): uuids_to_insert = {f"Item {i}": uuid.uuid4() for i in range(10)} for desc, uid in uuids_to_insert.items(): - cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc]) + cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc]) db_connection.commit() cursor.execute(f"SELECT id, description FROM {table_name}") @@ -7256,9 +7270,9 @@ def test_fetchmany_uuids(cursor, db_connection): assert len(fetched_rows) == len(uuids_to_insert), "Fetched row count mismatch" for retrieved_uuid, retrieved_desc in fetched_rows: assert isinstance(retrieved_uuid, uuid.UUID) - expected_uuid = uuids_to_insert[retrieved_desc] - assert retrieved_uuid == expected_uuid finally: + # Reset to original value + mssql_python.native_uuid = original_value cursor.execute(f"DROP TABLE IF EXISTS {table_name}") db_connection.commit() @@ -7333,8 +7347,16 @@ def test_duplicate_uuid_inserts(cursor, db_connection): def test_extreme_uuids(cursor, db_connection): """Test inserting extreme but valid UUIDs.""" - table_name = "#pytest_uuid_extreme" + import uuid + + # Save original setting + original_value = mssql_python.native_uuid + try: + # Set native_uuid to True for this test + mssql_python.native_uuid = True + + table_name = "#pytest_uuid_extreme" cursor.execute(f"DROP TABLE IF EXISTS {table_name}") cursor.execute(f"CREATE TABLE {table_name} (id UNIQUEIDENTIFIER)") db_connection.commit() @@ -7355,6 +7377,8 @@ def test_extreme_uuids(cursor, db_connection): for uid in extreme_uuids: assert uid in fetched_uuids, f"Extreme UUID {uid} not retrieved correctly" finally: + # Reset to original value + mssql_python.native_uuid = original_value cursor.execute(f"DROP TABLE IF EXISTS {table_name}") db_connection.commit() @@ -11413,3 +11437,42 @@ def test_close(db_connection): pytest.fail(f"Cursor close test failed: {e}") finally: cursor = db_connection.cursor() + +def test_native_uuid_setting(db_connection): + """Test that the native_uuid setting affects how UUID values are returned.""" + import uuid + + cursor = db_connection.cursor() + + # Create a temporary table with a UUID column + drop_table_if_exists(cursor, "#test_uuid") + cursor.execute("CREATE TABLE #test_uuid (id int, uuid_col uniqueidentifier)") + + # Generate a test UUID and insert it + test_uuid = uuid.uuid4() + cursor.execute("INSERT INTO #test_uuid VALUES (1, ?)", (test_uuid,)) + + # Save original setting + original_value = mssql_python.native_uuid + + try: + # Test with native_uuid = False + mssql_python.native_uuid = False + + cursor.execute("SELECT uuid_col FROM #test_uuid") + row = cursor.fetchone() + assert isinstance(row[0], str), "With native_uuid=False, UUIDs should be returned as strings" + assert row[0] == str(test_uuid), "UUID string value should match the original UUID" + + # Test with native_uuid = True + mssql_python.native_uuid = True + + cursor.execute("SELECT uuid_col FROM #test_uuid") + row = cursor.fetchone() + assert isinstance(row[0], uuid.UUID), "With native_uuid=True, UUIDs should be returned as uuid.UUID objects" + assert row[0] == test_uuid, "UUID object should match the original UUID" + + finally: + # Reset to original value and clean up + mssql_python.native_uuid = original_value + drop_table_if_exists(cursor, "#test_uuid") \ No newline at end of file From 2d916a44d78d47f80ab7011962543b0472fbd8b1 Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Tue, 7 Oct 2025 12:06:18 +0530 Subject: [PATCH 3/3] Resolving comment --- mssql_python/row.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/mssql_python/row.py b/mssql_python/row.py index 3cc88b8d..84f62316 100644 --- a/mssql_python/row.py +++ b/mssql_python/row.py @@ -33,18 +33,21 @@ def __init__(self, cursor, description, values, column_map=None): if hasattr(cursor.connection, '_output_converters') and cursor.connection._output_converters: self._values = self._apply_output_converters(values) else: - # Check for native_uuid setting - if not get_settings().native_uuid: - # Convert UUID objects to strings when native_uuid is False - processed_values = [] - for i, value in enumerate(values): - if i < len(description) and description[i] and isinstance(value, uuid.UUID): - processed_values.append(str(value)) - else: - processed_values.append(value) - self._values = processed_values + self._values = self._process_uuid_values(values, description) + + def _process_uuid_values(self, values, description): + """ + Convert UUID objects to strings if native_uuid setting is False. + """ + if get_settings().native_uuid: + return values + processed_values = [] + for i, value in enumerate(values): + if i < len(description) and description[i] and isinstance(value, uuid.UUID): + processed_values.append(str(value)) else: - self._values = values + processed_values.append(value) + return processed_values # TODO: ADO task - Optimize memory usage by sharing column map across rows # Instead of storing the full cursor_description in each Row object: