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 (
@@ -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