feat(rust/ffi): report ECANCELED through exported streams for cancelled operations - #3
Open
fornwall wants to merge 1 commit into
Open
feat(rust/ffi): report ECANCELED through exported streams for cancelled operations#3fornwall wants to merge 1 commit into
fornwall wants to merge 1 commit into
Conversation
fornwall
force-pushed
the
rust-ffi-stream-errno-status
branch
2 times, most recently
from
July 8, 2026 00:30
d91e7b2 to
e20fa42
Compare
The driver exporter exported result readers via arrow-rs's FFI_ArrowArrayStream::new, which reduces every mid-stream failure to an errno plus a message string: rich adbc_core errors surfaced through ArrowError::ExternalError landed in the catch-all EINVAL, and the ADBC 1.1.0 ErrorFromArrayStream slot in the driver table was left as a TODO. Replace the export machinery (adapted from arrow-rs's ffi_stream.rs) with one that stashes the full adbc_core::error::Error found in the error's source chain (or synthesized from the ArrowError variant): - get_next/get_schema return the errno for the error's ADBC status via a port of the canonical InternalAdbcStatusCodeToErrno table, so e.g. cancelled operations report ECANCELED as the validation suite expects. - ErrorFromArrayStream is now implemented, handing consumers the stream-owned FFI_AdbcError (message, SQLSTATE, vendor code, details) together with its status code, and NULL for foreign streams. Own streams are recognized via a registry of live private-data addresses, since Rust does not guarantee the function-pointer identity the C/C++ drivers rely on (Miri fails such comparisons). Tested by unit tests, a new end-to-end test driving the exported C driver table against a failing dummy-driver stream, and a clean run of the adbc_ffi suite under Miri. Signed-off-by: Fredrik Fornwall <fredrik@fornwall.net>
fornwall
force-pushed
the
rust-ffi-stream-errno-status
branch
from
July 8, 2026 00:43
e20fa42 to
3ec70f5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The driver exporter exports result readers via arrow-rs's
FFI_ArrowArrayStream::new, whose error→errno mapping is hardcoded to theArrowErrorvariant:NotYetImplemented→ENOSYS,MemoryError→ENOMEM,IoError→EIO, everything else→EINVAL. Rust ADBC drivers surface their richadbc_core::error::ErrorthroughArrowError::ExternalError, which lands in theEINVALcatch-all — so a driver whose in-flight query was cancelled has no way to reportECANCELEDthrough the C stream, even though the ADBC spec (and the C++ validation suite'sSqlQueryCancel) expect exactly that errno afterAdbcStatementCancel.Concretely:
adbc-spanner's now-working cancellation failsSpannerStatementTest.SqlQueryCancelwitherrno 22 (EINVAL), message "Cancelled: operation cancelled"— the status is right there in the message but unreachable in the code.Why owning the stream callbacks (and not something smaller)
This was checked against the C/C++ side of the repo, and the "invasive" part — implementing the
ArrowArrayStreamcallbacks instead of delegating to arrow-rs — is exactly what the in-tree C/C++ drivers do. E.g. the PostgreSQL driver'sTupleReader(c/driver/postgresql/statement.cc) implementsGetSchema/GetNextby hand and returnsInternalAdbcStatusCodeToErrno(status_); the shared status→errno table lives inc/driver/common/utils.c. arrow-rs's generic exporter is the outlier: it stringifies the error into its private data, so the ADBC status is unreachable once the stream is built.Smaller alternatives considered and rejected:
ArrowErrorvariant — no variant reachesECANCELED; dead end.EINVALpost-hoc — only the stringified message survives inside arrow-rs's private data, so this degenerates to sniffing"Cancelled:"prefixes; same amount of unsafe callback code, but fragile.FFI_ArrowArrayStream::new_with_error_code(reader, f)) — the genuinely smaller end-state, but needs an arrow-rs API addition and release; worth proposing separately.Fix
New
rust/ffi/src/exported_stream.rs: the same export machinery (adapted from arrow-rs'sffi_stream.rs, Apache-2.0), differing only in the error-code function:adbc_core::error::Error;errno_for_status, a direct port of the canonicalInternalAdbcStatusCodeToErrno(c/driver/common/utils.c):Cancelled→ECANCELED,NotFound→ENOENT,NotImplemented→ENOTSUP,Timeout→ETIMEDOUT,Unauthenticated/Unauthorized→EACCES, etc. — so Rust drivers report the same errnos through the C stream as the C/C++ drivers do;Errno constants come from
libc(new workspace dependency,default-features = false) rather than a hand-maintained per-OS cfg table — platform-correct everywhere including Windows, mirroring how the C code gets them from<errno.h>.All seven
FFI_ArrowArrayStream::new(reader)export sites indriver_exporter.rsnow go throughexport_reader.Testing
Unit tests drive the exported stream through the raw C callbacks, exactly as a C consumer would:
CancelledADBC error returnsECANCELED, message preserved viaget_last_errorCancellederror nested deeper in a source chain is still foundEINVAL/ENOSYS/EIO)cargo test -p adbc_ffi,cargo clippy -p adbc_ffi --all-targets -- -D warnings,cargo fmt --checkall pass.Follow-up (separate changes)
adbc_core/adbc_ffigit pin inadbc-spannerto include this, and re-gateSpannerStatementTest.SqlQueryCancelin its validation suite.apache/arrow-adbc) after it has proven out in the fork.🤖 Generated with Claude Code
https://claude.ai/code/session_01FXTDX6SvmGeXvttQux5zbS