@@ -511,6 +511,29 @@ void EnsureNativeFetchBufferSize(std::vector<ElementType>& buffer, size_t requir
511511 ResizeNativeFetchBuffer (buffer, newSize, reservedBytes);
512512}
513513
514+ size_t ExtendNativeFetchBuffer (std::vector<char >& buffer, size_t currentSize, size_t appendSize,
515+ size_t & reservedBytes) {
516+ const size_t requiredSize =
517+ CheckedAddSize (currentSize, appendSize, " Native fetch buffer size is too large" );
518+ EnsureNativeFetchBufferSize (buffer, requiredSize, reservedBytes);
519+ return requiredSize;
520+ }
521+
522+ py::tuple TestLobBufferGrowth (size_t chunkCount, size_t chunkSize) {
523+ std::vector<char > buffer;
524+ size_t dataSize = 0 ;
525+ size_t reservedBytes = 0 ;
526+ size_t growthCount = 0 ;
527+ for (size_t chunk = 0 ; chunk < chunkCount; ++chunk) {
528+ const size_t previousAllocation = buffer.size ();
529+ dataSize = ExtendNativeFetchBuffer (buffer, dataSize, chunkSize, reservedBytes);
530+ if (buffer.size () != previousAllocation) {
531+ ++growthCount;
532+ }
533+ }
534+ return py::make_tuple (growthCount, dataSize, buffer.size (), reservedBytes);
535+ }
536+
514537template <typename ElementType>
515538std::unique_ptr<ElementType[]> AllocateArrowArray (size_t count, size_t & reservedBytes,
516539 const char * errorMessage) {
@@ -2396,6 +2419,7 @@ SQLRETURN SQLExecute_wrap(const SqlHandlePtr statementHandle,
23962419 bool use_prepare,
23972420 const py::dict& encoding_settings) {
23982421 PERF_TIMER (" SQLExecute_wrap" );
2422+ ValidateInputSizes (input_sizes.ptr ());
23992423 if (!statementHandle || !statementHandle->get ()) {
24002424 return SQL_INVALID_HANDLE ;
24012425 }
@@ -3218,9 +3242,14 @@ SQLRETURN BindParameterArray(SqlHandle& handle, SQLHANDLE hStmt, const py::list&
32183242
32193243SQLRETURN SQLExecuteMany_wrap (const SqlHandlePtr statementHandle, const std::u16string& query,
32203244 const py::list& columnwise_params,
3221- std::vector<ParamInfo>& paramInfos, size_t paramSetSize ,
3245+ std::vector<ParamInfo>& paramInfos, py::handle paramSetSizeArg ,
32223246 const py::dict& encodingSettings) {
32233247 PERF_TIMER (" SQLExecuteMany_wrap" );
3248+ const size_t paramSetSize = static_cast <size_t >(
3249+ ValidateNativeRowCountArgument (paramSetSizeArg, " Parameter set size" , false ));
3250+ if (!statementHandle || !statementHandle->get ()) {
3251+ return SQL_INVALID_HANDLE ;
3252+ }
32243253 statementHandle->resultMetadata .clear ();
32253254 LOG (" SQLExecuteMany: Starting batch execution - param_count=%zu, "
32263255 " param_set_size=%zu" ,
@@ -3552,6 +3581,7 @@ py::object FetchLobColumnData(SQLHSTMT hStmt, SQLUSMALLINT colIndex, SQLSMALLINT
35523581 py::handle messages) {
35533582 PERF_TIMER (" FetchLobColumnData" );
35543583 std::vector<char > buffer;
3584+ size_t dataSize = 0 ;
35553585 size_t reservedBytes = 0 ;
35563586 ReserveNativeFetchBytes (reservedBytes, DAE_CHUNK_SIZE , sizeof (char ));
35573587 SQLRETURN ret = SQL_SUCCESS_WITH_INFO ;
@@ -3623,10 +3653,8 @@ py::object FetchLobColumnData(SQLHSTMT hStmt, SQLUSMALLINT colIndex, SQLSMALLINT
36233653 }
36243654 }
36253655 if (bytesRead > 0 ) {
3626- const size_t previousSize = buffer.size ();
3627- const size_t requiredSize =
3628- CheckedAddSize (previousSize, bytesRead, " LOB fetch buffer is too large" );
3629- ResizeNativeFetchBuffer (buffer, requiredSize, reservedBytes);
3656+ const size_t previousSize = dataSize;
3657+ dataSize = ExtendNativeFetchBuffer (buffer, dataSize, bytesRead, reservedBytes);
36303658 std::copy_n (chunk.data (), bytesRead, buffer.data () + previousSize);
36313659 LOG (" FetchLobColumnData: Appended %zu bytes at loop %d" , bytesRead, loopCount);
36323660 }
@@ -3635,21 +3663,21 @@ py::object FetchLobColumnData(SQLHSTMT hStmt, SQLUSMALLINT colIndex, SQLSMALLINT
36353663 break ;
36363664 }
36373665 }
3638- LOG (" FetchLobColumnData: Total bytes collected=%zu for column %d" , buffer. size () , colIndex);
3666+ LOG (" FetchLobColumnData: Total bytes collected=%zu for column %d" , dataSize , colIndex);
36393667
3640- if (buffer. empty () ) {
3668+ if (dataSize == 0 ) {
36413669 if (isBinary) {
36423670 return py::bytes (" " );
36433671 }
36443672 return py::str (" " );
36453673 }
36463674 if (isWideChar) {
3647- if (buffer. size () % sizeof (SQLWCHAR ) != 0 ) {
3675+ if (dataSize % sizeof (SQLWCHAR ) != 0 ) {
36483676 ThrowStdException (" Wide-character LOB data has an invalid byte length" );
36493677 }
36503678 int byteOrder = -1 ;
36513679 PyObject* decoded =
3652- PyUnicode_DecodeUTF16 (buffer.data (), static_cast <Py_ssize_t>(buffer. size () ),
3680+ PyUnicode_DecodeUTF16 (buffer.data (), static_cast <Py_ssize_t>(dataSize ),
36533681 " strict" , &byteOrder);
36543682 if (decoded == nullptr ) {
36553683 throw py::error_already_set ();
@@ -3659,18 +3687,18 @@ py::object FetchLobColumnData(SQLHSTMT hStmt, SQLUSMALLINT colIndex, SQLSMALLINT
36593687 if (isBinary) {
36603688 LOG (" FetchLobColumnData: Returning binary data - %zu bytes for column "
36613689 " %d" ,
3662- buffer. size () , colIndex);
3663- return py::bytes (buffer.data (), buffer. size () );
3690+ dataSize , colIndex);
3691+ return py::bytes (buffer.data (), dataSize );
36643692 }
36653693
36663694 // For SQL_C_CHAR data, decode using the appropriate encoding.
36673695 const std::string effectiveCharEncoding = GetEffectiveCharDecoding (charEncoding);
3668- py::bytes raw_bytes (buffer.data (), buffer. size () );
3696+ py::bytes raw_bytes (buffer.data (), dataSize );
36693697 try {
36703698 py::object decoded = raw_bytes.attr (" decode" )(effectiveCharEncoding, " strict" );
36713699 LOG (" FetchLobColumnData: Decoded narrow string with '%s' - %zu bytes -> %zu chars for "
36723700 " column %d" ,
3673- effectiveCharEncoding.c_str (), buffer. size () , py::len (decoded), colIndex);
3701+ effectiveCharEncoding.c_str (), dataSize , py::len (decoded), colIndex);
36743702 return decoded;
36753703 } catch (const py::error_already_set& e) {
36763704 LOG_ERROR (" FetchLobColumnData: Failed to decode with '%s' for column %d: %s" ,
@@ -7000,6 +7028,8 @@ PYBIND11_MODULE(ddbc_bindings, m) {
70007028 " Fetch an arrow batch of given length from the result set" , py::arg (" StatementHandle" ),
70017029 py::arg (" capsules" ), py::arg (" arrowBatchSize" ), py::arg (" charCtype" ),
70027030 py::arg (" messages" ) = py::none ());
7031+ m.def (" _test_lob_buffer_growth" , &TestLobBufferGrowth, py::arg (" chunk_count" ),
7032+ py::arg (" chunk_size" ));
70037033 m.def (" DDBCSQLFreeHandle" , &SQLFreeHandle_wrap, " Free a handle" );
70047034 m.def (" DDBCSQLResetStmt" , &SQLResetStmt_wrap,
70057035 " Close cursor and unbind params without freeing HSTMT" );
0 commit comments