Files
lancedb/python/python/tests/test_duckdb.py
T
Raphael Fakhri 566517be05 fix(python): apply pyarrow expression filters in PyarrowDatasetAdapter (#4592)
`PyarrowDatasetAdapter` lets DuckDB query a LanceDB table, but DuckDB
pushes `WHERE` clauses into `Dataset.scanner(filter=...)` as
`pyarrow.compute.Expression` objects. The adapter forwarded them to
`Table.count_rows` and `LanceQueryBuilder.where`, which accept only SQL
strings, so every filtered DuckDB query failed with `TypeError: argument
'filter': 'Expression' object is not an instance of 'str'`.

This change:

- Applies a `pyarrow.compute.Expression` filter locally with
`pyarrow.dataset.Scanner.from_batches`. The scan reads all columns and
pyarrow applies the filter and projection, because DuckDB can filter on
a column it does not project.
- Applies `head` limits after the filter, so the limit counts matching
rows.
- Routes `PyarrowDatasetAdapter.count_rows(filter)` through the scanner,
so it accepts both filter types.

SQL string filters keep the existing path and still run inside LanceDB.

Fixes #4591

### Testing

- New `test_dataset_adapter_expression_filter` in `test_pyarrow.py`
covers `count_rows`, `to_table`, `head` and a projection that excludes
the filtered column.
- New `test_query_with_filter` in `test_duckdb.py` runs DuckDB queries
with a `WHERE` clause.
- Both tests fail without the fix and pass with it. `ruff check` and
`ruff format --check` pass.
2026-10-23 03:24:23 +08:00

41 lines
1.2 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
import duckdb
import pyarrow as pa
import lancedb
from lancedb.integrations.pyarrow import PyarrowDatasetAdapter
def test_basic_query(tmp_path):
data = pa.table({"x": [1, 2, 3, 4], "y": [5, 6, 7, 8]})
conn = lancedb.connect(tmp_path)
tbl = conn.create_table("test", data)
adapter = PyarrowDatasetAdapter(tbl) # noqa: F841
duck_conn = duckdb.connect()
results = duck_conn.sql("SELECT SUM(x) FROM adapter").fetchall()
assert results[0][0] == 10
results = duck_conn.sql("SELECT SUM(y) FROM adapter").fetchall()
assert results[0][0] == 26
def test_query_with_filter(tmp_path):
data = pa.table({"x": [1, 2, 3, 4], "y": [5, 6, 7, 8]})
conn = lancedb.connect(tmp_path)
tbl = conn.create_table("test", data)
adapter = PyarrowDatasetAdapter(tbl) # noqa: F841
duck_conn = duckdb.connect()
results = duck_conn.sql("SELECT SUM(y) FROM adapter WHERE x > 2").fetchall()
assert results[0][0] == 15
results = duck_conn.sql("SELECT y FROM adapter WHERE x > 2 ORDER BY y").fetchall()
assert results == [(7,), (8,)]