Skip to content

Commit 389ab49

Browse files
authored
Merge branch 'main' into saumya/api-bug
2 parents 60ebb62 + 5d50020 commit 389ab49

File tree

2 files changed

+145
-36
lines changed

2 files changed

+145
-36
lines changed

mssql_python/cursor.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,14 +1679,23 @@ def executemany(self, operation: str, seq_of_parameters: list) -> None:
16791679
for row in seq_of_parameters:
16801680
processed_row = list(row)
16811681
for i, val in enumerate(processed_row):
1682-
if (parameters_type[i].paramSQLType in
1682+
if val is None:
1683+
continue
1684+
# Convert Decimals for money/smallmoney to string
1685+
if isinstance(val, decimal.Decimal) and parameters_type[i].paramSQLType == ddbc_sql_const.SQL_VARCHAR.value:
1686+
processed_row[i] = str(val)
1687+
# Existing numeric conversion
1688+
elif (parameters_type[i].paramSQLType in
16831689
(ddbc_sql_const.SQL_DECIMAL.value, ddbc_sql_const.SQL_NUMERIC.value) and
1684-
not isinstance(val, decimal.Decimal) and val is not None):
1690+
not isinstance(val, decimal.Decimal)):
16851691
try:
16861692
processed_row[i] = decimal.Decimal(str(val))
1687-
except:
1688-
pass # Keep original value if conversion fails
1693+
except Exception as e:
1694+
raise ValueError(
1695+
f"Failed to convert parameter at row {row}, column {i} to Decimal: {e}"
1696+
)
16891697
processed_parameters.append(processed_row)
1698+
16901699

16911700
# Now transpose the processed parameters
16921701
columnwise_params, row_count = self._transpose_rowwise_to_columnwise(processed_parameters)

tests/test_004_cursor.py

Lines changed: 132 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6729,9 +6729,9 @@ def test_nvarcharmax_large(cursor, db_connection):
67296729
def test_money_smallmoney_insert_fetch(cursor, db_connection):
67306730
"""Test inserting and retrieving valid MONEY and SMALLMONEY values including boundaries and typical data"""
67316731
try:
6732-
drop_table_if_exists(cursor, "dbo.money_test")
6732+
drop_table_if_exists(cursor, "#pytest_money_test")
67336733
cursor.execute("""
6734-
CREATE TABLE dbo.money_test (
6734+
CREATE TABLE #pytest_money_test (
67356735
id INT IDENTITY PRIMARY KEY,
67366736
m MONEY,
67376737
sm SMALLMONEY,
@@ -6742,27 +6742,27 @@ def test_money_smallmoney_insert_fetch(cursor, db_connection):
67426742
db_connection.commit()
67436743

67446744
# Max values
6745-
cursor.execute("INSERT INTO dbo.money_test (m, sm, d, n) VALUES (?, ?, ?, ?)",
6745+
cursor.execute("INSERT INTO #pytest_money_test (m, sm, d, n) VALUES (?, ?, ?, ?)",
67466746
(decimal.Decimal("922337203685477.5807"), decimal.Decimal("214748.3647"),
67476747
decimal.Decimal("9999999999999.9999"), decimal.Decimal("1234.5678")))
67486748

67496749
# Min values
6750-
cursor.execute("INSERT INTO dbo.money_test (m, sm, d, n) VALUES (?, ?, ?, ?)",
6750+
cursor.execute("INSERT INTO #pytest_money_test (m, sm, d, n) VALUES (?, ?, ?, ?)",
67516751
(decimal.Decimal("-922337203685477.5808"), decimal.Decimal("-214748.3648"),
67526752
decimal.Decimal("-9999999999999.9999"), decimal.Decimal("-1234.5678")))
67536753

67546754
# Typical values
6755-
cursor.execute("INSERT INTO dbo.money_test (m, sm, d, n) VALUES (?, ?, ?, ?)",
6755+
cursor.execute("INSERT INTO #pytest_money_test (m, sm, d, n) VALUES (?, ?, ?, ?)",
67566756
(decimal.Decimal("1234567.8901"), decimal.Decimal("12345.6789"),
67576757
decimal.Decimal("42.4242"), decimal.Decimal("3.1415")))
67586758

67596759
# NULL values
6760-
cursor.execute("INSERT INTO dbo.money_test (m, sm, d, n) VALUES (?, ?, ?, ?)",
6760+
cursor.execute("INSERT INTO #pytest_money_test (m, sm, d, n) VALUES (?, ?, ?, ?)",
67616761
(None, None, None, None))
67626762

67636763
db_connection.commit()
67646764

6765-
cursor.execute("SELECT m, sm, d, n FROM dbo.money_test ORDER BY id")
6765+
cursor.execute("SELECT m, sm, d, n FROM #pytest_money_test ORDER BY id")
67666766
results = cursor.fetchall()
67676767
assert len(results) == 4, f"Expected 4 rows, got {len(results)}"
67686768

@@ -6787,16 +6787,15 @@ def test_money_smallmoney_insert_fetch(cursor, db_connection):
67876787
except Exception as e:
67886788
pytest.fail(f"MONEY and SMALLMONEY insert/fetch test failed: {e}")
67896789
finally:
6790-
drop_table_if_exists(cursor, "dbo.money_test")
6790+
drop_table_if_exists(cursor, "#pytest_money_test")
67916791
db_connection.commit()
67926792

67936793

67946794
def test_money_smallmoney_null_handling(cursor, db_connection):
67956795
"""Test that NULL values for MONEY and SMALLMONEY are stored and retrieved correctly"""
67966796
try:
6797-
drop_table_if_exists(cursor, "dbo.money_test")
67986797
cursor.execute("""
6799-
CREATE TABLE dbo.money_test (
6798+
CREATE TABLE #pytest_money_test (
68006799
id INT IDENTITY PRIMARY KEY,
68016800
m MONEY,
68026801
sm SMALLMONEY
@@ -6805,19 +6804,19 @@ def test_money_smallmoney_null_handling(cursor, db_connection):
68056804
db_connection.commit()
68066805

68076806
# Row with both NULLs
6808-
cursor.execute("INSERT INTO dbo.money_test (m, sm) VALUES (?, ?)", (None, None))
6807+
cursor.execute("INSERT INTO #pytest_money_test (m, sm) VALUES (?, ?)", (None, None))
68096808

68106809
# Row with m filled, sm NULL
6811-
cursor.execute("INSERT INTO dbo.money_test (m, sm) VALUES (?, ?)",
6810+
cursor.execute("INSERT INTO #pytest_money_test (m, sm) VALUES (?, ?)",
68126811
(decimal.Decimal("123.4500"), None))
68136812

68146813
# Row with m NULL, sm filled
6815-
cursor.execute("INSERT INTO dbo.money_test (m, sm) VALUES (?, ?)",
6814+
cursor.execute("INSERT INTO #pytest_money_test (m, sm) VALUES (?, ?)",
68166815
(None, decimal.Decimal("67.8900")))
68176816

68186817
db_connection.commit()
68196818

6820-
cursor.execute("SELECT m, sm FROM dbo.money_test ORDER BY id")
6819+
cursor.execute("SELECT m, sm FROM #pytest_money_test ORDER BY id")
68216820
results = cursor.fetchall()
68226821
assert len(results) == 3, f"Expected 3 rows, got {len(results)}"
68236822

@@ -6838,16 +6837,15 @@ def test_money_smallmoney_null_handling(cursor, db_connection):
68386837
except Exception as e:
68396838
pytest.fail(f"MONEY and SMALLMONEY NULL handling test failed: {e}")
68406839
finally:
6841-
drop_table_if_exists(cursor, "dbo.money_test")
6840+
drop_table_if_exists(cursor, "#pytest_money_test")
68426841
db_connection.commit()
68436842

68446843

68456844
def test_money_smallmoney_roundtrip(cursor, db_connection):
68466845
"""Test inserting and retrieving MONEY and SMALLMONEY using decimal.Decimal roundtrip"""
68476846
try:
6848-
drop_table_if_exists(cursor, "dbo.money_test")
68496847
cursor.execute("""
6850-
CREATE TABLE dbo.money_test (
6848+
CREATE TABLE #pytest_money_test (
68516849
id INT IDENTITY PRIMARY KEY,
68526850
m MONEY,
68536851
sm SMALLMONEY
@@ -6856,10 +6854,10 @@ def test_money_smallmoney_roundtrip(cursor, db_connection):
68566854
db_connection.commit()
68576855

68586856
values = (decimal.Decimal("12345.6789"), decimal.Decimal("987.6543"))
6859-
cursor.execute("INSERT INTO dbo.money_test (m, sm) VALUES (?, ?)", values)
6857+
cursor.execute("INSERT INTO #pytest_money_test (m, sm) VALUES (?, ?)", values)
68606858
db_connection.commit()
68616859

6862-
cursor.execute("SELECT m, sm FROM dbo.money_test ORDER BY id DESC")
6860+
cursor.execute("SELECT m, sm FROM #pytest_money_test ORDER BY id DESC")
68636861
row = cursor.fetchone()
68646862
for i, (val, exp_val) in enumerate(zip(row, values), 1):
68656863
assert val == exp_val, f"col{i} roundtrip mismatch, got {val}, expected {exp_val}"
@@ -6868,16 +6866,16 @@ def test_money_smallmoney_roundtrip(cursor, db_connection):
68686866
except Exception as e:
68696867
pytest.fail(f"MONEY and SMALLMONEY roundtrip test failed: {e}")
68706868
finally:
6871-
drop_table_if_exists(cursor, "dbo.money_test")
6869+
drop_table_if_exists(cursor, "#pytest_money_test")
68726870
db_connection.commit()
68736871

68746872

68756873
def test_money_smallmoney_boundaries(cursor, db_connection):
68766874
"""Test boundary values for MONEY and SMALLMONEY types are handled correctly"""
68776875
try:
6878-
drop_table_if_exists(cursor, "dbo.money_test")
6876+
drop_table_if_exists(cursor, "#pytest_money_test")
68796877
cursor.execute("""
6880-
CREATE TABLE dbo.money_test (
6878+
CREATE TABLE #pytest_money_test (
68816879
id INT IDENTITY PRIMARY KEY,
68826880
m MONEY,
68836881
sm SMALLMONEY
@@ -6886,16 +6884,16 @@ def test_money_smallmoney_boundaries(cursor, db_connection):
68866884
db_connection.commit()
68876885

68886886
# Insert max boundary
6889-
cursor.execute("INSERT INTO dbo.money_test (m, sm) VALUES (?, ?)",
6887+
cursor.execute("INSERT INTO #pytest_money_test (m, sm) VALUES (?, ?)",
68906888
(decimal.Decimal("922337203685477.5807"), decimal.Decimal("214748.3647")))
68916889

68926890
# Insert min boundary
6893-
cursor.execute("INSERT INTO dbo.money_test (m, sm) VALUES (?, ?)",
6891+
cursor.execute("INSERT INTO #pytest_money_test (m, sm) VALUES (?, ?)",
68946892
(decimal.Decimal("-922337203685477.5808"), decimal.Decimal("-214748.3648")))
68956893

68966894
db_connection.commit()
68976895

6898-
cursor.execute("SELECT m, sm FROM dbo.money_test ORDER BY id DESC")
6896+
cursor.execute("SELECT m, sm FROM #pytest_money_test ORDER BY id DESC")
68996897
results = cursor.fetchall()
69006898
expected = [
69016899
(decimal.Decimal("-922337203685477.5808"), decimal.Decimal("-214748.3648")),
@@ -6909,16 +6907,15 @@ def test_money_smallmoney_boundaries(cursor, db_connection):
69096907
except Exception as e:
69106908
pytest.fail(f"MONEY and SMALLMONEY boundary values test failed: {e}")
69116909
finally:
6912-
drop_table_if_exists(cursor, "dbo.money_test")
6910+
drop_table_if_exists(cursor, "#pytest_money_test")
69136911
db_connection.commit()
69146912

69156913

69166914
def test_money_smallmoney_invalid_values(cursor, db_connection):
69176915
"""Test that invalid or out-of-range MONEY and SMALLMONEY values raise errors"""
69186916
try:
6919-
drop_table_if_exists(cursor, "dbo.money_test")
69206917
cursor.execute("""
6921-
CREATE TABLE dbo.money_test (
6918+
CREATE TABLE #pytest_money_test (
69226919
id INT IDENTITY PRIMARY KEY,
69236920
m MONEY,
69246921
sm SMALLMONEY
@@ -6928,20 +6925,123 @@ def test_money_smallmoney_invalid_values(cursor, db_connection):
69286925

69296926
# Out of range MONEY
69306927
with pytest.raises(Exception):
6931-
cursor.execute("INSERT INTO dbo.money_test (m) VALUES (?)", (decimal.Decimal("922337203685477.5808"),))
6928+
cursor.execute("INSERT INTO #pytest_money_test (m) VALUES (?)", (decimal.Decimal("922337203685477.5808"),))
69326929

69336930
# Out of range SMALLMONEY
69346931
with pytest.raises(Exception):
6935-
cursor.execute("INSERT INTO dbo.money_test (sm) VALUES (?)", (decimal.Decimal("214748.3648"),))
6932+
cursor.execute("INSERT INTO #pytest_money_test (sm) VALUES (?)", (decimal.Decimal("214748.3648"),))
69366933

69376934
# Invalid string
69386935
with pytest.raises(Exception):
6939-
cursor.execute("INSERT INTO dbo.money_test (m) VALUES (?)", ("invalid_string",))
6936+
cursor.execute("INSERT INTO #pytest_money_test (m) VALUES (?)", ("invalid_string",))
69406937

69416938
except Exception as e:
69426939
pytest.fail(f"MONEY and SMALLMONEY invalid values test failed: {e}")
69436940
finally:
6944-
drop_table_if_exists(cursor, "dbo.money_test")
6941+
drop_table_if_exists(cursor, "#pytest_money_test")
6942+
db_connection.commit()
6943+
6944+
def test_money_smallmoney_roundtrip_executemany(cursor, db_connection):
6945+
"""Test inserting and retrieving MONEY and SMALLMONEY using executemany with decimal.Decimal"""
6946+
try:
6947+
cursor.execute("""
6948+
CREATE TABLE #pytest_money_test (
6949+
id INT IDENTITY PRIMARY KEY,
6950+
m MONEY,
6951+
sm SMALLMONEY
6952+
)
6953+
""")
6954+
db_connection.commit()
6955+
6956+
test_data = [
6957+
(decimal.Decimal("12345.6789"), decimal.Decimal("987.6543")),
6958+
(decimal.Decimal("0.0001"), decimal.Decimal("0.01")),
6959+
(None, decimal.Decimal("42.42")),
6960+
(decimal.Decimal("-1000.99"), None),
6961+
]
6962+
6963+
# Insert using executemany directly with Decimals
6964+
cursor.executemany(
6965+
"INSERT INTO #pytest_money_test (m, sm) VALUES (?, ?)",
6966+
test_data
6967+
)
6968+
db_connection.commit()
6969+
6970+
cursor.execute("SELECT m, sm FROM #pytest_money_test ORDER BY id")
6971+
results = cursor.fetchall()
6972+
assert len(results) == len(test_data)
6973+
6974+
for i, (row, expected) in enumerate(zip(results, test_data), 1):
6975+
for j, (val, exp_val) in enumerate(zip(row, expected), 1):
6976+
if exp_val is None:
6977+
assert val is None
6978+
else:
6979+
assert val == exp_val
6980+
assert isinstance(val, decimal.Decimal)
6981+
6982+
finally:
6983+
drop_table_if_exists(cursor, "#pytest_money_test")
6984+
db_connection.commit()
6985+
6986+
6987+
def test_money_smallmoney_executemany_null_handling(cursor, db_connection):
6988+
"""Test inserting NULLs into MONEY and SMALLMONEY using executemany"""
6989+
try:
6990+
cursor.execute("""
6991+
CREATE TABLE #pytest_money_test (
6992+
id INT IDENTITY PRIMARY KEY,
6993+
m MONEY,
6994+
sm SMALLMONEY
6995+
)
6996+
""")
6997+
db_connection.commit()
6998+
6999+
rows = [
7000+
(None, None),
7001+
(decimal.Decimal("123.4500"), None),
7002+
(None, decimal.Decimal("67.8900")),
7003+
]
7004+
cursor.executemany("INSERT INTO #pytest_money_test (m, sm) VALUES (?, ?)", rows)
7005+
db_connection.commit()
7006+
7007+
cursor.execute("SELECT m, sm FROM #pytest_money_test ORDER BY id ASC")
7008+
results = cursor.fetchall()
7009+
assert len(results) == len(rows)
7010+
7011+
for row, expected in zip(results, rows):
7012+
for val, exp_val in zip(row, expected):
7013+
if exp_val is None:
7014+
assert val is None
7015+
else:
7016+
assert val == exp_val
7017+
assert isinstance(val, decimal.Decimal)
7018+
7019+
finally:
7020+
drop_table_if_exists(cursor, "#pytest_money_test")
7021+
db_connection.commit()
7022+
7023+
def test_money_smallmoney_out_of_range_low(cursor, db_connection):
7024+
"""Test inserting values just below the minimum MONEY/SMALLMONEY range raises error"""
7025+
try:
7026+
drop_table_if_exists(cursor, "#pytest_money_test")
7027+
cursor.execute("CREATE TABLE #pytest_money_test (m MONEY, sm SMALLMONEY)")
7028+
db_connection.commit()
7029+
7030+
# Just below minimum MONEY
7031+
with pytest.raises(Exception):
7032+
cursor.execute(
7033+
"INSERT INTO #pytest_money_test (m) VALUES (?)",
7034+
(decimal.Decimal("-922337203685477.5809"),)
7035+
)
7036+
7037+
# Just below minimum SMALLMONEY
7038+
with pytest.raises(Exception):
7039+
cursor.execute(
7040+
"INSERT INTO #pytest_money_test (sm) VALUES (?)",
7041+
(decimal.Decimal("-214748.3649"),)
7042+
)
7043+
finally:
7044+
drop_table_if_exists(cursor, "#pytest_money_test")
69457045
db_connection.commit()
69467046

69477047
def test_uuid_insert_and_select_none(cursor, db_connection):

0 commit comments

Comments
 (0)