Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 8 additions & 20 deletions mssql_python/pybind/ddbc_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2235,17 +2235,14 @@ SQLRETURN BindParameterArray(SqlHandle& handle, SQLHANDLE hStmt, const py::list&
"param_index=%d, count=%zu",
paramIndex, paramSetSize);
int* dataArray = AllocateParamBufferArray<int>(tempBuffers, paramSetSize);
strLenOrIndArray = AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
for (size_t i = 0; i < paramSetSize; ++i) {
if (columnValues[i].is_none()) {
if (!strLenOrIndArray)
strLenOrIndArray =
AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
dataArray[i] = 0;
strLenOrIndArray[i] = SQL_NULL_DATA;
} else {
dataArray[i] = columnValues[i].cast<int>();
if (strLenOrIndArray)
strLenOrIndArray[i] = 0;
strLenOrIndArray[i] = 0;
}
}
LOG("BindParameterArray: SQL_C_LONG bound - param_index=%d", paramIndex);
Expand All @@ -2257,17 +2254,14 @@ SQLRETURN BindParameterArray(SqlHandle& handle, SQLHANDLE hStmt, const py::list&
"param_index=%d, count=%zu",
paramIndex, paramSetSize);
double* dataArray = AllocateParamBufferArray<double>(tempBuffers, paramSetSize);
strLenOrIndArray = AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
for (size_t i = 0; i < paramSetSize; ++i) {
if (columnValues[i].is_none()) {
if (!strLenOrIndArray)
strLenOrIndArray =
AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
dataArray[i] = 0;
strLenOrIndArray[i] = SQL_NULL_DATA;
} else {
dataArray[i] = columnValues[i].cast<double>();
if (strLenOrIndArray)
strLenOrIndArray[i] = 0;
strLenOrIndArray[i] = 0;
}
}
LOG("BindParameterArray: SQL_C_DOUBLE bound - "
Expand Down Expand Up @@ -2316,11 +2310,9 @@ SQLRETURN BindParameterArray(SqlHandle& handle, SQLHANDLE hStmt, const py::list&
paramIndex, paramSetSize);
unsigned char* dataArray =
AllocateParamBufferArray<unsigned char>(tempBuffers, paramSetSize);
strLenOrIndArray = AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
for (size_t i = 0; i < paramSetSize; ++i) {
if (columnValues[i].is_none()) {
if (!strLenOrIndArray)
strLenOrIndArray =
AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
dataArray[i] = 0;
strLenOrIndArray[i] = SQL_NULL_DATA;
} else {
Expand All @@ -2333,8 +2325,7 @@ SQLRETURN BindParameterArray(SqlHandle& handle, SQLHANDLE hStmt, const py::list&
std::to_string(i));
}
dataArray[i] = static_cast<unsigned char>(intVal);
if (strLenOrIndArray)
strLenOrIndArray[i] = 0;
strLenOrIndArray[i] = 0;
}
}
LOG("BindParameterArray: SQL_C_TINYINT bound - "
Expand All @@ -2349,11 +2340,9 @@ SQLRETURN BindParameterArray(SqlHandle& handle, SQLHANDLE hStmt, const py::list&
"param_index=%d, count=%zu",
paramIndex, paramSetSize);
short* dataArray = AllocateParamBufferArray<short>(tempBuffers, paramSetSize);
strLenOrIndArray = AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
for (size_t i = 0; i < paramSetSize; ++i) {
if (columnValues[i].is_none()) {
if (!strLenOrIndArray)
strLenOrIndArray =
AllocateParamBufferArray<SQLLEN>(tempBuffers, paramSetSize);
dataArray[i] = 0;
strLenOrIndArray[i] = SQL_NULL_DATA;
} else {
Expand All @@ -2367,8 +2356,7 @@ SQLRETURN BindParameterArray(SqlHandle& handle, SQLHANDLE hStmt, const py::list&
std::to_string(i));
}
dataArray[i] = static_cast<short>(intVal);
if (strLenOrIndArray)
strLenOrIndArray[i] = 0;
strLenOrIndArray[i] = 0;
}
}
LOG("BindParameterArray: SQL_C_SHORT bound - "
Expand Down
36 changes: 36 additions & 0 deletions tests/test_004_cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,42 @@ def test_executemany_ints_with_none(cursor, db_connection):
db_connection.commit()


def test_executemany_numeric_types_with_late_none(cursor, db_connection):
"""Test fixed-width numeric array indicators when NULL follows non-NULL values."""
try:
cursor.execute("""CREATE TABLE #pytest_numeric_late_none (
id INT NOT NULL,
tinyint_val TINYINT NULL,
smallint_val SMALLINT NULL,
int_val INT NULL,
float_val FLOAT NULL
)""")
null_rows = {80, 83}
data = [
(
row_id,
None if row_id in null_rows else row_id,
None if row_id in null_rows else 1000 + row_id,
None if row_id in null_rows else 100000 + row_id,
None if row_id in null_rows else 100000.5 + row_id,
)
for row_id in range(86)
]

for _ in range(10):
cursor.execute("TRUNCATE TABLE #pytest_numeric_late_none")
cursor.executemany("INSERT INTO #pytest_numeric_late_none VALUES (?, ?, ?, ?, ?)", data)
assert cursor.rowcount == len(data)
db_connection.commit()

cursor.execute("""SELECT id, tinyint_val, smallint_val, int_val, float_val
FROM #pytest_numeric_late_none ORDER BY id""")
assert [tuple(row) for row in cursor.fetchall()] == list(data)
finally:
cursor.execute("DROP TABLE IF EXISTS #pytest_numeric_late_none")
db_connection.commit()


def test_executemany_strings_of_various_lengths(cursor, db_connection):
"""Test executemany with strings of different lengths."""
try:
Expand Down
Loading