Skip to content

Commit f0eacda

Browse files
authored
Merge branch 'main' into datetime_conversion_error
2 parents 83272b0 + 7d06c96 commit f0eacda

File tree

4 files changed

+1067
-489
lines changed

4 files changed

+1067
-489
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 (
@@ -316,16 +341,16 @@ def _map_sql_type(self, param, parameters_list, i):
316341
if utf16_len > MAX_INLINE_CHAR: # Long strings -> DAE
317342
if is_unicode:
318343
return (
319-
ddbc_sql_const.SQL_WLONGVARCHAR.value,
344+
ddbc_sql_const.SQL_WVARCHAR.value,
320345
ddbc_sql_const.SQL_C_WCHAR.value,
321-
utf16_len,
346+
0,
322347
0,
323348
True,
324349
)
325350
return (
326-
ddbc_sql_const.SQL_LONGVARCHAR.value,
351+
ddbc_sql_const.SQL_VARCHAR.value,
327352
ddbc_sql_const.SQL_C_CHAR.value,
328-
len(param),
353+
0,
329354
0,
330355
True,
331356
)
@@ -347,27 +372,24 @@ def _map_sql_type(self, param, parameters_list, i):
347372
False,
348373
)
349374

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

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

0 commit comments

Comments
 (0)