Skip to content

Commit c860e5d

Browse files
committed
resolve review comments
1 parent 46a16ff commit c860e5d

File tree

2 files changed

+336
-122
lines changed

2 files changed

+336
-122
lines changed

mssql_python/cursor.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)