Skip to content

Rewrite Arrow.jl internals for 3.0 - #609

Draft
quinnj wants to merge 301 commits into
mainfrom
core-rewrite
Draft

Rewrite Arrow.jl internals for 3.0#609
quinnj wants to merge 301 commits into
mainfrom
core-rewrite

Conversation

@quinnj

@quinnj quinnj commented Aug 20, 2026

Copy link
Copy Markdown
Member

Summary

  • replace the Arrow 2.x internals with a validated core, new IPC reader and writer, C Data and C Stream support, and a materialized public table facade
  • compile each Tables.Scan once, keep storage and public-domain plans separate, preserve stable empty-result schemas, and support sparse byte-range reads with footer statistics
  • deepen column construction around one recursive adapter for native, retained-schema, dictionary, Union, and ArrowTypes-backed columns
  • retain automatic ArrowTypes custom serialization, logical lifting, extension metadata, and nested custom-type round trips
  • prepare ArrowStrings 1.0 as the StringView representation package and keep Arrow.jl's export surface small
  • remove JSON3 and StructTypes integration and support claims
  • deepen deterministic fuzzing, Apache Arrow corpus coverage, PyArrow/nanoarrow oracles, C Data checks, allocation limits, trim checks, documentation, and package administration

Dependency

JuliaData/Tables.jl#380 is merged and released as Tables.jl 1.14.0. Commit dd8c272 removes the temporary source and CI pins, raises the Tables.jl compatibility floor to 1.14, and resolves Tables from the General registry everywhere (package, CI workflows, conformance image, docs).

Breaking changes

This is the Arrow.jl 3.0 rewrite. Important changes include:

  • Julia 1.10 is now required.
  • Arrow.Table and Arrow.Stream return materialized Julia vectors instead of lazy Arrow vector wrappers.
  • writing is eager; incremental writers, append, curried writes, and several Arrow 2.x writer options were removed.
  • Arrow.write(io, table) writes file format by default; use file=false for stream format.
  • ArrowTypes-based custom serialization remains automatic, but ArrowTypes is no longer exported by Arrow.jl. Packages that define mappings should depend on and import ArrowTypes directly. Arrow.ArrowTypes remains as a qualified compatibility binding.
  • most APIs are no longer exported and must be called through Arrow.

Fresh declared heterogeneous Julia Union columns are written as canonical dense Arrow Unions. A retained Union rewrite fails closed when materialization has lost its original route. Nested retained dictionary pools follow the same fail-closed rule.

See CHANGELOG.md and docs/src/migration.md for the complete list and migration guidance.

Validation

  • the previous head 67e17cf passes 115/115 GitHub checks across the push, pull-request, and Conformance workflows, including Windows Julia latest
  • the Tables.jl 1.14 registry switch (dd8c272, current head) passes the full root suites on Julia 1.10.11 and 1.12.6 against registered Tables 1.14.0, the JuliaC trim gate 6/6, Documenter, JuliaFormatter 2.12.4, and all three docker conformance suites (corpus, oracle, cdata) in the rebuilt image
  • the final mmap ownership fix passes the focused facade suites on Julia 1.10 and Julia 1.12: 577/577 and 579/579 assertions
  • the preceding frozen-tree full root suites pass on Julia 1.10 and Julia 1.12: 6,698/6,698 and 6,701/6,701 assertions
  • all four acceptance batteries pass on both versions: 159/159 named checks, including 47 Scan checks and real statistics-pruned no-fetch proofs
  • seeded properties pass 1,369/1,369 on both Julia versions
  • minimum ArrowTypes 2.0 compatibility passes 7/7 on Julia 1.10; the full ArrowTypes suite includes 49/49 public-domain Scan assertions
  • ArrowStrings 1.0 passes 2,515/2,515 tests on Julia 1.10 and Julia 1.12
  • deterministic extended fuzzing passes 512 cases, 3,072 variants, 3,072 rewrites, 27,648 Scan checks, 49 layout checks, and 20,000 malformed mutations across all 41 routes with 119 determinism replays
  • malformed mutation outcomes: 561 accepted by the format, 19,439 clean validation failures, 0 unexpected allocation-limit classifications, and no raw exception leaks
  • Apache Arrow corpus: 388 pass, 0 fail, 63 declared skips
  • PyArrow/nanoarrow oracle: 315 pass, 0 fail, 20 declared skips
  • C Data oracle: 147 pass, 0 fail, 8 declared skips
  • JuliaC trim-safe compile passes 6/6 checks with zero verifier errors and a successful generated binary
  • Documenter, JuliaFormatter 2.12.4, Apache RAT, TOML/YAML parsing, and git diff --check pass
  • independent standards and specification reviews report CLEAN with no strong or worthwhile findings

Co-authored by Codex

quinnj and others added 30 commits August 12, 2026 21:51
Reject unsupported hosts before old generated FlatBuffers code can interpret little-endian metadata with native-endian loads.

Co-Authored-By: Codex <codex@openai.com>
Document the fresh IPC endianness finding, its disposition, the withdrawn candidates, and validation evidence. Add round five to the README file index.

Co-Authored-By: Codex <codex@openai.com>
Require element alignment only when a vector has an element to read. Add an end-to-end zero-row Null batch regression that matches the empty struct-vector encoding found in official integration streams.

Co-Authored-By: Codex <codex@openai.com>
Reject KeyValue entries whose value field is absent instead of silently converting absence to an empty string. Cover both the malformed case and a valid explicit empty value.

Co-Authored-By: Codex <codex@openai.com>
State that the C stream importer and facade are future users of the shared pull protocol. Only the IPC reader implements that protocol in this prove-out.

Co-Authored-By: Codex <codex@openai.com>
Reject an absent Schema.fields vector instead of treating it as an empty schema. Keep a present zero-length vector valid and cover both encodings.

Co-Authored-By: Codex <codex@openai.com>
Avoid claiming that unimplemented C stream and partition adapters already exchange RecordBatch values. Distinguish the implemented IPC path from the target design.

Co-Authored-By: Codex <codex@openai.com>
Require every vector length word to be four-byte aligned, including empty vectors. This prevents malformed empty table vectors from reaching an old generated unsafe_wrap getter with a misaligned pointer while retaining valid relaxed element alignment for empty struct vectors.

Co-Authored-By: Codex <codex@openai.com>
Document the fresh IPC verifier and documentation findings, their dispositions, scope decisions, and validation evidence. Add round six to the README file inventory.

Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Return the exact lifecycle state to open when the winning closer is interrupted while waiting for active guards. This keeps the region usable and allows a later close to release it exactly once.

Co-Authored-By: Codex <codex@openai.com>
Clarify that same-size mutation can invalidate cached semantic validation for borrowed Julia buffers, while resizing can also invalidate their pointers.

Co-Authored-By: Codex <codex@openai.com>
Detect Arrow 0.17's Message-level experimental compression marker before record or dictionary body decoding. This prevents length-prefixed compressed bytes from being exposed as ordinary values.

Co-Authored-By: Codex <codex@openai.com>
State that mapped and foreign backing bytes must stay alive and unchanged while Core uses them or their cached validation certificates. External mutation cannot be detected by the prove-out.

Co-Authored-By: Codex <codex@openai.com>
Reject unknown flag bits and flags attached to layouts where their semantics do not apply. Failed imports still release both moved lifetimes exactly once.

Co-Authored-By: Codex <codex@openai.com>
Document five adversarial findings, their dispositions, scope decisions, and final validation. Update the README review index for round eight.

Co-Authored-By: Codex <codex@openai.com>
Construct imported owners without a finalizer, move the source under an interruption-safe handoff, and arm the copied owner only after ownership transfers. Failed post-move setup releases the copied producer callback exactly once.

Co-Authored-By: Codex <codex@openai.com>
Keep the claim, recursive release, and completion transaction interrupt-safe. Roll failed claims back to LIVE and retry idempotent descendant progress before the void C callback returns.

Co-Authored-By: Codex <codex@openai.com>
Keep each root registered while one reaper owns its cleanup. Record native frees and source-pin releases as progress so interrupted cleanup can resume without leaks or double frees.

Co-Authored-By: Codex <codex@openai.com>
Install release and cleanup rollback handlers before their state claims. Preallocated claim slots let task-delivered exceptions restore LIVE or clear the cleanup claim at the exact post-mutation boundary.

Co-Authored-By: Codex <codex@openai.com>
Use the source ArrowArray release field as the authoritative move marker. Task cancellation immediately after the move store now releases only the copied owner and cannot strand the producer callback.

Co-Authored-By: Codex <codex@openai.com>
Document the three C Data interruption and ownership findings, their dispositions, scope decisions, and final validation evidence.

Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Retry interrupted mapping release at both constructor and finalizer ownership boundaries. Check munmap failures before publishing the mapping as released.

Co-Authored-By: Codex <codex@openai.com>
quinnj and others added 30 commits August 18, 2026 22:09
…er route prose

- ci: pass `project: matrix.pkg.dir` to setup-julia so each package's
  `min` cell resolves its own Julia compat (ArrowStrings 1.10)
- manual: composite layouts are dynamic-path with schema-derived types
  (union and undeclared-null exceptions named); dictionary and run-end
  wrappers are transparent to their value child's route and type

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With setup-julia resolving each package's own floor, ArrowTypes' declared
`julia = "1.0"` selects 1.0.5, whose Pkg predates test/Project.toml and
which has no macOS ARM64 binary; the cells cannot run its suite on either
OS. Its `lts` cells remain its oldest lane. The floor itself is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Arrow.jl `julia = "1.10"` (was 1.12), ArrowTypes.jl `1.10` (was 1.0);
ArrowStrings.jl already 1.10. JuliaC `--trim` needs 1.12, so the trim
gate stays 1.12-only (test/trim_compile_tests.jl already guards on it).

- ArrowCore: the mmap release cell targets `_mmaproot(arr)` — the backing
  `Memory` from Julia 1.11, the array itself before (where Mmap registers
  its unmap finalizer); `mmapregion`'s docstring now attaches to it
- ArrowCore: buffer-role sequences are shared constants; `layoutspec` no
  longer builds a vector per buffer-by-role lookup (Julia 1.11+ elided
  it, 1.10 did not: typed struct materialization now 1.6 MB/100k rows on
  1.12 and 4.8 MB on 1.10, from 8.0 MB / 17.6 MB)
- ci: ArrowTypes back in the `min` cells; local ArrowTypes+ArrowStrings
  are developed BEFORE the build step (Julia 1.10's Pkg does not read
  `[sources]`, and ArrowStrings is unregistered)
- manual: state the 1.10 floor and the 1.12 trim requirement

Verified on Julia 1.10.11 and 1.12.6: Arrow 420/321/4 + acceptance,
ArrowTypes 133, ArrowStrings 2495; trim 6/6 (1.12); docs; docker
conformance corpus/oracle/cdata.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The release audit (dev/release/run_rat.sh) flagged 66 files without the
header: the review records and design/research notes under docs/dev, the
conformance, bench and trim environment Project files, and one test file
with a truncated header. ArrowTypes.jl 2.4.0 carries its raised Julia
floor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…omments

Tests
- batteries.jl runs the statistics and byte-range scan suites again
  (`_stats_main`, `_scan_main` → `_ranged_main`); they had not been called
  since the rewrite replaced src/, so ~720 lines of pins were dormant
- child processes: the typed-allocation and threaded-stress children are
  `@test success(...)` with the suite's project; the "concurrent IPC pulls"
  launch (an env-var guard nothing read) and two orphan pass lines removed
- shared helpers: `battery_prelude.jl` (the alias loop), MIXED_EXPECTED +
  `_mixed_two_partitions_bytes`, `_viewentry`/`_viewlong` reused; the fetch
  accounting (`FetchLog`/`countingsource`/`_fetched`) moves from src/scan.jl
  to the test helpers; `_rejects` is a `function`; `@test true` placeholders
  and value dumps dropped; history narration in comments rewritten
- test/Project.toml declares Pkg (trim_compile_tests.jl imports it)

Dead code
- ArrowCore: `_validate_temporal_values`, the `_validate_advisory_values`
  fallback, `arrowtype_for(::Type{String})`, an unreachable Struct branch,
  redundant structural re-checks in `_validate_ree_values`, `batch`'s
  duplicate validation loop, two unused default arguments
- IPC/C data: `_blockmessagebodylength`, `_containsdictionary`, the 5-arg
  `IPCStream`, `_vu16` alias, `formatstring(::DictionaryType)`; facade:
  the 4-arg `_writecolumn`, 3-arg `_scanbatch`, `Arrow`-scope `using EnumX`
- vendored FlatBuffers: `finishwithfileidentifier`, `createsharedstring!`
  (+ the `sharedstrings` field), `createbytevector`, `prependstructslot!`,
  the unused `sh` parameter, `getvalue` (called an undefined function),
  `getoffsetslot`, `getslot`, `setindex!`, the Builder-based table ctor;
  `src/metadata/Flatbuf.jl` and its generator branch (Arrow.jl's `Meta`
  module is the include target)

Behaviour
- `fromjulia` on a `Vector{Union{Missing,Vector}}` declares the list field
  nullable from the ELEMENT TYPE, like every other builder, not from the
  observed null count
- `DictEncode{T,V} <: AbstractVector{T}` (was `AbstractVector{Any}` with an
  `eltype` override that disagreed)
- `_validate_descriptor(::TimestampType)` is a `function` (banned form)
- error text uses `descriptorname` where the file mandates it; the
  `RecordBatch` arity check precedes per-column validation

Docs and comments (present state only; wrong claims corrected)
- docstrings attached to the right definitions (`mmapregion`,
  `ForeignOwner`, `_boundschema`, `_blockmessage`); new one-liners for the
  descriptor structs, `Schema`, `BufferRole`, `layoutspec`,
  `close!(::ForeignOwner)`, `release!(::StreamOwner)`, typed
  `materialize`, `statsfile`, `ArrowTypes.ToArrow`, `Arrow.ValidationError`
- `Arrow.Table`/`Arrow.Stream` docstrings: source kinds, `mmap`, decode set;
  `Arrow.jl` module docstring corrected; `getvalue`'s dispatch claim,
  `fromjulia`'s Bool claim, `decodefield`'s signature/variadic claim,
  `framemessages`' and other stale cross-file references
- generator header/comments and the research note describe the present
  tree; the DESIGN note's counts, statistics keys and extension shape;
  manual: Tables `jq/scan` prerequisite, file-format-only mmap, `close!`
  target, `columnnames` output, LargeListView, native-endian decimals,
  ArrowCore currency in the C-data section; README: public surface,
  ArrowStrings registration status, layout entries, docker network note;
  reference: `ImportedStream`, `close!(::ForeignOwner)`, `ValidationError`
- ArrowTypes: docstring `@ref` targets and typos; a README

Tooling
- CI: `1.11` replaces `lts` (identical to `min` at a 1.10 floor); Arrow.jl
  tests develop only ArrowStrings; codecov waits for the 48 uploads
- conformance: unused EnumX/PooledArrays dropped from the env and image
  warm list; the host env pins Harbor 1.1; corpus requires
  ARROW_TESTING_DIR; dead `_oracle_child` and stale usage lines removed;
  Dockerfile pins explained, `CMD` matches the driver
- bench: one workload-name list, absolute workdir; release: ArrowStrings
  registration and RC verification steps; `.gitignore`/rat exclusions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The repository's .JuliaFormatter.toml (the CI Format job) had never been
run over the 3.0 sources; origin/main is formatter-clean, so the branch
must be too. Whitespace/wrapping only — the formatter is idempotent on the
result, and every suite passes on Julia 1.10.11 and 1.12.6 before and
after (Arrow 421/321 + batteries, ArrowStrings 2495, ArrowTypes 133, trim
6/6, docs, docker conformance 275/170/143).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e, two dead helpers

- bench/run.jl: the multi-line backtick command literals are single-line
  (interpolated locals for the long paths), so a formatter pass cannot
  change the raw string a `@cmd` macro receives
- docs/dev/research-flatbuffers-cdata.md describes the present tree: the
  writer surface and FlatBuffers gap list without the deleted builder
  helpers, no line counts, the two malformed reference remnants removed;
  the C-data comparison states the semantic-tier import validation, the
  shared-cell `close!` revocation, the 1 MiB C-string bound, imported and
  exported schema metadata, the existing pyarrow C-data oracle, and the
  public `release!`/`close!` names
- conformance/arrowjson.jl `_u64` and tools/fbsgen.jl `_ident` (both
  uncalled) removed; the four generated files still regenerate
  byte-identically after JuliaFormatter
- The conformance image's warm list keeps EnumX on purpose: it is a direct
  Arrow dependency, which is the list's stated rule (690e1cc's message
  overstated the removal)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… corrected

The FlatBuffers/C-data research note now states the vendored builder's
shared low-level ancestry with JuliaData/FlatBuffers.jl, the complete
runtime surface the rewrite uses, the staged verify order (root start →
inline version gate → root rest), the builder head underflow behaviour,
how each Base-name collision is resolved, the schema release timing, the
eager null count on import, and that schema metadata import trusts the
producer's declared extents (no bounds claim). The cdata.jl header citation
that does not exist is gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tension

The byte-range read surface is a source interface plus `Tables.Scan`
pushdown on `Arrow.Table`, not a special handle:

- `Arrow.AbstractArrowSource` (src/source.jl): an implementation defines
  `sourcelength(src)` and `readrange(src, offset, len)`, and may override
  `readranges(src, ranges)` to issue a round's planned ranges concurrently
- `Arrow.Table(src::AbstractArrowSource; scan=…)`: the footer comes from
  one tail read (cached on the handle; its trailing magic also decides
  file vs stream format — a stream object is read whole), statistics and
  the scan window prune batches, only the surviving batches' metadata and
  the selected + filter-referenced columns' buffers are requested,
  coalesced — three request rounds. The leading magic is no longer
  fetched: the footer is the sole authority. `Arrow.Stream(src)` reads the
  object whole
- ext/ArrowCloudStoreExt.jl (`[weakdeps] CloudStore`): a
  `CloudStore.Object` is a source (its known size, one HTTP `Range` GET per
  range, one task per planned range) and `Arrow.Table(obj; …)` /
  `Arrow.Stream(obj; …)` accept it directly
- REMOVED from the public surface: `RangedSource`, `RangedFile`,
  `fetchranges`. The planner (`SourceFile`, internal) is the same code re-
  skinned: `_fetchexact`/`_fetchspans` read through the interface

Tests: the ranged battery runs over `BytesSource`/`CountingSource`
(exact ranges pinned as before); test/cloudstore_tests.jl drives the
extension end to end against CloudBase's Minio server (ranged reads,
projected/windowed and statistics-pruned scans, whole reads of file- and
stream-format objects, `Stream`); CloudStore/CloudBase are test deps.
Docs: manual remote-reads section, reference (`AbstractArrowSource`,
`sourcelength`, `readrange`, `readranges`), DESIGN §2, core-README, README.

Verified on Julia 1.10.11 and 1.12.6: Arrow 421/321 + all batteries +
extension 12/12; trim 6/6; docs; JuliaFormatter no-op; rat.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g, one body round

- (H) `readranges` is gone from the source interface: Arrow reads a round's
  planned spans itself, through `readrange`, with a worker pool of
  `concurrentreads(src)` tasks (default 1) pulling requests off one counter
  and storing every result by request index — a transport's completion
  order can never permute payloads, and the reads in flight are bounded
  whatever the span count. `readrange` results are type- and
  length-checked; the docstring states the trusted-source boundary (Arrow
  cannot authenticate the bytes a source returns for a range)
- (M) CloudStore extension: every range GET carries `If-Match` with the
  object's ETag, so a handle is pinned to one object version — an
  overwritten key fails the next read instead of mixing versions across
  request rounds (pinned against Minio); `concurrentreads` is 16
- (M) the planner fetches the selected dictionary bodies and the selected
  record buffers in ONE round (dictionaries decode first from the shared
  spans); the dead `blockwants` map is gone
- (L) a Footer larger than the tail window is read once and cached on the
  handle; ranges the cached tail already covers are served from it without
  a request (a file no larger than the window costs exactly one request)
- (L) `sourcelength` is validated as an Integer before conversion; the
  coalescer's negative-range guard is a `ValidationError`
- (L) `Arrow.Table(src)` without a scan, with an unpushable scan, over a
  zero-field file, or over a stream-format object reads the object whole
  (the cached tail plus its prefix: two requests) — what the manual and
  docstring say; the DESIGN request-count model matches
- tests: facade pins for request rounds through the public path, tail-once,
  tail-cache serving, bounded concurrency with out-of-order completion, and
  every contract violation (short/long/wrong-type payloads, invalid
  lengths) as ValidationError; the ext test pins ETag rejection; the
  battery's coalescing check plans outside a small tail

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt worker pool, no GCS claim

- `SourceFile` requires `sourcelength` to return an `Integer` (a Float,
  String, or `nothing` is a ValidationError, pinned through `Arrow.Table`)
- the range-read worker pool follows the repository guidelines: the shared
  request counter is an `@atomic` field on a mutable `_SpanQueue` (no
  `Threads.Atomic`), each worker is `errormonitor(Threads.@Spawn …)`, and
  the per-worker loop is its own function
- the CloudStore extension claims S3 and Azure Blob Storage only: the
  registered CloudStore releases (1.6–1.8) have no GCS `Object` path

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e extension)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every Arrow.jl test cell, the monorepo job, the docs job and the release
verification failed at precompile because registered Tables.jl has no
`Tables.Scan`. The root Project's `[sources]` now pins Tables to the
`jq/scan` branch (read by Pkg from Julia 1.11, including the release
verification's Julia 1.12); the 1.10 test cells, the monorepo env and the
docs env add it explicitly alongside the local ArrowStrings develop. The
docs job installs Julia through setup-julia like the other jobs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Complete the final correctness, API, documentation, test, CI, conformance, and release-administration pass for the Arrow 3 rewrite.

Track the exact reviewed Tables.Scan prerequisite and add focused regressions for its current API and semantics.
Keep ArrowTypes minimum-version coverage on Ubuntu while excluding only the macOS Apple Silicon cells, for which Julia 1.0 has no binary.
Process root and subpackage coverage from their own source directories so the ArrowTypes Julia 1.0 lane never parses Arrow 3 source syntax.
Restore ArrowTypes as a facade dependency and compatibility boundary. Lower and lift custom values recursively, preserve extension metadata, synthesize dense unions, and keep extension filters in the public value domain.

Add cross-version, downstream, nested-layout, partition, scan, and malformed-input coverage. Update the migration, release, CI, and conformance guidance for ArrowTypes 2.4 and ArrowStrings 1.0.
… release files

- bench/: the 2.x leg (bench_2x.jl, env2x/) is gone; the driver times this
  package and PyArrow
- docs/dev/research-flatbuffers-cdata.md removed
- dev/release/README.md and verify_rc.sh are back at main's version

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- `release!` is the one release verb across the API: `release!(::Table)`,
  `release!(::Stream)`, `release!(::OwnerRegion)`, and the C-data owners.
  Arrow exports it (and imports ArrowCore's generic so cdata.jl extends it)
- ForeignOwner has one verb and one contract: `release!(owner)` revokes every
  region over the import through the shared cell (later access is an
  InvalidStateException), then the cell's action runs the producer release
  exactly once — the raw post-release undefined behaviour is gone
- `ArrowStrings.ArrowStringVector` is `ArrowStrings.StringVector`

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`_applyscan` now consumes a `Tables.Scan` exactly through `_ScanSink`
(src/scan.jl), on both the in-memory and the byte-range paths:
- the filter is evaluated per decoded batch by the generic evaluator
  (`Tables.filtermask` over the decode set), so Arrow and the executor share
  one three-valued semantics by construction
- `offset`/`limit` compose over the qualifying rows with saturating
  arithmetic; without a filter the metadata window still skips whole
  batches, with one decoding stops the moment the window is full
- selection and renames are applied at column construction, in selection
  order; the residual is empty except for type overrides
- the battery pins the consumed residual, filter+limit in one pass, and
  that a filled window stops decoding before a corrupt later batch

Tables.jl's `jq/scan` branch moved: `Tables.bind` is `Tables.resolve` (the
`BoundScan` carries the name-normalized filter, so Arrow's own resolver is
gone), `All()` is the projection identity, and `coleq`/`colne`/`in_` are
`colcmp(==, …)`/`colcmp(!=, …)`/`colin` — adapted in src, tests, and manual.

Docs: the DESIGN note describes the implemented pushdown (no stages);
manual, core-README and README follow; the core-README "Interruption
contract" section is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unify scan planning and column construction around bounded, single-pass seams. Restore ArrowTypes lowering and lifting, remove JSON3 integration, deepen conformance and deterministic fuzz coverage, update documentation, and prepare ArrowStrings 1.0. Preserve empty-scan schema metadata and charge every package-owned allocation.
Tables.jl 1.14.0 ships Tables.Scan, so the temporary source override on
the reviewed scan-branch commit is gone: the package, CI workflows,
fuzz workflow, conformance image, and docs all resolve Tables from the
registry, and the compat floor is 1.14. The released Scan represents
"keep every column" as Tables.All() instead of nothing, so the resolver
work model drops its now-impossible nothing branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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