@@ -617,10 +617,41 @@ def execute(
617617 # Initialize description after execution
618618 self ._initialize_description ()
619619
620+ @staticmethod
621+ def _select_best_sample_value (column ):
622+ """
623+ Selects the most representative non-null value from a column for type inference.
624+
625+ This is used during executemany() to infer SQL/C types based on actual data,
626+ preferring a non-null value that is not the first row to avoid bias from placeholder defaults.
627+
628+ Args:
629+ column: List of values in the column.
630+ """
631+ non_nulls = [v for v in column if v is not None ]
632+ if not non_nulls :
633+ return None
634+ if all (isinstance (v , int ) for v in non_nulls ):
635+ # Pick the value with the widest range (min/max)
636+ return max (non_nulls , key = lambda v : abs (v ))
637+ if all (isinstance (v , float ) for v in non_nulls ):
638+ return 0.0
639+ if all (isinstance (v , decimal .Decimal ) for v in non_nulls ):
640+ return max (non_nulls , key = lambda d : len (d .as_tuple ().digits ))
641+ if all (isinstance (v , str ) for v in non_nulls ):
642+ return max (non_nulls , key = lambda s : len (str (s )))
643+ if all (isinstance (v , datetime .datetime ) for v in non_nulls ):
644+ return datetime .datetime .now ()
645+ if all (isinstance (v , datetime .date ) for v in non_nulls ):
646+ return datetime .date .today ()
647+ return non_nulls [0 ] # fallback
648+
620649 def _transpose_rowwise_to_columnwise (self , seq_of_parameters : list ) -> list :
621650 """
622651 Convert list of rows (row-wise) into list of columns (column-wise),
623652 for array binding via ODBC.
653+ Args:
654+ seq_of_parameters: Sequence of sequences or mappings of parameters.
624655 """
625656 if not seq_of_parameters :
626657 return []
@@ -658,16 +689,17 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None:
658689
659690 for col_index in range (param_count ):
660691 column = [row [col_index ] for row in seq_of_parameters ]
661- sample_value = column [0 ]
662- if isinstance (sample_value , str ):
663- sample_value = max (column , key = lambda s : len (str (s )) if s is not None else 0 )
664- elif isinstance (sample_value , decimal .Decimal ):
665- sample_value = max (column , key = lambda d : len (d .as_tuple ().digits ) if d is not None else 0 )
666- param = sample_value
692+ sample_value = self ._select_best_sample_value (column )
667693 dummy_row = list (seq_of_parameters [0 ])
668- parameters_type .append (self ._create_parameter_types_list (param , param_info , dummy_row , col_index ))
694+ parameters_type .append (
695+ self ._create_parameter_types_list (sample_value , param_info , dummy_row , col_index )
696+ )
669697
670698 columnwise_params = self ._transpose_rowwise_to_columnwise (seq_of_parameters )
699+ if ENABLE_LOGGING :
700+ logger .info ("Executing batch query with %d parameter sets:\n %s" ,
701+ len (seq_of_parameters ),"\n " .join (f" { i + 1 } : { tuple (p ) if isinstance (p , (list , tuple )) else p } " for i , p in enumerate (seq_of_parameters ))
702+ )
671703
672704 # Execute batched statement
673705 ret = ddbc_bindings .SQLExecuteMany (
0 commit comments