diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py
index 594cd2b0..62572642 100644
--- a/mssql_python/cursor.py
+++ b/mssql_python/cursor.py
@@ -395,8 +395,7 @@ def _map_sql_type( # pylint: disable=too-many-arguments,too-many-positional-arg
# Detect MONEY / SMALLMONEY range
if SMALLMONEY_MIN <= param <= SMALLMONEY_MAX:
- # smallmoney
- parameters_list[i] = str(param)
+ parameters_list[i] = format(param, 'f')
return (
ddbc_sql_const.SQL_VARCHAR.value,
ddbc_sql_const.SQL_C_CHAR.value,
@@ -405,8 +404,7 @@ def _map_sql_type( # pylint: disable=too-many-arguments,too-many-positional-arg
False,
)
if MONEY_MIN <= param <= MONEY_MAX:
- # money
- parameters_list[i] = str(param)
+ parameters_list[i] = format(param, 'f')
return (
ddbc_sql_const.SQL_VARCHAR.value,
ddbc_sql_const.SQL_C_CHAR.value,
@@ -1916,13 +1914,12 @@ def executemany( # pylint: disable=too-many-locals,too-many-branches,too-many-s
for i, val in enumerate(processed_row):
if val is None:
continue
- # Convert Decimals for money/smallmoney to string
if (
isinstance(val, decimal.Decimal)
and parameters_type[i].paramSQLType
== ddbc_sql_const.SQL_VARCHAR.value
):
- processed_row[i] = str(val)
+ processed_row[i] = format(val, 'f')
# Existing numeric conversion
elif parameters_type[i].paramSQLType in (
ddbc_sql_const.SQL_DECIMAL.value,
diff --git a/tests/test_004_cursor.py b/tests/test_004_cursor.py
index bab755d2..83f61e06 100644
--- a/tests/test_004_cursor.py
+++ b/tests/test_004_cursor.py
@@ -13477,20 +13477,6 @@ def test_numeric_leading_zeros_precision_loss(
actual == expected
), f"Leading zeros precision loss for {value}, expected {expected}, got {actual}"
- except Exception as e:
- # Handle cases where values get converted to scientific notation and cause SQL Server conversion errors
- error_msg = str(e).lower()
- if (
- "converting" in error_msg
- and "varchar" in error_msg
- and "numeric" in error_msg
- ):
- pytest.skip(
- f"Value {value} converted to scientific notation, causing expected SQL Server conversion error: {e}"
- )
- else:
- raise # Re-raise unexpected errors
-
finally:
try:
cursor.execute(f"DROP TABLE {table_name}")
@@ -13538,24 +13524,6 @@ def test_numeric_extreme_exponents_precision_loss(
"1E-18"
), f"Extreme exponent value not preserved for {description}: {value} -> {actual}"
- except Exception as e:
- # Handle expected SQL Server validation errors for scientific notation values
- error_msg = str(e).lower()
- if "scale" in error_msg and "range" in error_msg:
- # This is expected - SQL Server rejects invalid scale/precision combinations
- pytest.skip(
- f"Expected SQL Server scale/precision validation for {description}: {e}"
- )
- elif any(
- keyword in error_msg
- for keyword in ["converting", "overflow", "precision", "varchar", "numeric"]
- ):
- # Other expected precision/conversion issues
- pytest.skip(
- f"Expected SQL Server precision limits or VARCHAR conversion for {description}: {e}"
- )
- else:
- raise # Re-raise if it's not a precision-related error
finally:
try:
cursor.execute(f"DROP TABLE {table_name}")
@@ -13563,7 +13531,6 @@ def test_numeric_extreme_exponents_precision_loss(
except:
pass # Table might not exist if creation failed
-
# ---------------------------------------------------------
# Test 12: 38-digit precision boundary limits
# ---------------------------------------------------------
@@ -13659,6 +13626,72 @@ def test_numeric_beyond_38_digit_precision_negative(
), f"Expected SQL Server precision limit message for {description}, got: {error_msg}"
+@pytest.mark.parametrize(
+ "values, description",
+ [
+ # Small decimal values with scientific notation
+ (
+ [
+ decimal.Decimal('0.70000000000696'),
+ decimal.Decimal('1E-7'),
+ decimal.Decimal('0.00001'),
+ decimal.Decimal('6.96E-12'),
+ ],
+ "Small decimals with scientific notation"
+ ),
+ # Large decimal values with scientific notation
+ (
+ [
+ decimal.Decimal('4E+8'),
+ decimal.Decimal('1.521E+15'),
+ decimal.Decimal('5.748E+18'),
+ decimal.Decimal('1E+11')
+ ],
+ "Large decimals with positive exponents"
+ ),
+ # Medium-sized decimals
+ (
+ [
+ decimal.Decimal('123.456'),
+ decimal.Decimal('9999.9999'),
+ decimal.Decimal('1000000.50')
+ ],
+ "Medium-sized decimals"
+ ),
+ ],
+)
+def test_decimal_scientific_notation_to_varchar(cursor, db_connection, values, description):
+ """
+ Test that Decimal values with scientific notation are properly converted
+ to VARCHAR without triggering 'varchar to numeric' conversion errors.
+ This verifies that the driver correctly handles Decimal to VARCHAR conversion
+ """
+ table_name = "#pytest_decimal_varchar_conversion"
+ try:
+ cursor.execute(f"CREATE TABLE {table_name} (id INT IDENTITY(1,1), val VARCHAR(50))")
+
+ for val in values:
+ cursor.execute(f"INSERT INTO {table_name} (val) VALUES (?)", (val,))
+ db_connection.commit()
+
+ cursor.execute(f"SELECT val FROM {table_name} ORDER BY id")
+ rows = cursor.fetchall()
+
+ assert len(rows) == len(values), f"Expected {len(values)} rows, got {len(rows)}"
+
+ for i, (row, expected_val) in enumerate(zip(rows, values)):
+ stored_val = decimal.Decimal(row[0])
+ assert stored_val == expected_val, (
+ f"{description}: Row {i} mismatch - expected {expected_val}, got {stored_val}"
+ )
+
+ finally:
+ try:
+ cursor.execute(f"DROP TABLE {table_name}")
+ db_connection.commit()
+ except:
+ pass
+
SMALL_XML = "- 1
"
LARGE_XML = "" + "".join(f"- {i}
" for i in range(10000)) + ""
EMPTY_XML = ""
@@ -14400,4 +14433,4 @@ def test_close(db_connection):
except Exception as e:
pytest.fail(f"Cursor close test failed: {e}")
finally:
- cursor = db_connection.cursor()
+ cursor = db_connection.cursor()
\ No newline at end of file