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
8 changes: 8 additions & 0 deletions mssql_python/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ class ConstantsDDBC(Enum):
SQL_FETCH_RELATIVE = 6
SQL_FETCH_BOOKMARK = 8
SQL_DATETIMEOFFSET = -155
SQL_SS_TIME2 = -154
SQL_SS_XML = -152
SQL_C_SS_TIMESTAMPOFFSET = 0x4001
SQL_SCOPE_CURROW = 0
SQL_BEST_ROWID = 1
Expand Down Expand Up @@ -362,6 +364,12 @@ def get_valid_types(cls) -> set:
ConstantsDDBC.SQL_DATE.value,
ConstantsDDBC.SQL_TIME.value,
ConstantsDDBC.SQL_TIMESTAMP.value,
ConstantsDDBC.SQL_TYPE_DATE.value,
ConstantsDDBC.SQL_TYPE_TIME.value,
ConstantsDDBC.SQL_TYPE_TIMESTAMP.value,
ConstantsDDBC.SQL_SS_TIME2.value,
ConstantsDDBC.SQL_DATETIMEOFFSET.value,
ConstantsDDBC.SQL_SS_XML.value,
ConstantsDDBC.SQL_GUID.value,
}

Expand Down
71 changes: 59 additions & 12 deletions mssql_python/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ def _reset_inputsizes(self) -> None:
self._inputsizes = None

def _get_c_type_for_sql_type(self, sql_type: int) -> int:
"""Map SQL type to appropriate C type for parameter binding"""
"""Map SQL type to appropriate C type for parameter binding."""
sql_to_c_type = {
ddbc_sql_const.SQL_CHAR.value: ddbc_sql_const.SQL_C_CHAR.value,
ddbc_sql_const.SQL_VARCHAR.value: ddbc_sql_const.SQL_C_CHAR.value,
Expand All @@ -865,9 +865,19 @@ def _get_c_type_for_sql_type(self, sql_type: int) -> int:
ddbc_sql_const.SQL_BINARY.value: ddbc_sql_const.SQL_C_BINARY.value,
ddbc_sql_const.SQL_VARBINARY.value: ddbc_sql_const.SQL_C_BINARY.value,
ddbc_sql_const.SQL_LONGVARBINARY.value: ddbc_sql_const.SQL_C_BINARY.value,
# ODBC 3.x date/time types (reported by ODBC 18 driver)
ddbc_sql_const.SQL_TYPE_DATE.value: ddbc_sql_const.SQL_C_TYPE_DATE.value,
ddbc_sql_const.SQL_TYPE_TIME.value: ddbc_sql_const.SQL_C_TYPE_TIME.value,
ddbc_sql_const.SQL_TYPE_TIMESTAMP.value: ddbc_sql_const.SQL_C_TYPE_TIMESTAMP.value,
ddbc_sql_const.SQL_SS_TIME2.value: ddbc_sql_const.SQL_C_TYPE_TIME.value,
ddbc_sql_const.SQL_DATETIMEOFFSET.value: ddbc_sql_const.SQL_C_SS_TIMESTAMPOFFSET.value,
# ODBC 2.x aliases (accepted by setinputsizes via SQLTypes)
ddbc_sql_const.SQL_DATE.value: ddbc_sql_const.SQL_C_TYPE_DATE.value,
ddbc_sql_const.SQL_TIME.value: ddbc_sql_const.SQL_C_TYPE_TIME.value,
ddbc_sql_const.SQL_TIMESTAMP.value: ddbc_sql_const.SQL_C_TYPE_TIMESTAMP.value,
# Other types
ddbc_sql_const.SQL_GUID.value: ddbc_sql_const.SQL_C_GUID.value,
ddbc_sql_const.SQL_SS_XML.value: ddbc_sql_const.SQL_C_WCHAR.value,
}
return sql_to_c_type.get(sql_type, ddbc_sql_const.SQL_C_DEFAULT.value)

Expand Down Expand Up @@ -1026,34 +1036,71 @@ def _map_data_type(self, sql_type):
"""
Map SQL data type to Python data type.

Maps the ODBC SQL type code returned by SQLDescribeCol to the
corresponding Python type for cursor.description[i][1].

The ODBC 18 driver for SQL Server reports these type codes:
Standard ODBC 3.x types:
SQL_CHAR(1), SQL_VARCHAR(12), SQL_LONGVARCHAR(-1),
SQL_WCHAR(-8), SQL_WVARCHAR(-9), SQL_WLONGVARCHAR(-10),
SQL_INTEGER(4), SQL_SMALLINT(5), SQL_TINYINT(-6), SQL_BIGINT(-5),
SQL_BIT(-7), SQL_FLOAT(6), SQL_REAL(7), SQL_DOUBLE(8),
SQL_DECIMAL(3), SQL_NUMERIC(2),
SQL_BINARY(-2), SQL_VARBINARY(-3), SQL_LONGVARBINARY(-4),
SQL_TYPE_DATE(91), SQL_TYPE_TIME(92), SQL_TYPE_TIMESTAMP(93), SQL_GUID(-11)
SQL Server-specific types (from msodbcsql.h):
SQL_SS_TIME2(-154) for time columns
SQL_DATETIMEOFFSET(-155) for datetimeoffset columns
SQL_SS_XML(-152) for xml columns

ODBC 2.x aliases (9, 10, 11) are also accepted defensively.

Args:
sql_type: SQL data type.
sql_type: SQL data type code from SQLDescribeCol.

Returns:
Corresponding Python data type.
"""
sql_to_python_type = {
ddbc_sql_const.SQL_INTEGER.value: int,
ddbc_sql_const.SQL_VARCHAR.value: str,
ddbc_sql_const.SQL_WVARCHAR.value: str,
# String types
ddbc_sql_const.SQL_CHAR.value: str,
ddbc_sql_const.SQL_VARCHAR.value: str,
ddbc_sql_const.SQL_LONGVARCHAR.value: str,
ddbc_sql_const.SQL_WCHAR.value: str,
ddbc_sql_const.SQL_WVARCHAR.value: str,
ddbc_sql_const.SQL_WLONGVARCHAR.value: str,
# Integer types
ddbc_sql_const.SQL_INTEGER.value: int,
ddbc_sql_const.SQL_SMALLINT.value: int,
ddbc_sql_const.SQL_TINYINT.value: int,
ddbc_sql_const.SQL_BIGINT.value: int,
# Floating-point types
ddbc_sql_const.SQL_FLOAT.value: float,
ddbc_sql_const.SQL_DOUBLE.value: float,
ddbc_sql_const.SQL_REAL.value: float,
# Exact numeric types
ddbc_sql_const.SQL_DECIMAL.value: decimal.Decimal,
ddbc_sql_const.SQL_NUMERIC.value: decimal.Decimal,
ddbc_sql_const.SQL_DATE.value: datetime.date,
ddbc_sql_const.SQL_TIMESTAMP.value: datetime.datetime,
ddbc_sql_const.SQL_TIME.value: datetime.time,
# Date/time types — values the ODBC 18 driver actually reports
ddbc_sql_const.SQL_TYPE_DATE.value: datetime.date, # 91 — date
ddbc_sql_const.SQL_TYPE_TIME.value: datetime.time, # 92 — time (ODBC 3.x)
ddbc_sql_const.SQL_TYPE_TIMESTAMP.value: datetime.datetime, # 93 — datetime/datetime2/smalldatetime
ddbc_sql_const.SQL_SS_TIME2.value: datetime.time, # -154 — time
ddbc_sql_const.SQL_DATETIMEOFFSET.value: datetime.datetime, # -155 — datetimeoffset
# ODBC 2.x date/time aliases (defensive, in case any driver reports these)
ddbc_sql_const.SQL_DATE.value: datetime.date, # 9
ddbc_sql_const.SQL_TIME.value: datetime.time, # 10
ddbc_sql_const.SQL_TIMESTAMP.value: datetime.datetime, # 11
# Boolean
ddbc_sql_const.SQL_BIT.value: bool,
ddbc_sql_const.SQL_TINYINT.value: int,
ddbc_sql_const.SQL_SMALLINT.value: int,
ddbc_sql_const.SQL_BIGINT.value: int,
# Binary types
ddbc_sql_const.SQL_BINARY.value: bytes,
ddbc_sql_const.SQL_VARBINARY.value: bytes,
ddbc_sql_const.SQL_LONGVARBINARY.value: bytes,
# UUID
ddbc_sql_const.SQL_GUID.value: uuid.UUID,
# Add more mappings as needed
# XML — driver reports SQL_SS_XML (-152), fetched as str
ddbc_sql_const.SQL_SS_XML.value: str,
}
return sql_to_python_type.get(sql_type, str)

Expand Down
Loading
Loading