Skip to content

Fix 0-row result when re-executing a cached prepared statement scanning a python object - #54

Merged
adsharma merged 1 commit into
mainfrom
fix/pyarrow-scan-reexecution
Sep 6, 2026
Merged

Fix 0-row result when re-executing a cached prepared statement scanning a python object#54
adsharma merged 1 commit into
mainfrom
fix/pyarrow-scan-reexecution

Conversation

@adsharma

@adsharma adsharma commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Re-executing the same statement that scans a pandas/polars/pyarrow object (e.g. two LOAD FROM df queries on one connection) returned 0 rows on every execution after the first. This broke test_pyarrow_primitive (its second, parameterized LOAD FROM $df execution returned an empty table).
  • Root cause: with the physical-plan cache enabled, the cached operator tree template is cloned via TableFunctionCall::copy(), which shares the same TableFuncSharedState instance between the template and its copies. The first execution advanced PyArrowTableScanSharedState::currentChunk to the end of the chunk list, and resetState() was the base-class no-op, so prepareForReuse() never rewound the cursor and subsequent executions scanned zero chunks.
  • Fix: override resetState() in PyArrowTableScanSharedState to reset currentChunk = 0.

Testing

  • Added test_pyarrow_scan_repeated_execution (executes the same LOAD FROM df RETURN count(*) three times, asserts the full count each time). Verified it fails without the fix and passes with it.
  • test_pyarrow_primitive and the rest of test_scan_pandas_pyarrow.py pass; CI will cover the full suite.

CI note: this only touches the pybind extension header + tests; C++ change is one-line override in src_cpp/include/pyarrow/pyarrow_scan.h.

…ng a python object

Re-executing the same statement that scans a pandas/polars/pyarrow object
(e.g. two LOAD FROM df queries on one connection) returned 0 rows on every
execution after the first.

Root cause: with the physical-plan cache enabled, the cached operator tree
template is cloned via TableFunctionCall::copy(), which shares the same
TableFuncSharedState instance between the template and its copies. The first
execution advanced PyArrowTableScanSharedState::currentChunk to the end of
the chunk list, and the shared state's resetState() was the base-class
no-op, so prepareForReuse() never rewound the cursor and subsequent
executions scanned zero chunks.

Fix: override resetState() in PyArrowTableScanSharedState to reset
currentChunk to 0.

Includes a regression test that executes the same LOAD FROM df query three
times and asserts each execution returns the full row count.
@adsharma
adsharma merged commit dd285ce into main Sep 6, 2026
2 checks passed
@adsharma
adsharma deleted the fix/pyarrow-scan-reexecution branch September 6, 2026 22:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant