Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c3628c9
Link MSVC runtime library statically
staticlibs Oct 17, 2025
3239e22
Bump submodule
duckdblabs-bot Dec 30, 2025
c9fb976
Fix select_dtypes with quoted identifiers
Schwarf Dec 31, 2025
0ed560a
Add test to verify bug is fixed.
Schwarf Jan 1, 2026
1440722
ci: trigger
Schwarf Jan 1, 2026
75181a2
Add new line at EOF.
Schwarf Jan 1, 2026
b09e8cd
[duckdb-labs bot] Bump DuckDB submodule (#240)
evertlammerts Jan 1, 2026
7acec08
Use py::object (reinterpret_steal) for PyObject_CallObject return val…
Schwarf Jan 3, 2026
cc274da
Add regression test for Python UDF return value refcount leak.
Schwarf Jan 3, 2026
f97248f
Fix formatting.
Schwarf Jan 3, 2026
f94dc3a
Fix adbc tests
evertlammerts Jan 5, 2026
087dd1d
Fix adbc tests (#248)
evertlammerts Jan 5, 2026
b690066
Merge branch 'main' into fix/select-dtypes-identifier-quoting
evertlammerts Jan 5, 2026
53fc11c
Exclude jemalloc from anything apart from debug builds on osx and linux
evertlammerts Jan 5, 2026
73fa4d4
Merge branch 'main' into fix/python-udf-refcount-leak
evertlammerts Jan 5, 2026
ff32a8a
Fix review finding. Use KeywordHelper::WriteOptionallyQuoted
Schwarf Jan 5, 2026
46286c1
Exclude jemalloc from anything apart from debug builds on osx and linux
evertlammerts Jan 5, 2026
0dd3514
Fix select_dtypes for quoted column identifiers (#242)
evertlammerts Jan 5, 2026
625e2f8
Fix jemalloc cmake filter (#249)
evertlammerts Jan 5, 2026
dbb9c15
Merge branch 'main' into fix/python-udf-refcount-leak
evertlammerts Jan 5, 2026
901d1ea
remove jemalloc extension
evertlammerts Jan 6, 2026
3649e17
remove jemalloc extension (#250)
evertlammerts Jan 6, 2026
e8ee9d3
Merge branch 'main' into msvc_static
evertlammerts Jan 6, 2026
41ae2b5
Link MSVC runtime library statically (#130)
evertlammerts Jan 6, 2026
c1be81a
enable jemalloc but only on linux 64bit, aligned with duckdb core
evertlammerts Jan 6, 2026
af8f38d
Bumped submodule
evertlammerts Jan 6, 2026
14a86f5
fixed linux jemalloc check
evertlammerts Jan 6, 2026
b845b35
Fix : aggregate method typing to accept list of expressions
Narwhal-fish Jan 5, 2026
6d6f2fb
Merge branch 'main' into fix-DuckDBPyRelation.aggregate-incorrect-typing
evertlammerts Jan 6, 2026
45fb522
Fix : aggregate method typing to accept list of expressions (#246)
evertlammerts Jan 6, 2026
2eaced6
Merge branch 'main' into fix/python-udf-refcount-leak
evertlammerts Jan 6, 2026
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ project(duckdb_py LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

# Set the library name
set(DUCKDB_PYTHON_LIB_NAME "_duckdb")
Expand Down
4 changes: 3 additions & 1 deletion _duckdb-stubs/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,9 @@ class DuckDBPyRelation:
def __getattr__(self, name: str) -> DuckDBPyRelation: ...
def __getitem__(self, name: str) -> DuckDBPyRelation: ...
def __len__(self) -> int: ...
def aggregate(self, aggr_expr: Expression | str, group_expr: Expression | str = "") -> DuckDBPyRelation: ...
def aggregate(
self, aggr_expr: Expression | str | list[Expression], group_expr: Expression | str = ""
) -> DuckDBPyRelation: ...
def any_value(
self, column: str, groups: str = "", window_spec: str = "", projected_columns: str = ""
) -> DuckDBPyRelation: ...
Expand Down
54 changes: 27 additions & 27 deletions cmake/duckdb_loader.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,18 @@ function(_duckdb_validate_jemalloc_config)
return()
endif()

# jemalloc is only allowed in linux and osx debug builds
set(supported_os
CMAKE_SYSTEM_NAME
STREQUAL
"Darwin"
OR
CMAKE_SYSTEM_NAME
STREQUAL
"Linux")
set(jemalloc_allowed CMAKE_BUILD_TYPE STREQUAL "Debug" AND supported_os)
# jemalloc is only enabled on 64bit x86 linux builds
if(CMAKE_SIZEOF_VOID_P EQUAL 8
AND CMAKE_SYSTEM_NAME STREQUAL "Linux"
AND NOT BSD)
set(jemalloc_allowed TRUE)
else()
set(jemalloc_allowed FALSE)
endif()

if(NOT jemalloc_allowed)
message(
WARNING
"jemalloc extension is only supported on Linux and OSX in Debug builds.\n"
"Removing jemalloc from extension list.")
message(WARNING "jemalloc extension is only supported on Linux.\n"
"Removing jemalloc from extension list.")
# Remove jemalloc from the extension list
string(REPLACE "jemalloc" "" BUILD_EXTENSIONS_FILTERED
"${BUILD_EXTENSIONS}")
Expand Down Expand Up @@ -183,19 +180,17 @@ function(_duckdb_create_interface_target target_name)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_options(
${target_name}
INTERFACE
/wd4244 # suppress Conversion from 'type1' to 'type2', possible loss of
# data
/wd4267 # suppress Conversion from ‘size_t’ to ‘type’, possible loss of
# data
/wd4200 # suppress Nonstandard extension used: zero-sized array in
# struct/union
/wd26451
/wd26495 # suppress Code Analysis
/D_CRT_SECURE_NO_WARNINGS # suppress warnings about unsafe functions
/D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR # see
# https://github.com/duckdblabs/duckdb-internal/issues/5151
/utf-8 # treat source files as UTF-8 encoded
INTERFACE /wd4244 # suppress Conversion from 'type1' to 'type2', possible
# loss of data
/wd4267 # suppress Conversion from ‘size_t’ to ‘type’, possible
# loss of data
/wd4200 # suppress Nonstandard extension used: zero-sized array
# in struct/union
/wd26451
/wd26495 # suppress Code Analysis
/D_CRT_SECURE_NO_WARNINGS # suppress warnings about unsafe
# functions
/utf-8 # treat source files as UTF-8 encoded
)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_compile_options(
Expand Down Expand Up @@ -247,6 +242,11 @@ function(duckdb_add_library target_name)

# Create clean interface target
_duckdb_create_interface_target(${target_name})

# Propagate BUILD_EXTENSIONS back to caller scope in case it was modified
set(BUILD_EXTENSIONS
"${BUILD_EXTENSIONS}"
PARENT_SCOPE)
endfunction()

function(duckdb_link_extensions target_name)
Expand Down
2 changes: 1 addition & 1 deletion external/duckdb
Submodule duckdb updated 154 files
2 changes: 1 addition & 1 deletion src/duckdb_py/pyrelation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::ProjectFromTypes(const py::object
if (!projection.empty()) {
projection += ", ";
}
projection += names[i];
projection += KeywordHelper::WriteOptionallyQuoted(names[i]);
}
}
if (projection.empty()) {
Expand Down
2 changes: 1 addition & 1 deletion src/duckdb_py/python_udf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ static scalar_function_t CreateNativeFunction(PyObject *function, PythonExceptio
}

// Call the function
auto ret = PyObject_CallObject(function, bundled_parameters.ptr());
auto ret = py::reinterpret_steal<py::object>(PyObject_CallObject(function, bundled_parameters.ptr()));
if (ret == nullptr && PyErr_Occurred()) {
if (exception_handling == PythonExceptionHandling::FORWARD_ERROR) {
auto exception = py::error_already_set();
Expand Down
5 changes: 3 additions & 2 deletions tests/fast/adbc/test_connection_get_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ def test_connection_get_info_all(self):
"duckdb",
"v" + duckdb.__duckdb_version__, # don't hardcode this, as it will change every version
"ADBC DuckDB Driver",
"v" + duckdb.__duckdb_version__, # don't hardcode this, as it will change every version
"(unknown)",
"(unknown)",
None,
],
type=pa.string(),
)
Expand Down Expand Up @@ -58,7 +59,7 @@ def test_unrecognized_codes(self):
table = reader.read_all()
values = table["info_value"]

expected_result = pa.array(["duckdb", "(unknown)"], type=pa.string())
expected_result = pa.array(["duckdb"], type=pa.string())

assert values.num_chunks == 1
chunk = values.chunk(0)
Expand Down
7 changes: 7 additions & 0 deletions tests/fast/test_relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,3 +688,10 @@ def create_view(con, view_name: str) -> None:

res = con.sql("select * from vw").fetchall()
assert res == expected

def test_relation_select_dtypes_quotes_identifiers_with_spaces(self, duckdb_cursor):
df = pd.DataFrame({"na me": ["alice", "bob"], "x": [1, 2]})
rel = duckdb_cursor.from_df(df)
out = rel.select_dtypes([duckdb.sqltypes.VARCHAR]).fetchdf()
assert list(out.columns) == ["na me"]
assert out["na me"].tolist() == ["alice", "bob"]
40 changes: 40 additions & 0 deletions tests/fast/udf/test_udf_refcount_leak.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import gc
import platform
import sys

import pytest

import duckdb


@pytest.mark.parametrize(("rows", "iters"), [(1000, 20)])
def test_python_scalar_udf_return_value_refcount_does_not_leak(rows, iters):
if platform.python_implementation() != "CPython":
pytest.skip("refcount-based test requires CPython")

payload = b"processed_data_" + b"x" * 8192 # large-ish bytes to mimic the reported issue

def udf_bytes(_):
return payload # Always return the exact same object so we can track its refcount.

# Baseline refcount (note: getrefcount adds a temporary ref)
baseline = sys.getrefcount(payload)

con = duckdb.connect()
con.create_function("udf_bytes", udf_bytes, ["BIGINT"], "VARCHAR")

for _ in range(iters):
con.execute(f"SELECT udf_bytes(range) FROM range({rows})")
res = con.fetchall()
# Drop the result ASAP so we don't keep any refs alive in Python
del res
gc.collect()

# Re-check refcount. In the buggy version this grows by rows*iters (huge).
after = sys.getrefcount(payload)

# Allow a tiny tolerance for transient references/caches.
# In the presence of the leak, this will be thousands+ higher.
assert after <= baseline + 10, (baseline, after)

con.close()
Loading