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