@@ -315,11 +315,62 @@ query_result * connection_wrapper::query(const std::string & query_str, const st
315315 }
316316 if (result->error_message )
317317 {
318- throw std::runtime_error (result->error_message );
318+ std::string msg_copy (result->error_message );
319+ free_result_v2 (result);
320+ throw std::runtime_error (msg_copy);
319321 }
320322 return new query_result (result, false );
321323}
322324
325+ streaming_query_result * connection_wrapper::send_query (const std::string & query_str, const std::string & format)
326+ {
327+ global_query_obj = findQueryableObjFromQuery (query_str);
328+
329+ py::gil_scoped_release release;
330+ auto * result = query_conn_streaming (*conn, query_str.c_str (), format.c_str ());
331+ const auto * error_msg = chdb_streaming_result_error (result);
332+ if (error_msg)
333+ {
334+ std::string msg_copy (error_msg);
335+ chdb_destroy_result (result);
336+ throw std::runtime_error (msg_copy);
337+ }
338+
339+ return new streaming_query_result (result);
340+ }
341+
342+ query_result * connection_wrapper::streaming_fetch_result (streaming_query_result * streaming_result)
343+ {
344+ py::gil_scoped_release release;
345+
346+ if (!streaming_result || !streaming_result->get_result ())
347+ return nullptr ;
348+
349+ auto * result = chdb_streaming_fetch_result (*conn, streaming_result->get_result ());
350+
351+ if (result->len == 0 )
352+ LOG_DEBUG (getLogger (" CHDB" ), " Empty result returned for streaming query" );
353+
354+ if (result->error_message )
355+ {
356+ std::string msg_copy (result->error_message );
357+ free_result_v2 (result);
358+ throw std::runtime_error (msg_copy);
359+ }
360+
361+ return new query_result (result, false );
362+ }
363+
364+ void connection_wrapper::streaming_cancel_query (streaming_query_result * streaming_result)
365+ {
366+ py::gil_scoped_release release;
367+
368+ if (!streaming_result || !streaming_result->get_result ())
369+ return ;
370+
371+ chdb_streaming_cancel_query (*conn, streaming_result->get_result ());
372+ }
373+
323374void cursor_wrapper::execute (const std::string & query_str)
324375{
325376 release_result ();
@@ -407,6 +458,11 @@ PYBIND11_MODULE(_chdb, m)
407458 .def (" has_error" , &query_result::has_error)
408459 .def (" error_message" , &query_result::error_message);
409460
461+ py::class_<streaming_query_result>(m, " streaming_query_result" )
462+ .def (py::init<chdb_streaming_result *>(), py::return_value_policy::take_ownership)
463+ .def (" has_error" , &streaming_query_result::has_error)
464+ .def (" error_message" , &streaming_query_result::error_message);
465+
410466 py::class_<DB::PyReader, std::shared_ptr<DB::PyReader>>(m, " PyReader" )
411467 .def (
412468 py::init<const py::object &>(),
@@ -460,7 +516,23 @@ PYBIND11_MODULE(_chdb, m)
460516 &connection_wrapper::query,
461517 py::arg (" query_str" ),
462518 py::arg (" format" ) = " CSV" ,
463- " Execute a query and return a query_result object" );
519+ " Execute a query and return a query_result object" )
520+ .def (
521+ " send_query" ,
522+ &connection_wrapper::send_query,
523+ py::arg (" query_str" ),
524+ py::arg (" format" ) = " CSV" ,
525+ " Send a streaming query and return a streaming query result object" )
526+ .def (
527+ " streaming_fetch_result" ,
528+ &connection_wrapper::streaming_fetch_result,
529+ py::arg (" streaming_result" ),
530+ " Fetches a data chunk from the streaming result. This function should be called repeatedly until the result is exhausted" )
531+ .def (
532+ " streaming_cancel_query" ,
533+ &connection_wrapper::streaming_cancel_query,
534+ py::arg (" streaming_result" ),
535+ " Cancel a streaming query" );
464536
465537 m.def (
466538 " query" ,
0 commit comments