From 3f6c31cd51bf0caa4830d2545bc5b03a69705f3d Mon Sep 17 00:00:00 2001 From: Evert Lammerts Date: Thu, 26 Feb 2026 18:20:51 +0100 Subject: [PATCH] fix polars segfault and pin submodule --- external/duckdb | 2 +- src/duckdb_py/pyresult.cpp | 21 ++++++--------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/external/duckdb b/external/duckdb index 4213f2ba..461ef9e3 160000 --- a/external/duckdb +++ b/external/duckdb @@ -1 +1 @@ -Subproject commit 4213f2bae35fd8790c0d0551c4d24246a039a0c8 +Subproject commit 461ef9e350c2599d77df623a6cb9b3aa84213a25 diff --git a/src/duckdb_py/pyresult.cpp b/src/duckdb_py/pyresult.cpp index b4ed691b..34b5f6ff 100644 --- a/src/duckdb_py/pyresult.cpp +++ b/src/duckdb_py/pyresult.cpp @@ -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(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);