Skip to content

Commit 8a9befe

Browse files
committed
Merge remote-tracking branch 'origin/datetime_conversion_error' into datetime_conversion_error
2 parents 92553a3 + 2a7ac20 commit 8a9befe

File tree

4 files changed

+1119
-599
lines changed

4 files changed

+1119
-599
lines changed

mssql_python/cursor.py

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
# Constants for string handling
2222
MAX_INLINE_CHAR = 4000 # NVARCHAR/VARCHAR inline limit; this triggers NVARCHAR(MAX)/VARCHAR(MAX) + DAE
23+
SMALLMONEY_MIN = decimal.Decimal('-214748.3648')
24+
SMALLMONEY_MAX = decimal.Decimal('214748.3647')
25+
MONEY_MIN = decimal.Decimal('-922337203685477.5808')
26+
MONEY_MAX = decimal.Decimal('922337203685477.5807')
2327

2428
class Cursor:
2529
"""
@@ -282,18 +286,39 @@ def _map_sql_type(self, param, parameters_list, i):
282286
0,
283287
False,
284288
)
285-
289+
286290
if isinstance(param, decimal.Decimal):
287-
parameters_list[i] = self._get_numeric_data(
288-
param
289-
) # Replace the parameter with the dictionary
290-
return (
291-
ddbc_sql_const.SQL_NUMERIC.value,
292-
ddbc_sql_const.SQL_C_NUMERIC.value,
293-
parameters_list[i].precision,
294-
parameters_list[i].scale,
295-
False,
296-
)
291+
# Detect MONEY / SMALLMONEY range
292+
if SMALLMONEY_MIN <= param <= SMALLMONEY_MAX:
293+
# smallmoney
294+
parameters_list[i] = str(param)
295+
return (
296+
ddbc_sql_const.SQL_VARCHAR.value,
297+
ddbc_sql_const.SQL_C_CHAR.value,
298+
len(parameters_list[i]),
299+
0,
300+
False,
301+
)
302+
elif MONEY_MIN <= param <= MONEY_MAX:
303+
# money
304+
parameters_list[i] = str(param)
305+
return (
306+
ddbc_sql_const.SQL_VARCHAR.value,
307+
ddbc_sql_const.SQL_C_CHAR.value,
308+
len(parameters_list[i]),
309+
0,
310+
False,
311+
)
312+
else:
313+
# fallback to generic numeric binding
314+
parameters_list[i] = self._get_numeric_data(param)
315+
return (
316+
ddbc_sql_const.SQL_NUMERIC.value,
317+
ddbc_sql_const.SQL_C_NUMERIC.value,
318+
parameters_list[i].precision,
319+
parameters_list[i].scale,
320+
False,
321+
)
297322

298323
if isinstance(param, str):
299324
if (
@@ -317,16 +342,16 @@ def _map_sql_type(self, param, parameters_list, i):
317342
if utf16_len > MAX_INLINE_CHAR: # Long strings -> DAE
318343
if is_unicode:
319344
return (
320-
ddbc_sql_const.SQL_WLONGVARCHAR.value,
345+
ddbc_sql_const.SQL_WVARCHAR.value,
321346
ddbc_sql_const.SQL_C_WCHAR.value,
322-
utf16_len,
347+
0,
323348
0,
324349
True,
325350
)
326351
return (
327-
ddbc_sql_const.SQL_LONGVARCHAR.value,
352+
ddbc_sql_const.SQL_VARCHAR.value,
328353
ddbc_sql_const.SQL_C_CHAR.value,
329-
len(param),
354+
0,
330355
0,
331356
True,
332357
)
@@ -348,27 +373,24 @@ def _map_sql_type(self, param, parameters_list, i):
348373
False,
349374
)
350375

351-
if isinstance(param, bytes):
352-
# Use VARBINARY for Python bytes/bytearray since they are variable-length by nature.
353-
# This avoids storage waste from BINARY's zero-padding and matches Python's semantics.
354-
return (
355-
ddbc_sql_const.SQL_VARBINARY.value,
356-
ddbc_sql_const.SQL_C_BINARY.value,
357-
len(param),
358-
0,
359-
False,
360-
)
361-
362-
if isinstance(param, bytearray):
363-
# Use VARBINARY for Python bytes/bytearray since they are variable-length by nature.
364-
# This avoids storage waste from BINARY's zero-padding and matches Python's semantics.
365-
return (
366-
ddbc_sql_const.SQL_VARBINARY.value,
367-
ddbc_sql_const.SQL_C_BINARY.value,
368-
len(param),
369-
0,
370-
False,
371-
)
376+
if isinstance(param, (bytes, bytearray)):
377+
length = len(param)
378+
if length > 8000: # Use VARBINARY(MAX) for large blobs
379+
return (
380+
ddbc_sql_const.SQL_VARBINARY.value,
381+
ddbc_sql_const.SQL_C_BINARY.value,
382+
0,
383+
0,
384+
True
385+
)
386+
else: # Small blobs → direct binding
387+
return (
388+
ddbc_sql_const.SQL_VARBINARY.value,
389+
ddbc_sql_const.SQL_C_BINARY.value,
390+
max(length, 1),
391+
0,
392+
False
393+
)
372394

373395
if isinstance(param, datetime.datetime):
374396
return (

0 commit comments

Comments
 (0)