Skip to content

Commit 2bcee36

Browse files
authored
Merge branch 'main' into saumya/uuid-executemany
2 parents 44c274e + 0d745b1 commit 2bcee36

File tree

4 files changed

+91
-43
lines changed

4 files changed

+91
-43
lines changed

PyPI_Description.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,12 @@ PyBind11 provides:
3939

4040
We are currently in **Public Preview**.
4141

42-
## What's new in v0.11.0
43-
44-
- **Database Metadata & Introspection:** Added comprehensive `getInfo()` method and extensive catalog APIs (`SQLGetTypeInfo`, `SQLProcedures`, `SQLForeignKeys`, `SQLColumns`) for complete database schema discovery and introspection capabilities.
45-
- **Advanced Parameter Management:** Implemented `setinputsizes()` with SQL type constants and enhanced parameter validation through the `SQLTypes` class for precise type control in parameterized queries.
46-
- **Large Data Streaming Enhancements:** Extended streaming support to VARBINARY(MAX) and VARCHAR(MAX) across all fetch operations (`fetchone`, `fetchmany`, `fetchall`) with improved chunked retrieval for efficient memory usage.
47-
- **Output Data Conversion System:** Introduced flexible output converter framework with `add_output_converter()`, `remove_output_converter()`, and related methods for customizable data transformations during result fetching.
48-
- **Connection-Level Execute:** Added direct `execute()` method to Connection class for streamlined query execution without explicit cursor management.
49-
- **Financial Data Type Support:** Comprehensive support for SQL Server MONEY and SMALLMONEY types with proper boundary value handling and decimal precision.
50-
- **Enhanced Configuration APIs:** Added connection timeout control, decimal separator configuration (`getDecimalSeperator()`, `setDecimalSeperator()`), and improved global variable management.
42+
## What's new in v0.12.0
43+
44+
- **Complex Data Type Support:** Added native support for DATETIMEOFFSET and UNIQUEIDENTIFIER data types with full round-trip handling, enabling seamless integration with Python's timezone-aware `datetime` objects and `uuid.UUID` types.
45+
- **Support for monetary or currency values data types:** Extended MONEY and SMALLMONEY support to `executemany` operations with proper NULL handling and decimal conversion for improved bulk financial data processing.
46+
- **Improved Database Metadata API:** Added `getinfo()` method with enhanced ODBC metadata retrieval, allowing users to query driver/data source information using ODBC info types.
47+
- **Data Processing Optimizations:** Removed aggressive datetime parsing to prevent incorrect type conversions and improve data integrity across diverse datetime formats and string data.
5148

5249
For more information, please visit the project link on Github: https://github.com/microsoft/mssql-python
5350

mssql_python/cursor.py

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None):
362362
0,
363363
False,
364364
)
365-
365+
366366
try:
367367
val = uuid.UUID(param)
368368
parameters_list[i] = val.bytes_le
@@ -376,38 +376,6 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None):
376376
except ValueError:
377377
pass
378378

379-
380-
# Attempt to parse as date, datetime, datetime2, timestamp, smalldatetime or time
381-
if self._parse_date(param):
382-
parameters_list[i] = self._parse_date(
383-
param
384-
) # Replace the parameter with the date object
385-
return (
386-
ddbc_sql_const.SQL_DATE.value,
387-
ddbc_sql_const.SQL_C_TYPE_DATE.value,
388-
10,
389-
0,
390-
False,
391-
)
392-
if self._parse_datetime(param):
393-
parameters_list[i] = self._parse_datetime(param)
394-
return (
395-
ddbc_sql_const.SQL_TIMESTAMP.value,
396-
ddbc_sql_const.SQL_C_TYPE_TIMESTAMP.value,
397-
26,
398-
6,
399-
False,
400-
)
401-
if self._parse_time(param):
402-
parameters_list[i] = self._parse_time(param)
403-
return (
404-
ddbc_sql_const.SQL_TIME.value,
405-
ddbc_sql_const.SQL_C_TYPE_TIME.value,
406-
8,
407-
0,
408-
False,
409-
)
410-
411379
# String mapping logic here
412380
is_unicode = self._is_unicode_string(param)
413381

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def finalize_options(self):
8383

8484
setup(
8585
name='mssql-python',
86-
version='0.11.0',
86+
version='0.12.0',
8787
description='A Python library for interacting with Microsoft SQL Server',
8888
long_description=open('PyPI_Description.md', encoding='utf-8').read(),
8989
long_description_content_type='text/markdown',

tests/test_004_cursor.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10764,6 +10764,89 @@ def test_executemany_with_uuids(cursor, db_connection):
1076410764
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
1076510765
db_connection.commit()
1076610766

10767+
def test_date_string_parameter_binding(cursor, db_connection):
10768+
"""Verify that date-like strings are treated as strings in parameter binding"""
10769+
table_name = "#pytest_date_string"
10770+
try:
10771+
drop_table_if_exists(cursor, table_name)
10772+
cursor.execute(f"""
10773+
CREATE TABLE {table_name} (
10774+
a_column VARCHAR(20)
10775+
)
10776+
""")
10777+
cursor.execute(f"INSERT INTO {table_name} (a_column) VALUES ('string1'), ('string2')")
10778+
db_connection.commit()
10779+
10780+
date_str = "2025-08-12"
10781+
10782+
# Should fail to match anything, since binding may treat it as DATE not VARCHAR
10783+
cursor.execute(f"SELECT a_column FROM {table_name} WHERE RIGHT(a_column, 10) = ?", (date_str,))
10784+
rows = cursor.fetchall()
10785+
10786+
assert rows == [], f"Expected no match for date-like string, got {rows}"
10787+
10788+
except Exception as e:
10789+
pytest.fail(f"Date string parameter binding test failed: {e}")
10790+
finally:
10791+
drop_table_if_exists(cursor, table_name)
10792+
db_connection.commit()
10793+
10794+
def test_time_string_parameter_binding(cursor, db_connection):
10795+
"""Verify that time-like strings are treated as strings in parameter binding"""
10796+
table_name = "#pytest_time_string"
10797+
try:
10798+
drop_table_if_exists(cursor, table_name)
10799+
cursor.execute(f"""
10800+
CREATE TABLE {table_name} (
10801+
time_col VARCHAR(22)
10802+
)
10803+
""")
10804+
cursor.execute(f"INSERT INTO {table_name} (time_col) VALUES ('prefix_14:30:45_suffix')")
10805+
db_connection.commit()
10806+
10807+
time_str = "14:30:45"
10808+
10809+
# This should fail because '14:30:45' gets converted to TIME type
10810+
# and SQL Server can't compare TIME against VARCHAR with prefix/suffix
10811+
cursor.execute(f"SELECT time_col FROM {table_name} WHERE time_col = ?", (time_str,))
10812+
rows = cursor.fetchall()
10813+
10814+
assert rows == [], f"Expected no match for time-like string, got {rows}"
10815+
10816+
except Exception as e:
10817+
pytest.fail(f"Time string parameter binding test failed: {e}")
10818+
finally:
10819+
drop_table_if_exists(cursor, table_name)
10820+
db_connection.commit()
10821+
10822+
def test_datetime_string_parameter_binding(cursor, db_connection):
10823+
"""Verify that datetime-like strings are treated as strings in parameter binding"""
10824+
table_name = "#pytest_datetime_string"
10825+
try:
10826+
drop_table_if_exists(cursor, table_name)
10827+
cursor.execute(f"""
10828+
CREATE TABLE {table_name} (
10829+
datetime_col VARCHAR(33)
10830+
)
10831+
""")
10832+
cursor.execute(f"INSERT INTO {table_name} (datetime_col) VALUES ('prefix_2025-08-12T14:30:45_suffix')")
10833+
db_connection.commit()
10834+
10835+
datetime_str = "2025-08-12T14:30:45"
10836+
10837+
# This should fail because '2025-08-12T14:30:45' gets converted to TIMESTAMP type
10838+
# and SQL Server can't compare TIMESTAMP against VARCHAR with prefix/suffix
10839+
cursor.execute(f"SELECT datetime_col FROM {table_name} WHERE datetime_col = ?", (datetime_str,))
10840+
rows = cursor.fetchall()
10841+
10842+
assert rows == [], f"Expected no match for datetime-like string, got {rows}"
10843+
10844+
except Exception as e:
10845+
pytest.fail(f"Datetime string parameter binding test failed: {e}")
10846+
finally:
10847+
drop_table_if_exists(cursor, table_name)
10848+
db_connection.commit()
10849+
1076710850
def test_close(db_connection):
1076810851
"""Test closing the cursor"""
1076910852
try:

0 commit comments

Comments
 (0)