Skip to content

Commit c03b72b

Browse files
committed
cleanup
1 parent e9aa833 commit c03b72b

File tree

3 files changed

+112
-22
lines changed

3 files changed

+112
-22
lines changed

mssql_python/cursor.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -774,8 +774,6 @@ def execute(
774774

775775
if parameters:
776776
for i, param in enumerate(parameters):
777-
if isinstance(param, uuid.UUID):
778-
parameters[i] = param.bytes
779777
paraminfo = self._create_parameter_types_list(
780778
param, param_info, parameters, i
781779
)

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -484,29 +484,27 @@ SQLRETURN BindParameters(SQLHANDLE hStmt, const py::list& params,
484484
break;
485485
}
486486
case SQL_C_GUID: {
487-
if (!py::isinstance<py::bytes>(param)) {
487+
py::object uuid_obj = param;
488+
py::object uuid_cls = py::module_::import("uuid").attr("UUID");
489+
if (!py::isinstance(uuid_obj, uuid_cls)) {
488490
ThrowStdException(MakeParamMismatchErrorStr(paramInfo.paramCType, paramIndex));
489491
}
490-
py::bytes uuid_bytes = param.cast<py::bytes>();
491-
const unsigned char* uuid_data = reinterpret_cast<const unsigned char*>(PyBytes_AS_STRING(uuid_bytes.ptr()));
492+
py::bytes uuid_bytes = uuid_obj.attr("bytes").cast<py::bytes>();
492493
if (PyBytes_GET_SIZE(uuid_bytes.ptr()) != 16) {
493494
ThrowStdException("UUID binary data must be exactly 16 bytes long.");
494495
}
496+
const unsigned char* b = reinterpret_cast<const unsigned char*>(PyBytes_AS_STRING(uuid_bytes.ptr()));
495497
SQLGUID* guid_data_ptr = AllocateParamBuffer<SQLGUID>(paramBuffers);
498+
guid_data_ptr->Data1 = (static_cast<uint32_t>(b[3]) << 24) |
499+
(static_cast<uint32_t>(b[2]) << 16) |
500+
(static_cast<uint32_t>(b[1]) << 8) |
501+
static_cast<uint32_t>(b[0]);
502+
guid_data_ptr->Data2 = (static_cast<uint16_t>(b[5]) << 8) |
503+
static_cast<uint16_t>(b[4]);
504+
guid_data_ptr->Data3 = (static_cast<uint16_t>(b[7]) << 8) |
505+
static_cast<uint16_t>(b[6]);
506+
std::memcpy(guid_data_ptr->Data4, &b[8], 8);
496507

497-
guid_data_ptr->Data1 =
498-
(static_cast<uint32_t>(uuid_data[3]) << 24) |
499-
(static_cast<uint32_t>(uuid_data[2]) << 16) |
500-
(static_cast<uint32_t>(uuid_data[1]) << 8) |
501-
(static_cast<uint32_t>(uuid_data[0]));
502-
guid_data_ptr->Data2 =
503-
(static_cast<uint16_t>(uuid_data[5]) << 8) |
504-
(static_cast<uint16_t>(uuid_data[4]));
505-
guid_data_ptr->Data3 =
506-
(static_cast<uint16_t>(uuid_data[7]) << 8) |
507-
(static_cast<uint16_t>(uuid_data[6]));
508-
509-
std::memcpy(guid_data_ptr->Data4, &uuid_data[8], 8);
510508
dataPtr = static_cast<void*>(guid_data_ptr);
511509
bufferLength = sizeof(SQLGUID);
512510
strLenOrIndPtr = AllocateParamBuffer<SQLLEN>(paramBuffers);
@@ -2242,8 +2240,7 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
22422240
std::memcpy(&guid_bytes[8], guidValue.Data4, sizeof(guidValue.Data4));
22432241

22442242
py::bytes py_guid_bytes(guid_bytes.data(), guid_bytes.size());
2245-
py::object uuid_module = py::module_::import("uuid");
2246-
py::object uuid_obj = uuid_module.attr("UUID")(py_guid_bytes);
2243+
py::object uuid_obj = py::module_::import("uuid").attr("UUID")(py::arg("bytes") = py_guid_bytes);
22472244
row.append(uuid_obj);
22482245
} else if (indicator == SQL_NULL_DATA) {
22492246
row.append(py::none());
@@ -2255,7 +2252,7 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
22552252
}
22562253
break;
22572254
}
2258-
#endif
2255+
#endif
22592256
default:
22602257
std::ostringstream errorString;
22612258
errorString << "Unsupported data type for column - " << columnName << ", Type - "
@@ -2628,7 +2625,6 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
26282625
std::vector<char> guid_bytes(16);
26292626
std::memcpy(guid_bytes.data(), guidValue, sizeof(SQLGUID));
26302627

2631-
// Convert the raw C++ byte vector to a Python bytes object
26322628
py::bytes py_guid_bytes(guid_bytes.data(), guid_bytes.size());
26332629
py::dict kwargs;
26342630
kwargs["bytes"] = py_guid_bytes;

tests/test_004_cursor.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6921,6 +6921,102 @@ def test_uuid_insert_with_none(cursor, db_connection):
69216921
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
69226922
db_connection.commit()
69236923

6924+
6925+
def test_uuid_insert_and_select_none(cursor, db_connection):
6926+
"""Test inserting and retrieving None in a nullable UUID column."""
6927+
table_name = "#pytest_uuid_nullable"
6928+
try:
6929+
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
6930+
cursor.execute(f"""
6931+
CREATE TABLE {table_name} (
6932+
id UNIQUEIDENTIFIER,
6933+
name NVARCHAR(50)
6934+
)
6935+
""")
6936+
db_connection.commit()
6937+
6938+
# Insert a row with None for the UUID
6939+
cursor.execute(f"INSERT INTO {table_name} (id, name) VALUES (?, ?)", [None, "Bob"])
6940+
db_connection.commit()
6941+
6942+
# Fetch the row
6943+
cursor.execute(f"SELECT id, name FROM {table_name}")
6944+
retrieved_uuid, retrieved_name = cursor.fetchone()
6945+
6946+
# Assert that the retrieved UUID is None
6947+
assert retrieved_uuid is None, f"Expected None, got {type(retrieved_uuid)}"
6948+
assert retrieved_name == "Bob"
6949+
finally:
6950+
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
6951+
db_connection.commit()
6952+
6953+
6954+
def test_insert_multiple_uuids(cursor, db_connection):
6955+
"""Test inserting multiple UUIDs and verifying retrieval."""
6956+
table_name = "#pytest_uuid_multiple"
6957+
try:
6958+
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
6959+
cursor.execute(f"""
6960+
CREATE TABLE {table_name} (
6961+
id UNIQUEIDENTIFIER PRIMARY KEY,
6962+
description NVARCHAR(50)
6963+
)
6964+
""")
6965+
db_connection.commit()
6966+
6967+
uuids_to_insert = {f"Item {i}": uuid.uuid4() for i in range(5)}
6968+
6969+
# Insert UUIDs and descriptions directly
6970+
for desc, uid in uuids_to_insert.items():
6971+
cursor.execute(f"INSERT INTO {table_name} (id, description) VALUES (?, ?)", [uid, desc])
6972+
db_connection.commit()
6973+
6974+
# Fetch all data
6975+
cursor.execute(f"SELECT id, description FROM {table_name}")
6976+
rows = cursor.fetchall()
6977+
6978+
# Verify each fetched row against the original data
6979+
assert len(rows) == len(uuids_to_insert), "Number of fetched rows does not match."
6980+
6981+
for retrieved_uuid, retrieved_desc in rows:
6982+
# Assert type is correct
6983+
assert isinstance(retrieved_uuid, uuid.UUID), f"Expected uuid.UUID, got {type(retrieved_uuid)}"
6984+
6985+
# Use the description to look up the original UUID
6986+
expected_uuid = uuids_to_insert.get(retrieved_desc)
6987+
6988+
assert expected_uuid is not None, f"Retrieved description '{retrieved_desc}' was not in the original data."
6989+
assert retrieved_uuid == expected_uuid, f"UUID mismatch for '{retrieved_desc}': expected {expected_uuid}, got {retrieved_uuid}"
6990+
finally:
6991+
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
6992+
db_connection.commit()
6993+
6994+
6995+
def test_uuid_insert_with_none(cursor, db_connection):
6996+
"""Test inserting None into a UUID column results in a NULL value."""
6997+
table_name = "#pytest_uuid_none"
6998+
try:
6999+
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
7000+
cursor.execute(f"""
7001+
CREATE TABLE {table_name} (
7002+
id UNIQUEIDENTIFIER,
7003+
name NVARCHAR(50)
7004+
)
7005+
""")
7006+
db_connection.commit()
7007+
7008+
cursor.execute(f"INSERT INTO {table_name} (id, name) VALUES (?, ?)", [None, "Bob"])
7009+
db_connection.commit()
7010+
7011+
cursor.execute(f"SELECT id, name FROM {table_name}")
7012+
retrieved_uuid, retrieved_name = cursor.fetchone()
7013+
7014+
assert retrieved_uuid is None, f"Expected NULL UUID, got {retrieved_uuid}"
7015+
assert retrieved_name == "Bob"
7016+
finally:
7017+
cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
7018+
db_connection.commit()
7019+
69247020
def test_close(db_connection):
69257021
"""Test closing the cursor"""
69267022
try:

0 commit comments

Comments
 (0)