Skip to content

Commit 83272b0

Browse files
committed
Removed aggressive datetime parsing
1 parent a3dd3b8 commit 83272b0

File tree

2 files changed

+141
-32
lines changed

2 files changed

+141
-32
lines changed

mssql_python/cursor.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -309,38 +309,6 @@ def _map_sql_type(self, param, parameters_list, i):
309309
False,
310310
)
311311

312-
# Attempt to parse as date, datetime, datetime2, timestamp, smalldatetime or time
313-
if self._parse_date(param):
314-
parameters_list[i] = self._parse_date(
315-
param
316-
) # Replace the parameter with the date object
317-
return (
318-
ddbc_sql_const.SQL_DATE.value,
319-
ddbc_sql_const.SQL_C_TYPE_DATE.value,
320-
10,
321-
0,
322-
False,
323-
)
324-
if self._parse_datetime(param):
325-
parameters_list[i] = self._parse_datetime(param)
326-
return (
327-
ddbc_sql_const.SQL_TIMESTAMP.value,
328-
ddbc_sql_const.SQL_C_TYPE_TIMESTAMP.value,
329-
26,
330-
6,
331-
False,
332-
)
333-
if self._parse_time(param):
334-
parameters_list[i] = self._parse_time(param)
335-
return (
336-
ddbc_sql_const.SQL_TIME.value,
337-
ddbc_sql_const.SQL_C_TYPE_TIME.value,
338-
8,
339-
0,
340-
False,
341-
)
342-
343-
# String mapping logic here
344312
is_unicode = self._is_unicode_string(param)
345313

346314
# Computes UTF-16 code units (handles surrogate pairs)

tests/test_004_cursor.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6548,6 +6548,147 @@ def test_only_null_and_empty_binary(cursor, db_connection):
65486548
drop_table_if_exists(cursor, "#pytest_null_empty_binary")
65496549
db_connection.commit()
65506550

6551+
def test_string_vs_datetime_parameter_behavior(cursor, db_connection):
6552+
"""
6553+
Test the difference between using string parameters vs datetime object parameters.
6554+
6555+
This test demonstrates that:
6556+
1. String parameters like '2025-08-12' are treated as literal strings
6557+
2. Datetime objects like date(2025, 8, 12) are properly handled as datetime types
6558+
3. Both approaches work correctly in their intended contexts
6559+
"""
6560+
try:
6561+
# Create test table with both string and date columns
6562+
drop_table_if_exists(cursor, "#pytest_string_vs_datetime")
6563+
cursor.execute("""
6564+
CREATE TABLE #pytest_string_vs_datetime (
6565+
id INT PRIMARY KEY,
6566+
string_col NVARCHAR(50),
6567+
date_col DATE
6568+
)
6569+
""")
6570+
db_connection.commit()
6571+
6572+
# Test 1: Insert using actual datetime objects (should work)
6573+
actual_date = date(2025, 8, 12)
6574+
cursor.execute("INSERT INTO #pytest_string_vs_datetime (id, string_col, date_col) VALUES (?, ?, ?)",
6575+
(1, "test_string", actual_date))
6576+
db_connection.commit()
6577+
6578+
# Test 2: Insert string into string column (should work)
6579+
date_string = '2025-08-12'
6580+
cursor.execute("INSERT INTO #pytest_string_vs_datetime (id, string_col, date_col) VALUES (?, ?, ?)",
6581+
(2, date_string, actual_date))
6582+
db_connection.commit()
6583+
6584+
# Test 3: Query using string parameter (should work as string comparison)
6585+
cursor.execute("SELECT id FROM #pytest_string_vs_datetime WHERE string_col = ?", (date_string,))
6586+
rows = cursor.fetchall()
6587+
assert len(rows) == 1, f"Expected 1 row for string comparison, got {len(rows)}"
6588+
assert rows[0][0] == 2, "Should find row 2 with matching string"
6589+
6590+
# Test 4: Query using datetime object parameter (should work as date comparison)
6591+
cursor.execute("SELECT id FROM #pytest_string_vs_datetime WHERE date_col = ?", (actual_date,))
6592+
rows = cursor.fetchall()
6593+
assert len(rows) == 2, f"Expected 2 rows for date comparison, got {len(rows)}"
6594+
6595+
# Test 5: Verify string functions work with string parameters (the original bug)
6596+
cursor.execute("SELECT id FROM #pytest_string_vs_datetime WHERE RIGHT(string_col, 10) = ?", (date_string,))
6597+
rows = cursor.fetchall()
6598+
assert len(rows) == 1, f"RIGHT() function should work with string parameter, got {len(rows)} rows"
6599+
6600+
except Exception as e:
6601+
pytest.fail(f"String vs datetime parameter behavior test failed: {e}")
6602+
finally:
6603+
drop_table_if_exists(cursor, "#pytest_string_vs_datetime")
6604+
db_connection.commit()
6605+
6606+
def test_string_parameters_no_conversion_error(cursor, db_connection):
6607+
"""
6608+
Test that string parameters that look like dates do NOT get automatically converted.
6609+
6610+
This reproduces the exact scenario from the original bug report.
6611+
"""
6612+
try:
6613+
# Create table similar to original bug report
6614+
drop_table_if_exists(cursor, "#pytest_string_no_conversion")
6615+
cursor.execute("CREATE TABLE #pytest_string_no_conversion (id INT, IncFileName NVARCHAR(100))")
6616+
db_connection.commit()
6617+
6618+
# Insert test data
6619+
cursor.execute("INSERT INTO #pytest_string_no_conversion VALUES (1, 'order_data_2025-08-12')")
6620+
cursor.execute("INSERT INTO #pytest_string_no_conversion VALUES (2, 'order_data_2025-08-13')")
6621+
db_connection.commit()
6622+
6623+
# The exact failing case from the bug report - should now work
6624+
date_str = '2025-08-12'
6625+
cursor.execute("SELECT IncFileName FROM #pytest_string_no_conversion WHERE RIGHT(IncFileName,10) = ?",
6626+
(str(date_str),))
6627+
rows = cursor.fetchall()
6628+
6629+
# Should find exactly one match
6630+
assert len(rows) == 1, f"Expected 1 matching row, got {len(rows)}"
6631+
assert rows[0][0] == "order_data_2025-08-12", "Should find the correct filename"
6632+
6633+
except Exception as e:
6634+
pytest.fail(f"String parameter no conversion error test failed: {e}")
6635+
finally:
6636+
drop_table_if_exists(cursor, "#pytest_string_no_conversion")
6637+
db_connection.commit()
6638+
6639+
def test_datetime_objects_still_work(cursor, db_connection):
6640+
"""
6641+
Test that actual datetime objects still work correctly after our fix.
6642+
6643+
This ensures backward compatibility - existing code using datetime objects
6644+
should continue to work exactly as before.
6645+
"""
6646+
try:
6647+
# Create table with various datetime column types
6648+
drop_table_if_exists(cursor, "#pytest_datetime_still_works")
6649+
cursor.execute("""
6650+
CREATE TABLE #pytest_datetime_still_works (
6651+
id INT PRIMARY KEY,
6652+
date_col DATE,
6653+
datetime_col DATETIME,
6654+
time_col TIME
6655+
)
6656+
""")
6657+
db_connection.commit()
6658+
6659+
# Test with actual datetime objects (should work as before)
6660+
test_date = date(2025, 8, 12)
6661+
test_datetime = datetime(2025, 8, 12, 14, 30, 0)
6662+
test_time = time(14, 30, 0)
6663+
6664+
# INSERT using datetime objects
6665+
cursor.execute("""
6666+
INSERT INTO #pytest_datetime_still_works
6667+
(id, date_col, datetime_col, time_col)
6668+
VALUES (?, ?, ?, ?)
6669+
""", (1, test_date, test_datetime, test_time))
6670+
db_connection.commit()
6671+
6672+
# SELECT using datetime objects as parameters
6673+
cursor.execute("SELECT id FROM #pytest_datetime_still_works WHERE date_col = ?", (test_date,))
6674+
rows = cursor.fetchall()
6675+
assert len(rows) == 1, f"Datetime object parameter should work, got {len(rows)} rows"
6676+
6677+
cursor.execute("SELECT id FROM #pytest_datetime_still_works WHERE time_col = ?", (test_time,))
6678+
rows = cursor.fetchall()
6679+
assert len(rows) == 1, f"Time object parameter should work, got {len(rows)} rows"
6680+
6681+
# Verify the data was stored correctly
6682+
cursor.execute("SELECT date_col, datetime_col, time_col FROM #pytest_datetime_still_works WHERE id = 1")
6683+
row = cursor.fetchone()
6684+
assert row is not None, "Should retrieve the inserted datetime data"
6685+
6686+
except Exception as e:
6687+
pytest.fail(f"Datetime objects still work test failed: {e}")
6688+
finally:
6689+
drop_table_if_exists(cursor, "#pytest_datetime_still_works")
6690+
db_connection.commit()
6691+
65516692
def test_close(db_connection):
65526693
"""Test closing the cursor"""
65536694
try:

0 commit comments

Comments
 (0)