2020
2121# Constants for string handling
2222MAX_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
2428class 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 (
@@ -348,16 +373,16 @@ def _map_sql_type(self, param, parameters_list, i):
348373 if utf16_len > MAX_INLINE_CHAR : # Long strings -> DAE
349374 if is_unicode :
350375 return (
351- ddbc_sql_const .SQL_WLONGVARCHAR .value ,
376+ ddbc_sql_const .SQL_WVARCHAR .value ,
352377 ddbc_sql_const .SQL_C_WCHAR .value ,
353- utf16_len ,
378+ 0 ,
354379 0 ,
355380 True ,
356381 )
357382 return (
358- ddbc_sql_const .SQL_LONGVARCHAR .value ,
383+ ddbc_sql_const .SQL_VARCHAR .value ,
359384 ddbc_sql_const .SQL_C_CHAR .value ,
360- len ( param ) ,
385+ 0 ,
361386 0 ,
362387 True ,
363388 )
@@ -379,27 +404,24 @@ def _map_sql_type(self, param, parameters_list, i):
379404 False ,
380405 )
381406
382- if isinstance (param , bytes ):
383- # Use VARBINARY for Python bytes/bytearray since they are variable-length by nature.
384- # This avoids storage waste from BINARY's zero-padding and matches Python's semantics.
385- return (
386- ddbc_sql_const .SQL_VARBINARY .value ,
387- ddbc_sql_const .SQL_C_BINARY .value ,
388- len (param ),
389- 0 ,
390- False ,
391- )
392-
393- if isinstance (param , bytearray ):
394- # Use VARBINARY for Python bytes/bytearray since they are variable-length by nature.
395- # This avoids storage waste from BINARY's zero-padding and matches Python's semantics.
396- return (
397- ddbc_sql_const .SQL_VARBINARY .value ,
398- ddbc_sql_const .SQL_C_BINARY .value ,
399- len (param ),
400- 0 ,
401- False ,
402- )
407+ if isinstance (param , (bytes , bytearray )):
408+ length = len (param )
409+ if length > 8000 : # Use VARBINARY(MAX) for large blobs
410+ return (
411+ ddbc_sql_const .SQL_VARBINARY .value ,
412+ ddbc_sql_const .SQL_C_BINARY .value ,
413+ 0 ,
414+ 0 ,
415+ True
416+ )
417+ else : # Small blobs → direct binding
418+ return (
419+ ddbc_sql_const .SQL_VARBINARY .value ,
420+ ddbc_sql_const .SQL_C_BINARY .value ,
421+ max (length , 1 ),
422+ 0 ,
423+ False
424+ )
403425
404426 if isinstance (param , datetime .datetime ):
405427 return (
0 commit comments