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
2 changes: 1 addition & 1 deletion external/duckdb
Submodule duckdb updated 106 files
21 changes: 6 additions & 15 deletions src/duckdb_py/pyresult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,26 +583,17 @@ static void ArrowArrayStreamPyCapsuleDestructor(PyObject *object) {
delete stream;
}

// Destructor for capsules pointing at an embedded ArrowArrayStream (fast path).
// The stream is owned by an ArrowQueryResultStreamWrapper; Release() frees both.
static void ArrowArrayStreamEmbeddedPyCapsuleDestructor(PyObject *object) {
auto data = PyCapsule_GetPointer(object, "arrow_array_stream");
if (!data) {
return;
}
auto stream = reinterpret_cast<ArrowArrayStream *>(data);
if (stream->release) {
stream->release(stream);
}
}

py::object DuckDBPyResult::FetchArrowCapsule(idx_t rows_per_batch) {
if (result && result->type == QueryResultType::ARROW_RESULT) {
// Fast path: yield pre-built Arrow arrays directly.
// The wrapper is heap-allocated; Release() deletes it via private_data.
// The capsule points at the embedded stream field — no separate heap allocation needed.
// We heap-allocate a separate ArrowArrayStream for the capsule so that the capsule
// holds a stable pointer even after the wrapper is consumed and deleted by a scan.
auto wrapper = new ArrowQueryResultStreamWrapper(std::move(result));
return py::capsule(&wrapper->stream, "arrow_array_stream", ArrowArrayStreamEmbeddedPyCapsuleDestructor);
auto stream = new ArrowArrayStream();
*stream = wrapper->stream;
wrapper->stream.release = nullptr;
return py::capsule(stream, "arrow_array_stream", ArrowArrayStreamPyCapsuleDestructor);
}
// Existing slow path for MaterializedQueryResult / StreamQueryResult
auto stream_p = FetchArrowArrayStream(rows_per_batch);
Expand Down
Loading