schenksj commented on PR #4952:
URL: 
https://github.com/apache/datafusion-comet/pull/4952#issuecomment-5304813339

   Thanks @parthchandra — and apologies for being hard to reach lately. Hoping 
we can get our meeting
   back on the calendar soon.
   
   All six threads are addressed, plus a rebase onto current `main` that turned 
out to be load-bearing
   — it surfaced two things this PR was silently wrong about. Details below.
   
   ## The rebase (worth reading first)
   
   This branch was 141 commits behind. Rebasing changed two substantive things:
   
   **1. Field 118 is now `Sample sample = 118` on `main`.** Exactly the 
collision you predicted — it
   just came from core rather than from #4633. `contrib_scan = 200` was already 
immune, but my
   `reserved 118` was not: reserving a number that is now in use is a protoc 
error. Dropped it. This
   is a nice retroactive argument for the permanent-envelope design: the 
contrib side needed no change
   at all.
   
   **2. `main` now supports metadata columns that this PR assumed were 
unsupported.** `transformV1Scan`
   serves `fileConstantMetadataColumns` (`file_path`, `file_name`, `file_size`, 
...) natively and
   rejects only the reader-generated ones (`row_index`); the V2 Iceberg path 
supports the columns in
   `CometIcebergNativeScan.MetadataFieldIds`. My earlier refactor had hoisted a 
**blanket**
   `metadataCols(...).nonEmpty` guard into each transform path, which on this 
base would have regressed
   both. Removed both blanket guards, and deleted a duplicate `metadataCols` 
helper the rebase left
   behind.
   
   That changes the shape of the fix for the metadata thread — see below.
   
   ## Per-thread
   
   **`CometScanContrib.scala:49` — doc that an implementation MUST return 
`None` for a scan it does not
   own.** Added, with the reasoning: first claim wins, so a contrib claiming 
another format's scan
   doesn't merely mis-handle it — it hides the scan from the contrib that could 
have read it, and the
   outcome depends on unspecified `ServiceLoader` ordering. The doc also 
directs implementers to decide
   ownership from something definitive (the relation's `fileFormat` class, the 
table's provider, a
   catalog type) rather than a path or table-name heuristic another format may 
also match.
   
   I also documented "own but cannot handle" as a distinct, expressible case: 
return
   `Some(withFallbackReason(scanExec, ...))` — claim the scan and terminate it 
with a diagnosable
   reason — rather than `None`, which would let Comet's built-in handling 
attempt a format it doesn't
   understand.
   
   **Can two contribs claim the same scan?** In principle yes, and core cannot 
detect it: a claim is
   opaque, and the only way to learn that a second contrib would also have 
claimed is to ask it, which
   is precisely what claiming is meant to prevent. So it's resolved by 
contract, not arbitration —
   first `Some` wins, later contribs are not consulted, and that's now 
documented on `firstClaim` and
   covered by a test. If you'd rather core were noisy about it, the cheap 
version is a debug-only pass
   that asks every contrib and logs when more than one claims; happy to add 
that if you want it.
   
   **`CometScanRule.scala:135` — a V2 contrib with a table named 
`files`/`snapshots` never reaches the
   hook.** Real bug; fixed by your first option. `isIcebergMetadataTable` moved 
out of the outer
   `transformScan` match and into `transformV2Scan`, directly after the contrib 
hook declines. A
   genuine Iceberg metadata table falls back with the identical reason; a 
contrib that owns a
   similarly-named table now gets offered it first. (I kept your 
case-insensitive refinement from
   `main` intact when relocating it.)
   
   **`CometScanRule.scala:181` — a V1 metadata test.** Given the change above, 
hoisting the guard was
   the wrong shape — `main`'s guard is now nuanced per column, and moving it 
would have thrown that
   away. Instead the **contrib hook moved up**: `transformV1Scan` offers the 
scan to `CometScanContrib`
   before any built-in guard runs, then `main`'s constant-vs-generated metadata 
logic proceeds
   untouched. Same property you asked for (a contrib that synthesises 
`_metadata` still gets a look),
   without relitigating what core supports.
   
   Test added to `CometScanRuleSuite` asserting the fallback *reason* for 
`_metadata.row_index` over a
   parquet source on a default build (`main`'s existing test asserts the plan 
shape; this asserts the
   message). Verified red — removing the guard fails it. Worth noting the red 
run also showed `main`'s
   plan-shape test still passing without the guard, so the reason assertion is 
doing real work.
   
   **`CometScanContrib.scala:99` — a suite like `CometScanWithPlanDataSuite`.** 
Added
   `CometScanContribSuite`, 7 tests, all on the default build, covering your 
three cases plus three
   more the contract needs:
   
   - empty registry → `None` from both hooks;
   - a stub registered through a `URLClassLoader` service file is discovered 
and its claim is returned;
   - a throwing stub is logged, declined, and the next contrib still gets a 
look;
   - a declining contrib passes through to the next;
   - first claim wins and later contribs are **not** consulted;
   - a `LinkageError` still propagates (the `NonFatal` boundary is deliberate, 
so it's pinned).
   
   One thing that test found: asserting "the default build registers no 
contribs" against the
   *registry* passes vacuously, because `contribs` swallows a 
`ServiceConfigurationError` and yields an
   empty registry — so "empty" holds both when nothing is registered and when 
something is registered
   but unloadable. (A stale service file in my `target/classes` from a contrib 
build is how I hit it.)
   The test now asserts against raw `ServiceLoader` discovery, which fails 
loudly instead.
   
   **`operator.proto:318` — issue to move contrib messages into 
`contrib/delta/proto`.** Filed as
   #5378. I also did the one part that seemed unsafe to defer: dispatch was 
keyed on
   `spark.spark_operator.DeltaScan` — core's proto package — so the relocation 
would have changed the
   identifier. It's now `comet.contrib.delta.DeltaScan`, naming the owner 
rather than the file's
   current home, which makes the move a no-op on the wire.
   
   **`planner.rs:1622` — issue for a generic native handler.** Filed as #5379, 
with both paths you
   outlined (dynamic plugin loading, incl. your nullderef reference; and 
statically-linked independent
   crates with the crate-level refactor needed to avoid the `core -> contrib -> 
core` cycle) and a note
   on why the priority is lower than the JVM side: the coupling is compile-time 
and feature-gated, and
   `dev/verify-contrib-delta-gate.sh` asserts a default build links zero 
contrib symbols.
   
   ## Validation
   
   On the rebased branch:
   
   - `CometScanContribSuite` (7), `CometScanRuleSuite`, 
`CometScanWithPlanDataSuite`,
     `PlanDataInjectorSuite`, `CometScanSchemeFallbackSuite` — **24/24 green on 
Spark 3.5 / Scala
     2.12 and on Spark 4.0 / Scala 2.13**, against a freshly built native lib.
   - `cargo check` clean on the default build and with `--features 
contrib-delta`; clippy `-D warnings`
     and `cargo fmt` clean.
   - `dev/verify-contrib-delta-gate.sh`: default build compiles no contrib 
classes, packages no contrib
     `META-INF/services`, links no contrib symbols.
   
   ---
   🤖 This reply was drafted with [Claude Code](https://claude.com/claude-code).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to