Skip to content

Commit 00147d6

Browse files
authored
Merge branch 'saumya/uuid' into saumya/uuid-executemany
2 parents d339b77 + d207f7a commit 00147d6

File tree

5 files changed

+258
-128
lines changed

5 files changed

+258
-128
lines changed

PyPI_Description.md

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,15 @@ PyBind11 provides:
3939

4040
We are currently in **Public Preview**.
4141

42-
## What's new in v0.10.0
43-
44-
- **SUSE Linux Support:** Added full support for SUSE and openSUSE distributions alongside existing other Linux distros support, broadening enterprise Linux compatibility.
45-
- **Context Manager Support:** Implemented Python `with` statement support for Connection and Cursor classes with automatic transaction management and resource cleanup.
46-
- **Large Text Streaming:** Added Data At Execution (DAE) support for streaming large text parameters (`NVARCHAR(MAX)`, `VARCHAR(MAX)`), eliminating memory constraints for bulk text `execute()` operations.
47-
- `VARBINARY(MAX)` support to follow alongwith streaming support for fetch operations.
48-
- **Enhanced Unicode Handling:** Improved emoji and international character support with robust UTF-16 encoding for reliable multilingual data processing.
49-
- **PyODBC Compatibility:** Enhanced API compatibility with pyodbc including:
50-
- DB-API 2.0 exception classes: `Warning`, `Error`, `InterfaceError`, `DatabaseError`, `DataError`, `OperationalError`, `IntegrityError`, `InternalError`, `ProgrammingError`, `NotSupportedError`
51-
- Context manager support with `with` statements for Connection and Cursor
52-
- Encoding configuration APIs: `setencoding()`, `getencoding()`, `setdecoding()`, `getdecoding()`
53-
- Cursor navigation APIs: `next()`, `__iter__()`, `scroll()`, `skip()`, `fetchval()`
54-
- Cursor attributes: `rownumber`, `messages`
55-
- Additional methods: `cursor.commit()`, `cursor.rollback()`, `table()`
42+
## What's new in v0.11.0
43+
44+
- **Database Metadata & Introspection:** Added comprehensive `getInfo()` method and extensive catalog APIs (`SQLGetTypeInfo`, `SQLProcedures`, `SQLForeignKeys`, `SQLColumns`) for complete database schema discovery and introspection capabilities.
45+
- **Advanced Parameter Management:** Implemented `setinputsizes()` with SQL type constants and enhanced parameter validation through the `SQLTypes` class for precise type control in parameterized queries.
46+
- **Large Data Streaming Enhancements:** Extended streaming support to VARBINARY(MAX) and VARCHAR(MAX) across all fetch operations (`fetchone`, `fetchmany`, `fetchall`) with improved chunked retrieval for efficient memory usage.
47+
- **Output Data Conversion System:** Introduced flexible output converter framework with `add_output_converter()`, `remove_output_converter()`, and related methods for customizable data transformations during result fetching.
48+
- **Connection-Level Execute:** Added direct `execute()` method to Connection class for streamlined query execution without explicit cursor management.
49+
- **Financial Data Type Support:** Comprehensive support for SQL Server MONEY and SMALLMONEY types with proper boundary value handling and decimal precision.
50+
- **Enhanced Configuration APIs:** Added connection timeout control, decimal separator configuration (`getDecimalSeperator()`, `setDecimalSeperator()`), and improved global variable management.
5651

5752
For more information, please visit the project link on Github: https://github.com/microsoft/mssql-python
5853

mssql_python/cursor.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,16 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None):
338338
parameters_list[i].scale,
339339
False,
340340
)
341+
342+
if isinstance(param, uuid.UUID):
343+
parameters_list[i] = param.bytes_le
344+
return (
345+
ddbc_sql_const.SQL_GUID.value,
346+
ddbc_sql_const.SQL_C_GUID.value,
347+
16,
348+
0,
349+
False,
350+
)
341351

342352
if isinstance(param, str):
343353
if (
@@ -352,6 +362,20 @@ def _map_sql_type(self, param, parameters_list, i, min_val=None, max_val=None):
352362
0,
353363
False,
354364
)
365+
366+
try:
367+
val = uuid.UUID(param)
368+
parameters_list[i] = val.bytes_le
369+
return (
370+
ddbc_sql_const.SQL_GUID.value,
371+
ddbc_sql_const.SQL_C_GUID.value,
372+
16,
373+
0,
374+
False
375+
)
376+
except ValueError:
377+
pass
378+
355379

356380
# Attempt to parse as date, datetime, datetime2, timestamp, smalldatetime or time
357381
if self._parse_date(param):

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -510,22 +510,21 @@ SQLRETURN BindParameters(SQLHANDLE hStmt, const py::list& params,
510510
py::bytes uuid_bytes = param.cast<py::bytes>();
511511
const unsigned char* uuid_data = reinterpret_cast<const unsigned char*>(PyBytes_AS_STRING(uuid_bytes.ptr()));
512512
if (PyBytes_GET_SIZE(uuid_bytes.ptr()) != 16) {
513+
LOG("Invalid UUID parameter at index {}: expected 16 bytes, got {} bytes, type {}", paramIndex, PyBytes_GET_SIZE(uuid_bytes.ptr()), paramInfo.paramCType);
513514
ThrowStdException("UUID binary data must be exactly 16 bytes long.");
514515
}
515516
SQLGUID* guid_data_ptr = AllocateParamBuffer<SQLGUID>(paramBuffers);
516-
517-
guid_data_ptr->Data1 =
518-
(static_cast<uint32_t>(uuid_data[3]) << 24) |
519-
(static_cast<uint32_t>(uuid_data[2]) << 16) |
520-
(static_cast<uint32_t>(uuid_data[1]) << 8) |
517+
guid_data_ptr->Data1 =
518+
(static_cast<uint32_t>(uuid_data[3]) << 24) |
519+
(static_cast<uint32_t>(uuid_data[2]) << 16) |
520+
(static_cast<uint32_t>(uuid_data[1]) << 8) |
521521
(static_cast<uint32_t>(uuid_data[0]));
522-
guid_data_ptr->Data2 =
523-
(static_cast<uint16_t>(uuid_data[5]) << 8) |
522+
guid_data_ptr->Data2 =
523+
(static_cast<uint16_t>(uuid_data[5]) << 8) |
524524
(static_cast<uint16_t>(uuid_data[4]));
525-
guid_data_ptr->Data3 =
526-
(static_cast<uint16_t>(uuid_data[7]) << 8) |
525+
guid_data_ptr->Data3 =
526+
(static_cast<uint16_t>(uuid_data[7]) << 8) |
527527
(static_cast<uint16_t>(uuid_data[6]));
528-
529528
std::memcpy(guid_data_ptr->Data4, &uuid_data[8], 8);
530529
dataPtr = static_cast<void*>(guid_data_ptr);
531530
bufferLength = sizeof(SQLGUID);
@@ -2635,7 +2634,7 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
26352634

26362635
py::bytes py_guid_bytes(guid_bytes.data(), guid_bytes.size());
26372636
py::object uuid_module = py::module_::import("uuid");
2638-
py::object uuid_obj = uuid_module.attr("UUID")(py_guid_bytes);
2637+
py::object uuid_obj = uuid_module.attr("UUID")(py::arg("bytes")=py_guid_bytes);
26392638
row.append(uuid_obj);
26402639
} else if (indicator == SQL_NULL_DATA) {
26412640
row.append(py::none());
@@ -3030,11 +3029,18 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
30303029
}
30313030
case SQL_GUID: {
30323031
SQLGUID* guidValue = &buffers.guidBuffers[col - 1][i];
3033-
std::vector<char> guid_bytes(16);
3034-
std::memcpy(guid_bytes.data(), guidValue, sizeof(SQLGUID));
3035-
3036-
// Convert the raw C++ byte vector to a Python bytes object
3037-
py::bytes py_guid_bytes(guid_bytes.data(), guid_bytes.size());
3032+
uint8_t reordered[16];
3033+
reordered[0] = ((char*)&guidValue->Data1)[3];
3034+
reordered[1] = ((char*)&guidValue->Data1)[2];
3035+
reordered[2] = ((char*)&guidValue->Data1)[1];
3036+
reordered[3] = ((char*)&guidValue->Data1)[0];
3037+
reordered[4] = ((char*)&guidValue->Data2)[1];
3038+
reordered[5] = ((char*)&guidValue->Data2)[0];
3039+
reordered[6] = ((char*)&guidValue->Data3)[1];
3040+
reordered[7] = ((char*)&guidValue->Data3)[0];
3041+
std::memcpy(reordered + 8, guidValue->Data4, 8);
3042+
3043+
py::bytes py_guid_bytes(reinterpret_cast<char*>(reordered), 16);
30383044
py::dict kwargs;
30393045
kwargs["bytes"] = py_guid_bytes;
30403046
py::object uuid_obj = py::module_::import("uuid").attr("UUID")(**kwargs);

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def finalize_options(self):
8383

8484
setup(
8585
name='mssql-python',
86-
version='0.10.0',
86+
version='0.11.0',
8787
description='A Python library for interacting with Microsoft SQL Server',
8888
long_description=open('PyPI_Description.md', encoding='utf-8').read(),
8989
long_description_content_type='text/markdown',

0 commit comments

Comments
 (0)