From fcca70620a211dd6549868bcc77b3b3f8fef4cb2 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Mon, 1 Sep 2025 22:29:02 +0530 Subject: [PATCH 1/8] fixing column type mapping based on best sample value --- mssql_python/cursor.py | 153 +++++++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 66 deletions(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index e2c811c9..9a91af61 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -218,9 +218,9 @@ def _get_numeric_data(self, param): numeric_data.val = val return numeric_data - def _map_sql_type(self, param, parameters_list, i): + def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): """ - Map a Python data type to the corresponding SQL type, + Map a Python data type to the corresponding SQL type, C type, Column size, and Decimal digits. Takes: - param: The parameter to map. @@ -230,19 +230,23 @@ def _map_sql_type(self, param, parameters_list, i): - A tuple containing the SQL type, C type, column size, and decimal digits. """ if param is None: - return ( - ddbc_sql_const.SQL_VARCHAR.value, - ddbc_sql_const.SQL_C_DEFAULT.value, - 1, - 0, - False, - ) + return ( + ddbc_sql_const.SQL_VARCHAR.value, + ddbc_sql_const.SQL_C_DEFAULT.value, + 1, + 0, + False, + ) if isinstance(param, bool): return ddbc_sql_const.SQL_BIT.value, ddbc_sql_const.SQL_C_BIT.value, 1, 0, False if isinstance(param, int): - if 0 <= param <= 255: + # Use min_val/max_val if available + value_to_check = max_val if max_val is not None else param + min_to_check = min_val if min_val is not None else param + + if 0 <= min_to_check and value_to_check <= 255: return ( ddbc_sql_const.SQL_TINYINT.value, ddbc_sql_const.SQL_C_TINYINT.value, @@ -250,7 +254,7 @@ def _map_sql_type(self, param, parameters_list, i): 0, False, ) - if -32768 <= param <= 32767: + if -32768 <= min_to_check and value_to_check <= 32767: return ( ddbc_sql_const.SQL_SMALLINT.value, ddbc_sql_const.SQL_C_SHORT.value, @@ -258,7 +262,7 @@ def _map_sql_type(self, param, parameters_list, i): 0, False, ) - if -2147483648 <= param <= 2147483647: + if -2147483648 <= min_to_check and value_to_check <= 2147483647: return ( ddbc_sql_const.SQL_INTEGER.value, ddbc_sql_const.SQL_C_LONG.value, @@ -382,16 +386,16 @@ def _map_sql_type(self, param, parameters_list, i): if isinstance(param, bytes): if len(param) > 8000: # Assuming VARBINARY(MAX) for long byte arrays return ( - ddbc_sql_const.SQL_VARBINARY.value, + ddbc_sql_const.SQL_LONGVARBINARY.value, ddbc_sql_const.SQL_C_BINARY.value, - len(param), + max(len(param), 1), 0, - False, + True, ) return ( - ddbc_sql_const.SQL_BINARY.value, + ddbc_sql_const.SQL_VARBINARY.value, ddbc_sql_const.SQL_C_BINARY.value, - len(param), + max(len(param), 1), 0, False, ) @@ -399,16 +403,16 @@ def _map_sql_type(self, param, parameters_list, i): if isinstance(param, bytearray): if len(param) > 8000: # Assuming VARBINARY(MAX) for long byte arrays return ( - ddbc_sql_const.SQL_VARBINARY.value, + ddbc_sql_const.SQL_LONGVARBINARY.value, ddbc_sql_const.SQL_C_BINARY.value, - len(param), + max(len(param), 1), 0, True, ) return ( - ddbc_sql_const.SQL_BINARY.value, + ddbc_sql_const.SQL_VARBINARY.value, ddbc_sql_const.SQL_C_BINARY.value, - len(param), + max(len(param), 1), 0, False, ) @@ -505,20 +509,18 @@ def _check_closed(self): driver_error="Operation cannot be performed: the cursor is closed.", ddbc_error="Operation cannot be performed: the cursor is closed." ) - - def _create_parameter_types_list(self, parameter, param_info, parameters_list, i): + + def _create_parameter_types_list(self, parameter, param_info, parameters_list, i, min_val=None, max_val=None): """ Maps parameter types for the given parameter. - Args: parameter: parameter to bind. - Returns: paraminfo. """ paraminfo = param_info() sql_type, c_type, column_size, decimal_digits, is_dae = self._map_sql_type( - parameter, parameters_list, i + parameter, parameters_list, i, min_val=min_val, max_val=max_val ) paraminfo.paramCType = c_type paraminfo.paramSQLType = sql_type @@ -528,7 +530,7 @@ def _create_parameter_types_list(self, parameter, param_info, parameters_list, i paraminfo.isDAE = is_dae if is_dae: - paraminfo.dataPtr = parameter # Will be converted to py::object* in C++ + paraminfo.dataPtr = parameter return paraminfo @@ -824,35 +826,6 @@ def execute( # Return self for method chaining return self - @staticmethod - def _select_best_sample_value(column): - """ - Selects the most representative non-null value from a column for type inference. - - This is used during executemany() to infer SQL/C types based on actual data, - preferring a non-null value that is not the first row to avoid bias from placeholder defaults. - - Args: - column: List of values in the column. - """ - non_nulls = [v for v in column if v is not None] - if not non_nulls: - return None - if all(isinstance(v, int) for v in non_nulls): - # Pick the value with the widest range (min/max) - return max(non_nulls, key=lambda v: abs(v)) - if all(isinstance(v, float) for v in non_nulls): - return 0.0 - if all(isinstance(v, decimal.Decimal) for v in non_nulls): - return max(non_nulls, key=lambda d: len(d.as_tuple().digits)) - if all(isinstance(v, str) for v in non_nulls): - return max(non_nulls, key=lambda s: len(str(s))) - if all(isinstance(v, datetime.datetime) for v in non_nulls): - return datetime.datetime.now() - if all(isinstance(v, datetime.date) for v in non_nulls): - return datetime.date.today() - return non_nulls[0] # fallback - def _transpose_rowwise_to_columnwise(self, seq_of_parameters: list) -> list: """ Convert list of rows (row-wise) into list of columns (column-wise), @@ -872,6 +845,58 @@ def _transpose_rowwise_to_columnwise(self, seq_of_parameters: list) -> list: columnwise[i].append(val) return columnwise + def _compute_column_type(self, column): + """ + Scan all rows in a column to determine: + - representative value (sample_value) + - is_dae flag + - final value to use in dummy row + - min_val/max_val for integers + """ + non_nulls = [v for v in column if v is not None] + if not non_nulls: + return None, False, None, None, None + + is_dae = False + sample_value = None + min_val = max_val = None + + # Handle integers separately to determine min/max + int_values = [v for v in non_nulls if isinstance(v, int)] + if int_values: + min_val, max_val = min(int_values), max(int_values) + sample_value = max(int_values, key=lambda x: abs(x)) + return sample_value, False, sample_value, min_val, max_val + + # Handle other types (strings, bytes, float, decimal, datetime) + for v in non_nulls: + if isinstance(v, str): + utf16_len = sum(2 if ord(c) > 0xFFFF else 1 for c in v) + if utf16_len > MAX_INLINE_CHAR: + is_dae = True + if not sample_value or len(v) > len(sample_value): + sample_value = v + elif isinstance(v, (bytes, bytearray)): + if len(v) > 8000: + is_dae = True + if not sample_value or len(v) > len(sample_value): + sample_value = v + elif isinstance(v, float): + if sample_value is None: + sample_value = 0.0 + elif isinstance(v, decimal.Decimal): + if sample_value is None or len(v.as_tuple().digits) > len(sample_value.as_tuple().digits): + sample_value = v + elif isinstance(v, (datetime.datetime, datetime.date, datetime.time)): + if sample_value is None: + sample_value = v + else: + if sample_value is None: + sample_value = v + + return sample_value, is_dae, sample_value, None, None + + def executemany(self, operation: str, seq_of_parameters: list) -> None: """ Prepare a database operation and execute it against all parameter sequences. @@ -885,10 +910,7 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: """ self._check_closed() self._reset_cursor() - - # Clear any previous messages self.messages = [] - if not seq_of_parameters: self.rowcount = 0 return @@ -899,18 +921,20 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: for col_index in range(param_count): column = [row[col_index] for row in seq_of_parameters] - sample_value = self._select_best_sample_value(column) + sample_value, is_dae, final_value, min_val, max_val = self._compute_column_type(column) dummy_row = list(seq_of_parameters[0]) - parameters_type.append( - self._create_parameter_types_list(sample_value, param_info, dummy_row, col_index) + dummy_row[col_index] = final_value + paraminfo = self._create_parameter_types_list( + sample_value, param_info, dummy_row, col_index, min_val=min_val, max_val=max_val ) + paraminfo.isDAE = is_dae + parameters_type.append(paraminfo) columnwise_params = self._transpose_rowwise_to_columnwise(seq_of_parameters) log('info', "Executing batch query with %d parameter sets:\n%s", 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)) ) - # Execute batched statement ret = ddbc_bindings.SQLExecuteMany( self.hstmt, operation, @@ -920,14 +944,11 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: ) check_error(ddbc_sql_const.SQL_HANDLE_STMT.value, self.hstmt, ret) - # Capture any diagnostic messages after execution if self.hstmt: self.messages.extend(ddbc_bindings.DDBCSQLGetAllDiagRecords(self.hstmt)) - self.rowcount = ddbc_bindings.DDBCSQLRowCount(self.hstmt) self.last_executed_stmt = operation self._initialize_description() - if self.description: self.rowcount = -1 self._reset_rownumber() From 3b76d32b359ac5ed09a1b1aa459b15b5b3665798 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Mon, 1 Sep 2025 22:35:49 +0530 Subject: [PATCH 2/8] cleanup --- mssql_python/cursor.py | 50 +++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 9a91af61..5113a16a 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -230,13 +230,13 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): - A tuple containing the SQL type, C type, column size, and decimal digits. """ if param is None: - return ( - ddbc_sql_const.SQL_VARCHAR.value, - ddbc_sql_const.SQL_C_DEFAULT.value, - 1, - 0, - False, - ) + return ( + ddbc_sql_const.SQL_VARCHAR.value, + ddbc_sql_const.SQL_C_DEFAULT.value, + 1, + 0, + False, + ) if isinstance(param, bool): return ddbc_sql_const.SQL_BIT.value, ddbc_sql_const.SQL_C_BIT.value, 1, 0, False @@ -344,17 +344,14 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): False, ) - # String mapping logic here + # String mapping logic here is_unicode = self._is_unicode_string(param) - - # Computes UTF-16 code units (handles surrogate pairs) - utf16_len = sum(2 if ord(c) > 0xFFFF else 1 for c in param) - if utf16_len > MAX_INLINE_CHAR: # Long strings -> DAE + if len(param) > MAX_INLINE_CHAR: # Long strings if is_unicode: return ( ddbc_sql_const.SQL_WLONGVARCHAR.value, ddbc_sql_const.SQL_C_WCHAR.value, - utf16_len, + len(param), 0, True, ) @@ -365,9 +362,8 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): 0, True, ) - - # Short strings - if is_unicode: + if is_unicode: # Short Unicode strings + utf16_len = len(param.encode("utf-16-le")) // 2 return ( ddbc_sql_const.SQL_WVARCHAR.value, ddbc_sql_const.SQL_C_WCHAR.value, @@ -386,16 +382,16 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): if isinstance(param, bytes): if len(param) > 8000: # Assuming VARBINARY(MAX) for long byte arrays return ( - ddbc_sql_const.SQL_LONGVARBINARY.value, + ddbc_sql_const.SQL_VARBINARY.value, ddbc_sql_const.SQL_C_BINARY.value, - max(len(param), 1), + len(param), 0, - True, + False, ) return ( - ddbc_sql_const.SQL_VARBINARY.value, + ddbc_sql_const.SQL_BINARY.value, ddbc_sql_const.SQL_C_BINARY.value, - max(len(param), 1), + len(param), 0, False, ) @@ -403,20 +399,20 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): if isinstance(param, bytearray): if len(param) > 8000: # Assuming VARBINARY(MAX) for long byte arrays return ( - ddbc_sql_const.SQL_LONGVARBINARY.value, + ddbc_sql_const.SQL_VARBINARY.value, ddbc_sql_const.SQL_C_BINARY.value, - max(len(param), 1), + len(param), 0, True, ) return ( - ddbc_sql_const.SQL_VARBINARY.value, + ddbc_sql_const.SQL_BINARY.value, ddbc_sql_const.SQL_C_BINARY.value, - max(len(param), 1), + len(param), 0, False, ) - + if isinstance(param, datetime.datetime): return ( ddbc_sql_const.SQL_TIMESTAMP.value, @@ -530,7 +526,7 @@ def _create_parameter_types_list(self, parameter, param_info, parameters_list, i paraminfo.isDAE = is_dae if is_dae: - paraminfo.dataPtr = parameter + paraminfo.dataPtr = parameter # Will be converted to py::object* in C++ return paraminfo From 3b9e6a8d7ed838a81a59ae061beb834abbea92de Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Tue, 2 Sep 2025 09:09:59 +0530 Subject: [PATCH 3/8] minor cleanup of comment --- mssql_python/cursor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 5113a16a..27c9606e 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -220,7 +220,7 @@ def _get_numeric_data(self, param): def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): """ - Map a Python data type to the corresponding SQL type, + Map a Python data type to the corresponding SQL type, C type, Column size, and Decimal digits. Takes: - param: The parameter to map. From a8ef24d2b0f563f751d0a2d0613f217902ba4b98 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Tue, 2 Sep 2025 09:15:50 +0530 Subject: [PATCH 4/8] cleanup --- mssql_python/cursor.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 27c9606e..1f08ca2c 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -344,14 +344,17 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): False, ) - # String mapping logic here + # String mapping logic here is_unicode = self._is_unicode_string(param) - if len(param) > MAX_INLINE_CHAR: # Long strings + + # Computes UTF-16 code units (handles surrogate pairs) + utf16_len = sum(2 if ord(c) > 0xFFFF else 1 for c in param) + if utf16_len > MAX_INLINE_CHAR: # Long strings -> DAE if is_unicode: return ( ddbc_sql_const.SQL_WLONGVARCHAR.value, ddbc_sql_const.SQL_C_WCHAR.value, - len(param), + utf16_len, 0, True, ) @@ -362,8 +365,9 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None): 0, True, ) - if is_unicode: # Short Unicode strings - utf16_len = len(param.encode("utf-16-le")) // 2 + + # Short strings + if is_unicode: return ( ddbc_sql_const.SQL_WVARCHAR.value, ddbc_sql_const.SQL_C_WCHAR.value, @@ -906,7 +910,10 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: """ self._check_closed() self._reset_cursor() + + # Clear any previous messages self.messages = [] + if not seq_of_parameters: self.rowcount = 0 return @@ -931,6 +938,7 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: 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)) ) + # Execute batched statement ret = ddbc_bindings.SQLExecuteMany( self.hstmt, operation, @@ -940,11 +948,14 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: ) check_error(ddbc_sql_const.SQL_HANDLE_STMT.value, self.hstmt, ret) + # Capture any diagnostic messages after execution if self.hstmt: self.messages.extend(ddbc_bindings.DDBCSQLGetAllDiagRecords(self.hstmt)) + self.rowcount = ddbc_bindings.DDBCSQLRowCount(self.hstmt) self.last_executed_stmt = operation self._initialize_description() + if self.description: self.rowcount = -1 self._reset_rownumber() From 09d1f9423061e64045a9fd37558b4c74c117cbdf Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Tue, 2 Sep 2025 10:05:13 +0530 Subject: [PATCH 5/8] simplify compute_column_type --- mssql_python/cursor.py | 67 ++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 1f08ca2c..5886b104 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -844,21 +844,26 @@ def _transpose_rowwise_to_columnwise(self, seq_of_parameters: list) -> list: for i, val in enumerate(row): columnwise[i].append(val) return columnwise - + def _compute_column_type(self, column): """ - Scan all rows in a column to determine: - - representative value (sample_value) - - is_dae flag - - final value to use in dummy row - - min_val/max_val for integers + Determine a representative and final value for a column. + + Args: + column: List of values in the column. + + Returns: + sample_value: Representative value for type inference. + final_value: Value to use in modified_row. + min_val: Minimum for integer columns (None otherwise). + max_val: Maximum for integer columns (None otherwise). """ non_nulls = [v for v in column if v is not None] if not non_nulls: - return None, False, None, None, None + return None, None, None, None - is_dae = False sample_value = None + final_value = None min_val = max_val = None # Handle integers separately to determine min/max @@ -866,36 +871,17 @@ def _compute_column_type(self, column): if int_values: min_val, max_val = min(int_values), max(int_values) sample_value = max(int_values, key=lambda x: abs(x)) - return sample_value, False, sample_value, min_val, max_val + final_value = sample_value + return sample_value, final_value, min_val, max_val - # Handle other types (strings, bytes, float, decimal, datetime) + # Handle other types for v in non_nulls: - if isinstance(v, str): - utf16_len = sum(2 if ord(c) > 0xFFFF else 1 for c in v) - if utf16_len > MAX_INLINE_CHAR: - is_dae = True - if not sample_value or len(v) > len(sample_value): - sample_value = v - elif isinstance(v, (bytes, bytearray)): - if len(v) > 8000: - is_dae = True - if not sample_value or len(v) > len(sample_value): - sample_value = v - elif isinstance(v, float): - if sample_value is None: - sample_value = 0.0 - elif isinstance(v, decimal.Decimal): - if sample_value is None or len(v.as_tuple().digits) > len(sample_value.as_tuple().digits): - sample_value = v - elif isinstance(v, (datetime.datetime, datetime.date, datetime.time)): - if sample_value is None: - sample_value = v - else: - if sample_value is None: - sample_value = v - - return sample_value, is_dae, sample_value, None, None + if not sample_value or (hasattr(v, '__len__') and len(v) > len(sample_value)): + sample_value = v + if final_value is None: + final_value = v + return sample_value, final_value, None, None def executemany(self, operation: str, seq_of_parameters: list) -> None: """ @@ -924,13 +910,12 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: for col_index in range(param_count): column = [row[col_index] for row in seq_of_parameters] - sample_value, is_dae, final_value, min_val, max_val = self._compute_column_type(column) - dummy_row = list(seq_of_parameters[0]) - dummy_row[col_index] = final_value + sample_value, final_value, min_val, max_val = self._compute_column_type(column) + modified_row = list(seq_of_parameters[0]) + modified_row[col_index] = final_value paraminfo = self._create_parameter_types_list( - sample_value, param_info, dummy_row, col_index, min_val=min_val, max_val=max_val + final_value, param_info, modified_row, col_index, min_val=min_val, max_val=max_val ) - paraminfo.isDAE = is_dae parameters_type.append(paraminfo) columnwise_params = self._transpose_rowwise_to_columnwise(seq_of_parameters) @@ -955,7 +940,7 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: self.rowcount = ddbc_bindings.DDBCSQLRowCount(self.hstmt) self.last_executed_stmt = operation self._initialize_description() - + if self.description: self.rowcount = -1 self._reset_rownumber() From ab3a5f77568a84b96e1ceef58d6ac892c4ca2043 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Tue, 2 Sep 2025 10:29:35 +0530 Subject: [PATCH 6/8] fix tests --- mssql_python/cursor.py | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 5886b104..656d48f7 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -847,41 +847,29 @@ def _transpose_rowwise_to_columnwise(self, seq_of_parameters: list) -> list: def _compute_column_type(self, column): """ - Determine a representative and final value for a column. - - Args: - column: List of values in the column. + Determine representative value and integer min/max for a column. Returns: - sample_value: Representative value for type inference. - final_value: Value to use in modified_row. - min_val: Minimum for integer columns (None otherwise). - max_val: Maximum for integer columns (None otherwise). + sample_value: Representative value for type inference and modified_row. + min_val: Minimum for integers (None otherwise). + max_val: Maximum for integers (None otherwise). """ non_nulls = [v for v in column if v is not None] if not non_nulls: - return None, None, None, None + return None, None, None - sample_value = None - final_value = None - min_val = max_val = None - - # Handle integers separately to determine min/max int_values = [v for v in non_nulls if isinstance(v, int)] if int_values: min_val, max_val = min(int_values), max(int_values) - sample_value = max(int_values, key=lambda x: abs(x)) - final_value = sample_value - return sample_value, final_value, min_val, max_val + sample_value = max(int_values, key=abs) + return sample_value, min_val, max_val - # Handle other types + sample_value = None for v in non_nulls: if not sample_value or (hasattr(v, '__len__') and len(v) > len(sample_value)): sample_value = v - if final_value is None: - final_value = v - return sample_value, final_value, None, None + return sample_value, None, None def executemany(self, operation: str, seq_of_parameters: list) -> None: """ @@ -910,11 +898,11 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: for col_index in range(param_count): column = [row[col_index] for row in seq_of_parameters] - sample_value, final_value, min_val, max_val = self._compute_column_type(column) + sample_value, min_val, max_val = self._compute_column_type(column) modified_row = list(seq_of_parameters[0]) - modified_row[col_index] = final_value + modified_row[col_index] = sample_value paraminfo = self._create_parameter_types_list( - final_value, param_info, modified_row, col_index, min_val=min_val, max_val=max_val + sample_value, param_info, modified_row, col_index, min_val=min_val, max_val=max_val ) parameters_type.append(paraminfo) From 5bcb1bccf532146bdebf49149b54ee7929fb0113 Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Thu, 11 Sep 2025 18:27:02 +0530 Subject: [PATCH 7/8] addressed review comment --- mssql_python/cursor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 656d48f7..4cdd4a74 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -907,7 +907,7 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: parameters_type.append(paraminfo) columnwise_params = self._transpose_rowwise_to_columnwise(seq_of_parameters) - log('info', "Executing batch query with %d parameter sets:\n%s", + log('debug', "Executing batch query with %d parameter sets:\n%s", 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)) ) From a9f633cf983a5fb03a26aa987e0d5dc4851f105d Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Wed, 17 Sep 2025 13:07:37 +0530 Subject: [PATCH 8/8] added comment --- mssql_python/cursor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 4cdd4a74..481db9ee 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -901,6 +901,7 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None: sample_value, min_val, max_val = self._compute_column_type(column) modified_row = list(seq_of_parameters[0]) modified_row[col_index] = sample_value + # sending original values for all rows here, we may change this if any inconsistent behavior is observed paraminfo = self._create_parameter_types_list( sample_value, param_info, modified_row, col_index, min_val=min_val, max_val=max_val )