Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
By default, the mssql-python driver converts Microsoft SQL data types to appropriate Python types (see Data type mappings). The output converter system lets you override this behavior for specific SQL types, enabling:
- Custom type transformations (for example, money to float instead of Decimal)
- Integration with third-party libraries (for example, spatial data to Shapely objects)
- Business-specific formatting (for example, dates to custom string formats)
Register output converters
Use add_output_converter() to register a converter function. The key is a Python type that matches the type_code in cursor.description:
import mssql_python
from datetime import datetime
def my_datetime_converter(value):
"""Convert datetime to a custom display string."""
if value is None:
return None
return value.strftime("%B %d, %Y at %I:%M %p")
conn = mssql_python.connect(connection_string)
conn.add_output_converter(datetime, my_datetime_converter)
cursor = conn.cursor()
cursor.execute("SELECT CAST('2026-07-16T10:30:00' AS DATETIME2)")
print(cursor.fetchval()) # July 16, 2026 at 10:30 AM
Converter function signature
The driver looks up converters by matching the Python type from cursor.description for each column. The type you pass to add_output_converter() must match one of these Python types:
cursor.description type_code |
Converter receives | Microsoft SQL types |
|---|---|---|
int |
int |
tinyint, smallint, int, bigint |
float |
float |
real, float |
decimal.Decimal |
Decimal |
decimal, numeric, money, smallmoney |
str |
bytes (UTF-16LE encoded) |
char, varchar, nchar, nvarchar, xml |
datetime.datetime |
datetime |
datetime, datetime2, smalldatetime |
datetime.date |
date |
date |
bytes |
bytes |
varbinary, binary, geography, geometry |
bool |
bool |
bit |
Note
String-type converters (str key) receive raw bytes in UTF-16LE encoding, not decoded Python strings. All other types receive the already converted Python object.
from decimal import Decimal
def decimal_converter(value: Decimal | None) -> float | None:
if value is None:
return None
# value is a Decimal object for numeric/money types
return float(value)
Manage converters
Use these methods to inspect or remove converters already registered on a connection.
Get existing converter
Retrieve the converter function currently registered for a type:
converter = conn.get_output_converter(str)
if converter:
print(f"Converter registered: {converter}")
else:
print("Using default conversion")
Remove a converter
Unregister a converter so the driver reverts to default conversion for that type:
conn.remove_output_converter(str)
# str columns now use default conversion
Clear all converters
Reset all registered converters to use Microsoft SQL's default type mappings:
conn.clear_output_converters()
# All types now use default conversion
Common converter patterns
These examples show the most frequently needed converter functions.
Convert VARCHAR to uppercase
String-type converters receive raw bytes in UTF-16LE encoding:
def uppercase_converter(value):
if value is None:
return None
return value.decode('utf-16-le').upper()
conn.add_output_converter(str, uppercase_converter)
cursor = conn.cursor()
cursor.execute("SELECT 'hello world'")
print(cursor.fetchval()) # HELLO WORLD
Convert money to float
By default, the driver returns DECIMAL/NUMERIC types as decimal.Decimal. Convert to float:
from decimal import Decimal
def money_to_float(value):
if value is None:
return None
return float(value)
conn.add_output_converter(Decimal, money_to_float)
cursor = conn.cursor()
cursor.execute("SELECT CAST(19.99 AS MONEY)")
result = cursor.fetchval()
print(type(result)) # <class 'float'>
Parse JSON data
Microsoft SQL Server can store JSON as text. Automatically parse to Python objects:
import json
def json_converter(value):
if value is None:
return None
text = value.decode('utf-16-le')
try:
return json.loads(text)
except json.JSONDecodeError:
return text # Return as string if not valid JSON
conn.add_output_converter(str, json_converter)
cursor = conn.cursor()
cursor.execute("SELECT '{\"name\": \"Widget\", \"price\": 19.99}'")
data = cursor.fetchval()
print(data['name']) # Widget
Caution
This example converts ALL string values (nvarchar, varchar, xml). JSON parsing is attempted on every string column. In practice, apply JSON parsing selectively instead of as a blanket converter.
Custom datetime formatting
Convert datetime to ISO string format:
from datetime import datetime
def datetime_to_iso(value):
if value is None:
return None
return value.isoformat()
conn.add_output_converter(datetime, datetime_to_iso)
cursor = conn.cursor()
cursor.execute("SELECT TOP 1 ModifiedDate FROM Production.Product")
print(cursor.fetchval()) # 2014-02-08T10:01:36.827000
Security considerations
Caution
When you register an output converter, the function you provide runs on every matching database value.
- Only register converters from trusted code.
- Never accept converter functions from user input.
- Malicious converters could execute arbitrary code or leak data.
# DANGEROUS - never do this
def unsafe_example(user_converter_code):
converter_func = eval(user_converter_code) # Security risk!
conn.add_output_converter(str, converter_func)
Example: Spatial data integration
Integrate spatial data with the Shapely library:
from shapely import wkb
def geometry_converter(value):
"""Convert WKB binary to Shapely geometry object."""
if value is None:
return None
return wkb.loads(value)
conn.add_output_converter(bytes, geometry_converter)
# Query must use STAsBinary() to get standard WKB format
cursor = conn.cursor()
cursor.execute(
"SELECT SpatialLocation.STAsBinary() FROM Person.Address "
"WHERE SpatialLocation IS NOT NULL AND City = 'Seattle'"
)
for row in cursor:
shape = row[0]
print(f"Point: ({shape.x:.4f}, {shape.y:.4f})")
Note
Registering a bytes converter affects ALL binary columns (varbinary, geography, geometry). Use clear_output_converters() after spatial queries if you also query nonspatial binary data.
Example: Custom Row formatting
Create a data class converter:
from dataclasses import dataclass
@dataclass
class Product:
id: int
name: str
price: float
def fetch_products_as_dataclass(cursor):
"""Convert rows to Product dataclass instances."""
cursor.execute(
"SELECT ProductID, Name, ListPrice FROM Production.Product "
"WHERE ListPrice > 0"
)
results = []
for row in cursor:
results.append(Product(
id=row.ProductID,
name=row.Name,
price=float(row.ListPrice)
))
return results
products = fetch_products_as_dataclass(cursor)
for p in products[:5]:
print(f"{p.name}: ${p.price:.2f}")
Converter scope and lifetime
- Connection-scoped: The driver registers converters per connection, not globally.
- Persistent: Converters remain active until you remove them or close the connection.
- Not inherited: New connections don't inherit converters from other connections.
conn1 = mssql_python.connect(connection_string)
conn1.add_output_converter(str, uppercase_converter)
conn2 = mssql_python.connect(connection_string)
# conn2 does NOT have the converter registered
Performance considerations
- The driver calls converters for every value of the registered type.
- Keep converter functions efficient.
- For high-volume queries, consider whether conversion is necessary.
- Profile performance if converters affect throughput.
import time
def slow_converter(value):
time.sleep(1) # Avoid time consuming routines like this - adds 1 second for each value processed!
return str(value) if value else None