Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion mssql_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""
import threading
import locale
import sys

# Exceptions
# https://www.python.org/dev/peps/pep-0249/#exceptions
Expand All @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand Down
19 changes: 18 additions & 1 deletion mssql_python/row.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -30,7 +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:
self._values = 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:
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:
Expand Down
85 changes: 74 additions & 11 deletions tests/test_004_cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import mssql_python
import uuid


# Setup test table
TEST_TABLE = """
CREATE TABLE #pytest_all_data_types (
Expand Down Expand Up @@ -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} (
Expand All @@ -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
Expand All @@ -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} (
Expand All @@ -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}")
Expand All @@ -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()

Expand Down Expand Up @@ -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()
Expand All @@ -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()

Expand Down Expand Up @@ -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")
Loading