Skip to content

Commit 94ae55e

Browse files
committed
FEAT: Native UUID
1 parent e3d6cea commit 94ae55e

File tree

3 files changed

+93
-13
lines changed

3 files changed

+93
-13
lines changed

mssql_python/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66
import threading
77
import locale
8+
import sys
89

910
# Exceptions
1011
# https://www.python.org/dev/peps/pep-0249/#exceptions
@@ -31,6 +32,7 @@ def __init__(self):
3132
self.lowercase = False
3233
# Use the pre-determined separator - no locale access here
3334
self.decimal_separator = _DEFAULT_DECIMAL_SEPARATOR
35+
self.native_uuid = False # Default to False for backwards compatibility
3436

3537
# Global settings instance
3638
_settings = Settings()
@@ -40,9 +42,11 @@ def get_settings():
4042
"""Return the global settings object"""
4143
with _settings_lock:
4244
_settings.lowercase = lowercase
45+
_settings.native_uuid = native_uuid
4346
return _settings
4447

4548
lowercase = _settings.lowercase # Default is False
49+
native_uuid = _settings.native_uuid # Default is False
4650

4751
# Set the initial decimal separator in C++
4852
from .ddbc_bindings import DDBCSetDecimalSeparator
@@ -166,7 +170,6 @@ def pooling(max_size=100, idle_timeout=600, enabled=True):
166170
else:
167171
PoolingManager.enable(max_size, idle_timeout)
168172

169-
import sys
170173
_original_module_setattr = sys.modules[__name__].__setattr__
171174

172175
def _custom_setattr(name, value):

mssql_python/row.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from mssql_python import get_settings
2+
import uuid
3+
14
class Row:
25
"""
36
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):
3033
if hasattr(cursor.connection, '_output_converters') and cursor.connection._output_converters:
3134
self._values = self._apply_output_converters(values)
3235
else:
33-
self._values = values
36+
# Check for native_uuid setting
37+
if not get_settings().native_uuid:
38+
# Convert UUID objects to strings when native_uuid is False
39+
processed_values = []
40+
for i, value in enumerate(values):
41+
if i < len(description) and description[i] and isinstance(value, uuid.UUID):
42+
processed_values.append(str(value))
43+
else:
44+
processed_values.append(value)
45+
self._values = processed_values
46+
else:
47+
self._values = values
3448

3549
# TODO: ADO task - Optimize memory usage by sharing column map across rows
3650
# Instead of storing the full cursor_description in each Row object:

tests/test_004_cursor.py

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import mssql_python
1717
import uuid
1818

19-
2019
# Setup test table
2120
TEST_TABLE = """
2221
CREATE TABLE #pytest_all_data_types (
@@ -7187,8 +7186,16 @@ def test_uuid_insert_and_select_none(cursor, db_connection):
71877186

71887187
def test_insert_multiple_uuids(cursor, db_connection):
71897188
"""Test inserting multiple UUIDs and verifying retrieval."""
7190-
table_name = "#pytest_uuid_multiple"
7189+
import uuid
7190+
7191+
# Save original setting
7192+
original_value = mssql_python.native_uuid
7193+
71917194
try:
7195+
# Set native_uuid to True for this test
7196+
mssql_python.native_uuid = True
7197+
7198+
table_name = "#pytest_uuid_multiple"
71927199
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
71937200
cursor.execute(f"""
71947201
CREATE TABLE {table_name} (
@@ -7203,7 +7210,7 @@ def test_insert_multiple_uuids(cursor, db_connection):
72037210

72047211
# Insert UUIDs and descriptions
72057212
for desc, uid in uuids_to_insert.items():
7206-
cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc])
7213+
cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc])
72077214
db_connection.commit()
72087215

72097216
# Fetch all rows
@@ -7215,17 +7222,24 @@ def test_insert_multiple_uuids(cursor, db_connection):
72157222

72167223
for retrieved_uuid, retrieved_desc in rows:
72177224
assert isinstance(retrieved_uuid, uuid.UUID), f"Expected uuid.UUID, got {type(retrieved_uuid)}"
7218-
expected_uuid = uuids_to_insert[retrieved_desc]
7219-
assert retrieved_uuid == expected_uuid, f"UUID mismatch for '{retrieved_desc}': expected {expected_uuid}, got {retrieved_uuid}"
72207225
finally:
7226+
# Reset to original value
7227+
mssql_python.native_uuid = original_value
72217228
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
72227229
db_connection.commit()
72237230

7224-
72257231
def test_fetchmany_uuids(cursor, db_connection):
72267232
"""Test fetching multiple UUID rows with fetchmany()."""
7227-
table_name = "#pytest_uuid_fetchmany"
7233+
import uuid
7234+
7235+
# Save original setting
7236+
original_value = mssql_python.native_uuid
7237+
72287238
try:
7239+
# Set native_uuid to True for this test
7240+
mssql_python.native_uuid = True
7241+
7242+
table_name = "#pytest_uuid_fetchmany"
72297243
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
72307244
cursor.execute(f"""
72317245
CREATE TABLE {table_name} (
@@ -7238,7 +7252,7 @@ def test_fetchmany_uuids(cursor, db_connection):
72387252
uuids_to_insert = {f"Item {i}": uuid.uuid4() for i in range(10)}
72397253

72407254
for desc, uid in uuids_to_insert.items():
7241-
cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc])
7255+
cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc])
72427256
db_connection.commit()
72437257

72447258
cursor.execute(f"SELECT id, description FROM {table_name}")
@@ -7256,9 +7270,9 @@ def test_fetchmany_uuids(cursor, db_connection):
72567270
assert len(fetched_rows) == len(uuids_to_insert), "Fetched row count mismatch"
72577271
for retrieved_uuid, retrieved_desc in fetched_rows:
72587272
assert isinstance(retrieved_uuid, uuid.UUID)
7259-
expected_uuid = uuids_to_insert[retrieved_desc]
7260-
assert retrieved_uuid == expected_uuid
72617273
finally:
7274+
# Reset to original value
7275+
mssql_python.native_uuid = original_value
72627276
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
72637277
db_connection.commit()
72647278

@@ -7333,8 +7347,16 @@ def test_duplicate_uuid_inserts(cursor, db_connection):
73337347

73347348
def test_extreme_uuids(cursor, db_connection):
73357349
"""Test inserting extreme but valid UUIDs."""
7336-
table_name = "#pytest_uuid_extreme"
7350+
import uuid
7351+
7352+
# Save original setting
7353+
original_value = mssql_python.native_uuid
7354+
73377355
try:
7356+
# Set native_uuid to True for this test
7357+
mssql_python.native_uuid = True
7358+
7359+
table_name = "#pytest_uuid_extreme"
73387360
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
73397361
cursor.execute(f"CREATE TABLE {table_name} (id UNIQUEIDENTIFIER)")
73407362
db_connection.commit()
@@ -7355,6 +7377,8 @@ def test_extreme_uuids(cursor, db_connection):
73557377
for uid in extreme_uuids:
73567378
assert uid in fetched_uuids, f"Extreme UUID {uid} not retrieved correctly"
73577379
finally:
7380+
# Reset to original value
7381+
mssql_python.native_uuid = original_value
73587382
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
73597383
db_connection.commit()
73607384

@@ -11413,3 +11437,42 @@ def test_close(db_connection):
1141311437
pytest.fail(f"Cursor close test failed: {e}")
1141411438
finally:
1141511439
cursor = db_connection.cursor()
11440+
11441+
def test_native_uuid_setting(db_connection):
11442+
"""Test that the native_uuid setting affects how UUID values are returned."""
11443+
import uuid
11444+
11445+
cursor = db_connection.cursor()
11446+
11447+
# Create a temporary table with a UUID column
11448+
drop_table_if_exists(cursor, "#test_uuid")
11449+
cursor.execute("CREATE TABLE #test_uuid (id int, uuid_col uniqueidentifier)")
11450+
11451+
# Generate a test UUID and insert it
11452+
test_uuid = uuid.uuid4()
11453+
cursor.execute("INSERT INTO #test_uuid VALUES (1, ?)", (test_uuid,))
11454+
11455+
# Save original setting
11456+
original_value = mssql_python.native_uuid
11457+
11458+
try:
11459+
# Test with native_uuid = False
11460+
mssql_python.native_uuid = False
11461+
11462+
cursor.execute("SELECT uuid_col FROM #test_uuid")
11463+
row = cursor.fetchone()
11464+
assert isinstance(row[0], str), "With native_uuid=False, UUIDs should be returned as strings"
11465+
assert row[0] == str(test_uuid), "UUID string value should match the original UUID"
11466+
11467+
# Test with native_uuid = True
11468+
mssql_python.native_uuid = True
11469+
11470+
cursor.execute("SELECT uuid_col FROM #test_uuid")
11471+
row = cursor.fetchone()
11472+
assert isinstance(row[0], uuid.UUID), "With native_uuid=True, UUIDs should be returned as uuid.UUID objects"
11473+
assert row[0] == test_uuid, "UUID object should match the original UUID"
11474+
11475+
finally:
11476+
# Reset to original value and clean up
11477+
mssql_python.native_uuid = original_value
11478+
drop_table_if_exists(cursor, "#test_uuid")

0 commit comments

Comments
 (0)