Skip to content

Commit ce4f8c5

Browse files
fix: output converter lookup by SQL type code, not Python type
_build_converter_map looked up converters using desc[1] (the mapped Python type, e.g. bytes) but add_output_converter registers by SQL type code (e.g. -151). Added _column_sql_types to Cursor so the converter map tries the raw SQL code first, then falls back to Python type, then WVARCHAR. Also fixes the output converter test to register by SQL_SS_UDT (-151) instead of bytes, and adds docstrings to _get_c_type_for_sql_type and _map_data_type documenting the SQL Server-specific types.
1 parent fde7ea4 commit ce4f8c5

2 files changed

Lines changed: 22 additions & 6 deletions

File tree

mssql_python/cursor.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -943,9 +943,11 @@ def _initialize_description(self, column_metadata: Optional[Any] = None) -> None
943943
"""Initialize the description attribute from column metadata."""
944944
if not column_metadata:
945945
self.description = None
946+
self._column_sql_types = None
946947
return
947948

948949
description = []
950+
sql_types = []
949951
for _, col in enumerate(column_metadata):
950952
# Get column name - lowercase it if the lowercase flag is set
951953
column_name = col["ColumnName"]
@@ -954,6 +956,8 @@ def _initialize_description(self, column_metadata: Optional[Any] = None) -> None
954956
if get_settings().lowercase:
955957
column_name = column_name.lower()
956958

959+
sql_types.append(col["DataType"])
960+
957961
# Add to description tuple (7 elements as per PEP-249)
958962
description.append(
959963
(
@@ -967,6 +971,7 @@ def _initialize_description(self, column_metadata: Optional[Any] = None) -> None
967971
)
968972
)
969973
self.description = description
974+
self._column_sql_types = sql_types
970975

971976
def _build_converter_map(self):
972977
"""
@@ -982,14 +987,24 @@ def _build_converter_map(self):
982987
return None
983988

984989
converter_map = []
990+
raw_types = getattr(self, "_column_sql_types", None)
985991

986-
for desc in self.description:
992+
for i, desc in enumerate(self.description):
987993
if desc is None:
988994
converter_map.append(None)
989995
continue
990-
sql_type = desc[1]
991-
converter = self.connection.get_output_converter(sql_type)
992-
# If no converter found for the SQL type, try the WVARCHAR converter as a fallback
996+
997+
converter = None
998+
999+
# First try lookup by raw SQL type code (e.g. -151 for SQL_SS_UDT)
1000+
if raw_types and i < len(raw_types):
1001+
converter = self.connection.get_output_converter(raw_types[i])
1002+
1003+
# Fall back to lookup by Python type from description
1004+
if converter is None:
1005+
converter = self.connection.get_output_converter(desc[1])
1006+
1007+
# Last resort: try the WVARCHAR converter as a catch-all fallback
9931008
if converter is None:
9941009
from mssql_python.constants import ConstantsDDBC
9951010

tests/test_017_spatial_types.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,14 +304,15 @@ def geography_converter(value):
304304
return None
305305
return b"CONVERTED:" + value
306306

307-
db_connection.add_output_converter(bytes, geography_converter)
307+
SQL_SS_UDT = -151
308+
db_connection.add_output_converter(SQL_SS_UDT, geography_converter)
308309

309310
try:
310311
row = cursor.execute("SELECT geo_col FROM #geo_converter;").fetchone()
311312
assert isinstance(row[0], bytes)
312313
assert row[0].startswith(b"CONVERTED:")
313314
finally:
314-
db_connection.remove_output_converter(bytes)
315+
db_connection.remove_output_converter(SQL_SS_UDT)
315316

316317

317318
def test_geography_description_metadata(cursor, db_connection):

0 commit comments

Comments
 (0)