Skip to content

[RLC] Case 2 — same-file deletion-vector merge (concurrent DML ⟂ DML) - #2

Open
sezruby wants to merge 35 commits into
masterfrom
row-level-concurrency-dv-poc
Open

sezruby wants to merge 35 commits into
masterfrom
row-level-concurrency-dv-poc

Conversation

@sezruby

@sezruby sezruby commented Jul 7, 2026 •

Copy link
Copy Markdown
Owner

Part of the row-level-concurrency umbrella (#3) — Case 2. Full design: #5. Addresses delta-io#7057.

Summary

POC implementing deletion-vector-based row-level concurrency for OSS Delta (Spark). Two concurrent DML operations that modify the same physical file but touch disjoint rows no longer abort the loser. A new config-gated phase in ConflictChecker decodes both transactions' deletion vectors, and when the newly-deleted rows are disjoint, merges them (dv_win ∪ dv_cur), writes a new DV file, and rebases the losing transaction onto the winner's post-image.

This is the sound same-file DV union. It is deliberately narrow: it reconciles conflicts that are provable from DV positions alone, and falls back to today's abort for everything else — including a rewrite-only DML winner's new image files, which are handed to conflict-time data skipping (Case 1, #4 / #8). See Correctness: UPDATE image files below.

What it does

  • New phase resolveRowLevelConflicts() in ConflictChecker, run before the file-level checks.
  • For a file both transactions modified with a DV, decode dv_win, dv_cur, and their common base dv_base (from the current txn's pre-image RemoveFile).
  • Disjointness rule: (dv_win ∩ dv_cur) \ dv_base empty → merge the DVs, persist a new DV file, and rebase the current AddFile(P) onto the winner's post-image (its RemoveFile(P) now tombstones the winner's AddFile(P)). Non-empty → genuine same-row conflict, aborts as before.
  • The delete/read, delete/delete, and append checks skip the resolved same-file paths (rowLevelResolvedPaths) — re-checking the winner's re-added AddFile(P) (whose DV we already folded in) would be a false conflict.
  • DV-only — row tracking is not required. Physical DV positions index one immutable Parquet file, so they are version-independent; when row tracking is enabled, the existing reassignOverlappingRowIds phase (already run) keeps IDs consistent since the merged file is the same physical file with an unchanged base row ID.
  • Gated by spark.databricks.delta.rowLevelConcurrency.enabled (internal, default off). No protocol / action / reader changes — the output is a standard DV action.

Correctness: UPDATE image files (deferred to Case 1 / #4)

An UPDATE produces two things per touched file: the same-path DV pair (handled here, soundly) and a new image file at a fresh path carrying the updated row values. This PR does not skip those image files in the append check. That is intentional:

An UPDATE can move a row into the loser's predicate — winner UPDATE SET x = 15 WHERE id = r, loser DELETE WHERE x > 10, where r started at x = 5. Under a winner→loser serial order the loser would delete r, so this is a genuine write-skew conflict. The DV union cannot detect it (r is a different row from the ones the loser touched), so any op-agnostic "suppress a rewrite-only DML winner's added files" shortcut would be unsound — it would let the loser commit with r surviving at x = 15, unmatched by its own predicate, strictly weaker than base WriteSerializable.

Instead, image files remain ordinary non-blind changed-data files and flow into the standard added-files check. On an unpartitioned table that conservatively conflicts (one-way safe: falls back to today's abort). Reconciling the provably-disjoint case — the loser's read predicate vs the image file's stats — is exactly conflict-time data skipping, owned by Case 1 (#4 / #8). Case 2's UPDATE image-file reconcile therefore depends on #8 being in the build; standalone, Case 2 is DV-union only.

Scope

Testing

RowLevelConcurrencySuite (new), using the phase-locking concurrency harness:

  • DELETE/DELETE: disjoint → both commit (merged DV cardinality 2); overlapping → still conflict.
  • UPDATE: DELETE (loser) vs UPDATE (winner) → winner's image file conservatively conflicts, loser aborts cleanly (one-way safe; Case 1 refines this to reconcile the disjoint case); overlapping UPDATE/UPDATE on the same row → still conflict.
  • Edge cases: winner fully removes a file → conflict; 3-way concurrent disjoint DELETEs → all commit; partitioned table → both commit.
  • Gating: feature flag off → conflict; deletion vectors disabled (flag on) → gate off, conflict.
  • Coverage: non-empty base-DV (exercises .andNot(base)), 3-way overlap → abort, explicit Serializable, row-tracking _metadata.row_id stability, CDF one-delete-per-version.

Data correctness is asserted end-to-end (final row set) plus merged-DV cardinalities.

🤖 Generated with Claude Code

@sezruby
sezruby force-pushed the row-level-concurrency-dv-poc branch from 001b81e to 73c7bc7 Compare July 8, 2026 03:11
…merge

POC for issue delta-io#7057. Adds a config-gated conflict-resolution phase to ConflictChecker that
resolves 'same physical file' conflicts between concurrent DML. It decodes both transactions'
deletion vectors and, when the newly-deleted rows are disjoint, merges them (dv_win UNION dv_cur),
writes a new DV file, and rebases the losing transaction onto the winner's post-image. The
delete/read, delete/delete, and append checks skip resolved paths; rewrite-only DML (DELETE/UPDATE)
winners also skip the append check. DV-only (row tracking not required). Gated by
spark.databricks.delta.rowLevelConcurrency.enabled (default off). Adds RowLevelConcurrencySuite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sezruby
sezruby force-pushed the row-level-concurrency-dv-poc branch from 73c7bc7 to e356283 Compare July 8, 2026 03:19
sezruby added a commit that referenced this pull request Jul 27, 2026
… remap

Opt-in (spark.databricks.delta.optimize.conflictReconciliation.enabled,
internal, default off). When a compaction OPTIMIZE loses to a concurrent
DELETE/UPDATE that added a deletion vector to a removed source file, the
ConflictChecker remaps that DV onto the compacted output by offset arithmetic
(outputPos = runOutStart + (sourceRow - runSourceStart)) and unions it into the
output's DV, instead of aborting.

The run layout (source_file, start, count) is observed at write time -- cheaply.
SourceCompositionCaptureExec reads the source file from InputFileBlockHolder and
counts rows per file; no helper column, no _metadata.row_index (avoiding the
DV-aware scan cost), rows pass through unchanged (~0% overhead). The composition
is persisted in the output AddFile's OPTIMIZE_SOURCE_COMPOSITION tag, so the
reconcile is cross-cluster-safe (replayed from the commit, no in-memory state).

DV'd source files supported: the driver splits their live rows into contiguous
physical segments around the read-time DV gaps, and only the winner's
incremental deletions (winnerDv \ readTimeDv) are remapped.

Coalesce compaction only; repartition (shuffle empties the holder) and
reclustering/ZORDER (row-permuting) are not tagged and abort as before.
All-resolvable-or-abort. OPTIMIZE-loser direction only; DELETE-loser and MERGE
are follow-ups. Stacked on the row-level-concurrency DV PoC (#5 / PR #2).

Adds OptimizeConflictReconciliationSuite (6).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove the unsound "Layer 2" append-suppression for rewrite-only DML
winners. Keying the append-check skip on the winner's op type
(DELETE/UPDATE) is unsound: an UPDATE can move a row into the loser's
predicate (winner `SET x=15`, loser `DELETE WHERE x>10`, row was x=5), a
genuine write-skew that suppressing the winner's image file would hide.

`canSkipAddedFileForRowLevelConcurrency` now skips only the same-file DV
union paths (`rowLevelResolvedPaths`). A rewrite-only DML winner's new
image files flow into the standard added-files check (conservative abort,
one-way safe); reconciling the provably-disjoint case via conflict-time
data skipping is owned by Case 1 (#4/#8).

Tests reframed to the Layer-1 envelope (9 tests, all passing): the two
"both commit" UPDATE cases move to Case 1; DELETE-vs-UPDATE now asserts
the image file conservatively conflicts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sezruby added a commit that referenced this pull request Aug 2, 2026
… remap

Opt-in (spark.databricks.delta.optimize.conflictReconciliation.enabled,
internal, default off). When a compaction OPTIMIZE loses to a concurrent
DELETE/UPDATE that added a deletion vector to a removed source file, the
ConflictChecker remaps that DV onto the compacted output by offset arithmetic
(outputPos = runOutStart + (sourceRow - runSourceStart)) and unions it into the
output's DV, instead of aborting.

The run layout (source_file, start, count) is observed at write time -- cheaply.
SourceCompositionCaptureExec reads the source file from InputFileBlockHolder and
counts rows per file; no helper column, no _metadata.row_index (avoiding the
DV-aware scan cost), rows pass through unchanged (~0% overhead). The composition
is persisted in the output AddFile's OPTIMIZE_SOURCE_COMPOSITION tag, so the
reconcile is cross-cluster-safe (replayed from the commit, no in-memory state).

DV'd source files supported: the driver splits their live rows into contiguous
physical segments around the read-time DV gaps, and only the winner's
incremental deletions (winnerDv \ readTimeDv) are remapped.

Coalesce compaction only; repartition (shuffle empties the holder) and
reclustering/ZORDER (row-permuting) are not tagged and abort as before.
All-resolvable-or-abort. OPTIMIZE-loser direction only; DELETE-loser and MERGE
are follow-ups. Stacked on the row-level-concurrency DV PoC (#5 / PR #2).

Adds OptimizeConflictReconciliationSuite (6).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…refs

Add coverage for the deletion-vector-merge row-level concurrency path:
- non-empty base DV: DELETE id=5 first, then two disjoint concurrent
  DELETEs, exercising the `(dv_win INTERSECT dv_cur) MINUS base` subtraction
  that every prior test left empty.
- 3-way where the last (overlapping) txn aborts while the two disjoint
  ones commit.
- disjoint DELETEs reconcile under explicit Serializable isolation.
- row tracking: surviving rows keep their stable `_metadata.row_id`
  across the same-file DV merge.
- change data feed: each deleted row is emitted once, attributed to the
  version that deleted it (winner at N, reconciled txn at N+1).

Also scrub fork-internal taxonomy from the suite comments (drop "Case 1 /
fork issue #4" and "Layer-1" wording) in favor of self-contained
descriptions ("conflict-time reader-side data skipping (a separate
change)", "same-file DV union"). No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sezruby sezruby changed the title [Spark] Row-level concurrency for concurrent DML via deletion-vector merge (POC) [RLC] Case 2 — same-file deletion-vector merge (concurrent DML ⟂ DML) Aug 3, 2026
sezruby and others added 8 commits August 4, 2026 11:25
Move the RLC deletion-vector merge (rowLevelConcurrencyEnabled,
winningOperationName, canSkipAddedFileForRowLevelConcurrency,
resolveRowLevelConflicts, the DV read/write helpers, and
rowLevelResolvedPaths) out of the 1862-line ConflictChecker into
RowLevelConcurrencyResolution -- a `self: ConflictChecker =>` trait mixed
into the checker. ConflictChecker keeps only the call sites; `spark` and
`winningCommitSummary` are widened to `protected val`, and the DV helpers
stay `protected` so the OPTIMIZE-vs-DML reconciliation (which mixes into
the same checker) can reuse them.

Also add a concrete worked example to the resolveRowLevelConflicts doc:
winner DELETEs {5,10}, current DELETEs {20,21}, disjoint over base {} ->
merge to {5,10,20,21}; had current deleted row 5, overlap {5} -> abort.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…iled

canSkipAddedFileForRowLevelConcurrency can only return true for a path in
rowLevelResolvedPaths, so when that set is empty the filterNot is a no-op.
Guard on rowLevelResolvedPaths.isEmpty to skip the traversal and its
allocation on the common no-conflict path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the same-file deletion-vector union coverage to MERGE:

- disjoint concurrent MERGE matched-deletes reconcile (DV union, like DELETE)
- a MERGE matched-delete reconciles against a concurrent plain DELETE
  (the union is operation-agnostic)
- overlapping MERGE matched-deletes still conflict
- a MERGE matched-update (winner) writes an image file, so a concurrent
  loser conservatively conflicts, mirroring the standalone-UPDATE case

MERGE inserts / WHEN NOT MATCHED BY SOURCE stay out of scope (they need
per-file row-tracking classification) and are not exercised.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Apply the same review feedback from Case 1 (delta-io#7358) proactively to Case 2:

- Fail-safe fallback: extract the per-file DV read/overlap/merge/write into
  reconcileFileDeletionVectors and wrap the per-path call in try/catch
  (NonFatal). If resolving one shared file throws (unreadable/corrupt DV,
  transient I/O), skip row-level resolution for it and let the standard
  file-level checks abort cleanly with a retryable Concurrent* exception,
  instead of surfacing an unexpected error out of conflict detection. Other
  shared files are still resolved independently.
- Index the current transaction's AddFile/RemoveFile maps in a single pass
  over actions instead of two `.collect{}.toMap` traversals.

No behavior change on the happy/genuine-conflict paths: RowLevelConcurrencySuite
18/18 still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
resolveRowLevelConflicts reconciled each shared file's deletion vectors
sequentially on the caller thread. That per-file work is driver-side
object-store DV I/O (read + merge + write), so when a winning transaction
conflicts on many files the reconciliation window grows with the file
count.

Reconcile each shared file independently on a bounded pool (cf.
DeltaFileOperations footer reads, which use 8); a single conflicting file
stays on the caller thread, so the common case is unchanged. Per-file
results are collected and then applied on the caller thread, so there is
no shared-state race across the pool. The existing per-file NonFatal
fail-safe is preserved (moved into reconcileOnePath): a DV
decode/merge/write failure for one file skips row-level resolution for it
(the standard checks then abort cleanly) without affecting the others.

No behavior change on the happy/genuine-conflict paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…l path

Every existing RowLevelConcurrencySuite conflict is single-file
(sharedPaths.size == 1), which takes the caller-thread branch of
resolveRowLevelConflicts. Add a two-file test: a DELETE whose predicate
spans two data files DV-updates both in one commit, so the loser
reconciles two shared paths at once (parallelism == 2), exercising the
ThreadUtils.parmap branch. parmap preserves input order, so the result
matches the sequential branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rializable read-op tests

Expand reconcileFileDeletionVectors' comment into an explicit induction on the
winner chain: after merging winner k the current txn's base advances to winner
k's DV, so each step's overlap test reduces to the two ops' own new deletes --
precise for any number of winners and independent of winner order.

Add two RowLevelConcurrencySuite tests:
- three disjoint DELETEs reconciling over a non-empty base DV (the deepest chain
  the ordering helpers reach; combines the base-DV and N-way subtleties).
- a Serializable MERGE matched-delete loser (a read-then-modify op) vs a
  concurrent disjoint DELETE, confirming reconciled paths drop from readFiles
  under the stricter isolation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…f guarding each check

Row-level resolution previously threaded a `!rowLevelResolvedPaths.contains(...)`
guard into four separate file-level checks (append, delete-read, whole-table,
delete-delete) in ConflictChecker. Instead, rewrite both sides of the conflict
once in resolveRowLevelConflicts: rebase the current transaction (as before) and
prune the reconciled AddFile(P)/RemoveFile(P) pair from a single effective
`winningCommitSummary` (now a `var`, mirroring `currentTransactionInfo`). The four
checks revert to byte-identical-to-upstream, since they now see a winner that
never touched the reconciled files.

Removes the trait-level mutable `rowLevelResolvedPaths` set and
`canSkipAddedFileForRowLevelConcurrency`; the "don't skip UPDATE image files"
reasoning moves to pruneReconciledFiles (only the same-path pair is pruned).

ConflictChecker footprint drops to a mixin + one `var` + one call site; all
subtle DV logic stays confined to reconcileFileDeletionVectors.

RowLevelConcurrencySuite 21/21, FeatureEnablementConcurrencySuite 40/40.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sezruby

sezruby commented Oct 7, 2026

Copy link
Copy Markdown
Owner Author

Cross-link: the out-of-scope item "MERGE with net-new inserts still conflicts" is now handled by #20 (Case 1 follow-up, opt-in merge.sourceKeyReadPredicate.enabled).

  • MERGE records a source-key read predicate. The value-exact tiers evaluate it as an exact semi-join against the source keys, so a concurrent insert of the same new key aborts and disjoint inserts commit.
  • For disjoint keys in the same file, the write/write side still needs this PR's DV union. With both PRs, MERGE ⟂ MERGE on disjoint keys reconciles like the reference implementation.

seewishnew and others added 4 commits October 9, 2026 19:25
…io#7818)

#### Which Delta project/connector is this regarding?

- [x] Spark

## Description

Retain the loading `KernelContext` when wrapping a Kernel snapshot in
`DeltaV2Snapshot`, and use its default engine for subsequent snapshot
operations. This keeps snapshot operations associated with the table's
context and configured filesystem options instead of constructing an
engine from session defaults alone.

Thread the context through snapshot managers, transactions, and snapshot
copies, and update the affected callers and test fixtures. The snapshot
managers, table, and transaction adapter retain context instead of a
default-engine field. The context creates a default engine when
requested, materializing Hadoop configuration from the active Spark
session plus the table's invariant filesystem options. No per-session
engine cache or credential-provider refresh policy is introduced.

## How was this patch tested?

- `spark/testOnly
org.apache.spark.sql.delta.v2.interop.DeltaV2SnapshotSuite`: all 23
tests passed.
- `sparkV2/Test/compile` passed, including Java formatting and style
checks.
- Negative control: restoring the old lazy getter makes the new
filesystem regression fail with stale session properties; restoring the
fix makes it pass.
- Added regression coverage for context-engine delegation on latest and
historical snapshots and context retention in catalog copies.
- Added a real recording-filesystem regression across sessions A/B/A and
inherited child-thread sessions, exercising latest/historical snapshots,
allFiles, and transaction-version lookup while checking session
properties and table-option precedence.

## Does this PR introduce _any_ user-facing changes?

No changes to released APIs. This fixes engine selection in the
unreleased Spark connector's snapshot facade.

---------

Signed-off-by: Vishnu Chandrashekhar <seewishnew@users.noreply.github.com>
Co-authored-by: Vishnu Chandrashekhar <seewishnew@users.noreply.github.com>
#### Which Delta project/connector is this regarding?

- [x] Spark

## Description

AMT checkpoints currently serialize as an object with separate fields.
Change the checkpoint payload to an array of single-action envelopes, as
specified by the [adaptive metadata
RFC](delta-io#6978 (comment)).

Introduce `AMTCheckpointAction` and `AMTCheckpointSingleAction`, carry
the checkpoint version in `CheckpointMetadata`, and preserve the field
accessors and field-based constructor. Validate that each required
singleton action appears exactly once, allow unknown envelopes, and
preserve action order. Align Jackson serialization and Spark decoding,
including the sidecar `type` field.

## How was this patch tested?

Adds and updates coverage in `AdaptiveMetadataActionsSerializerSuite`,
`ActionSerializerSuite`, and `LastCheckpointInfoSuite` for action
wrapping, JSON round trips, required-action validation, unknown
envelopes, Spark schemas, and decoding with sidecars. Updates AMT
checkpoint, snapshot, inheritance, writer, and field-spec tests for the
new representation.

Whitespace and patch-integrity checks pass. Scala tests have not been
run locally for this revision; CI validation is pending.

## Does this PR introduce _any_ user-facing changes?

The AMT checkpoint JSON payload changes from a field object to an array
of action envelopes, matching the RFC. The checkpoint version is stored
in its `checkpointMetadata` action.

Signed-off-by: Yumingxuan Guo <171533467+yumingxuanguo-db@users.noreply.github.com>
Co-authored-by: Yumingxuan Guo <171533467+yumingxuanguo-db@users.noreply.github.com>
…tries (delta-io#7851)

## Description

Generates `snapshot_id` and `modified_snapshot_id` for data entries
written to adaptive metadata tree (AMT) checkpoints, so each data entry
records the snapshot that added it and the snapshot that last modified
it. This implements the file-modification tracking described in the
protocol RFC delta-io#7820.

Key changes:
- Populate `snapshot_id` / `modified_snapshot_id` on AMT data entries in
`AMTWriteHelper` and `IncrementalAMTWriter`, and carry them through the
AMT action model.
- Propagate the fields through the commit path (`OptimisticTransaction`,
`Checkpoints`, `Snapshot`) and through commands that rewrite or re-add
files (`CloneTableBase`, `RestoreTableCommand`, `TahoeRemoveFileIndex`).

## How was this patch tested?

Added/updated coverage across the AMT suites (`AMTCheckpointWriteSuite`,
`AMTIncrementalWriteInvariantSuite`, `AMTInheritanceSuite`,
`AMTInheritanceReadSuite`, `AMTSnapshotSuite`, `AMTBackReferenceSuite`,
`AMTPartitionValuesSuite`, `AMTSingleActionSerializerSuite`,
`AMTUtilsSuite`) plus `ActionSerializerSuite`, `CheckpointsSuite`, and
`LastCheckpointInfoSuite`.

## Does this PR introduce _any_ user-facing changes?

No.

Co-authored-by: Jinhua Song <jinhua.song@databricks.com>
…elta-io#7852)

## Description

Spark's optimizer can request scan statistics (`estimateSizeInBytes` /
`estimateStatistics`) before physical planning. Previously the Delta V2
scan always selected files to answer these requests, even for unfiltered
scans where the result is already known.

For an unfiltered scan (no data filters, no partition filters, no pushed
limit, not a CDC read), the snapshot checksum already records the exact
size of every file the scan would select. The scan now returns that size
directly before planning, so an early optimizer statistics request no
longer triggers file selection. Physical planning stays lazy and still
selects files exactly once.

Filtered, limit, and CDC scans continue to plan files so Spark receives
their accurate post-pruning size. If the checksum does not record a
table size, the scan falls back to planning rather than reporting an
unknown size.

## How was this patch tested?

Added tests to `DeltaV2ScanTest`:
- Unfiltered scans report statistics without selecting files, and later
physical planning selects files exactly once and reuses that selection.
- Filtered scans and positive-limit scans plan files exactly once and
reuse the selection for repeated statistics requests.
- Tables written without checksums fall back to file planning for the
size estimate.

Existing statistics tests were updated to reflect that full statistics
for unfiltered scans now come from snapshot metadata.

## Does this PR introduce _any_ user-facing changes?

No.
Omar-Saleh and others added 20 commits October 13, 2026 07:19
…ta-io#7847)

<!--
Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, please read our contributor guidelines:
https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md
2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP]
Your PR title ...'.
  3. Be sure to keep the PR description updated to reflect all changes.
  4. Please write your PR title to summarize what this PR proposes.
5. If possible, provide a concise example to reproduce the issue for a
faster review.
6. If applicable, include the corresponding issue number in the PR title
and link it in the body.
-->

#### Which Delta project/connector is this regarding?
<!--
Please add the component selected below to the beginning of the pull
request title
For example: [Spark] Title of my pull request
-->

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description
Stop pushing down non deterministic predicates to CDF nodes, these
predicates will get reevaluated at query level thus potentially
returning inconsistent results.
<!--
- Describe what this PR changes.
- Describe why we need the change.
 
If this PR resolves an issue be sure to include "Resolves #XXX" to
correctly link and close the issue upon merge.
-->

## How was this patch tested?
New UTs
<!--
If tests were added, say they were added here. Please make sure to test
the changes thoroughly including negative and positive cases if
possible.
If the changes were tested in any way other than unit tests, please
clarify how you tested step by step (ideally copy and paste-able, so
that other reviewers can test and check, and descendants can verify in
the future).
If the changes were not tested, please explain why.
-->

## Does this PR introduce _any_ user-facing changes?
No
<!--
If yes, please clarify the previous behavior and the change this PR
proposes - provide the console output, description and/or an example to
show the behavior difference if possible.
If possible, please also clarify if this is a user-facing change
compared to the released Delta Lake versions or within the unreleased
branches such as master.
If no, write 'No'.
-->

---------

Co-authored-by: Omar Elhadidy <omar.elhadidy@databricks.com>
…ed relative DVs in VACUUM (delta-io#7853)

#### Which Delta project/connector is this regarding?

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description

A few more places that identify deletion vectors (DVs) now support
object identity, so that the
same DV blob is recognized whether it is described by a table-relative
(`r`) descriptor or a
UUID-based (`u`) descriptor.

1. `DeletionVectorDescriptor.uniqueFileId` was a lazy val that always
used the legacy
   `storageType + pathOrInlineDv` identity. It is replaced by
`uniqueFileId(tableRoot, useObjectIdentity)`, which mirrors `uniqueId`:
with object identity it
returns the normalized table-relative identity of the DV file, without
the offset. The legacy
value is kept as the private `legacyUniqueFileId`, which
`legacyUniqueId` is built on.
2. `getDeletionVectorRelativePathAndSize`, used by VACUUM, now treats
unencoded relative (`r`)
DVs like UUID-based (`u`) ones and returns their table-relative path.
Before this change, `r`
DVs fell through to the absolute-path branch, which asserts that the DV
path is absolute.
3. Adds a `FileAction.useDeletionVectorObjectIdentity(snapshot, spark)`
overload and uses it in
   the OPTIMIZE check that compares candidate file sets.

## How was this patch tested?

Existing tests. `DeletionVectorDescriptorSuite` and
`AMTCheckpointTestBase` are updated for the
new `uniqueFileId` signature.

## Does this PR introduce _any_ user-facing changes?

No.
…delta-io#7850)

#### Which Delta project/connector is this regarding?

- [ ] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [x] V4 RFC

## Description

Align the adaptive metadata tree RFC's manifest deletion vector encoding
to use the Mumbling bitmap spec in place of Roaring.

## How was this patch tested?

NA

## Does this PR introduce _any_ user-facing changes?

No
…ormat (delta-io#7855)

The AMT manifest deletion vectors (the masking MDV in `manifest_info.dv`
and the CDF `tracking.deleted_positions` / `replaced_positions`) were
backed by a Roaring bitmap. This switches `ManifestBitmap` to an
implementation backed by the immutable Mumbling bitmap format:
`MumblingManifestBitmap` wraps a `MumblingBitmap`, and its mutable
counterpart `MutableMumblingManifestBitmap` wraps a
`MutableMumblingBitmap`, so `ManifestBitmap.fromPositions` /
`fromSerializedByteArray` now produce and consume the Mumbling on-disk
bytes.

It also adds `MumblingRowIndexFilter` and
`MumblingRowIndexFilterProvider`: a `RowIndexFilter` that drops a leaf
row when its row index is contained in the MDV and keeps it otherwise,
probing the bitmap per row (leaf row indices are dense and small,
matching the point-membership shape of the Mumbling bitmap). The
provider is self-contained and serializable so a scan task owns its own
filter.

The test-only position-dump helper (`toArrayForTesting`) is removed; the
manifest-bitmap and AMT incremental-write tests now assert through
`cardinality` / `contains` and exercise all three `RowIndexFilter`
materialization entry points.
#### Which Delta project/connector is this regarding?

- [x] Spark

## Description

Convert `DeltaV2Table` from Java to Scala. Preserve its Java-facing
collection and optional
interfaces, synchronized schema initialization, and
`KernelContext`-backed engine lookup. Constants and static helpers move
to a Scala companion.
Existing tests remain unchanged.

Compatibility notes:

- Scala `protected` methods are JVM-public. Java subclasses that
override `protocol()`,
`metadata()`, `kernelEngine()`, `initialSnapshot()`, or `catalogTable()`
with protected
  visibility must widen their overrides to public when recompiling.
- Helper visibility and reflection metadata differ from the Java
implementation, including
  the public static forwarder for `getDecodedPath`.
- Scala introduces some tuple and callback allocations; performance
impact is unmeasured.

## How was this patch tested?

Existing tests are unchanged. Local tests have not been run in this
checkout; CI validation
is pending.

## Does this PR introduce _any_ user-facing changes?

No intended SQL behavior change. Java subclass and reflection
compatibility differences are
listed above.

Signed-off-by: Hao Sun <86338940+HaoSun1993@users.noreply.github.com>
…rop dv_cardinality (delta-io#7816)

<!--
Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, please read our contributor guidelines:
https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md
2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP]
Your PR title ...'.
  3. Be sure to keep the PR description updated to reflect all changes.
  4. Please write your PR title to summarize what this PR proposes.
5. If possible, provide a concise example to reproduce the issue for a
faster review.
6. If applicable, include the corresponding issue number in the PR title
and link it in the body.
-->

#### Which Delta project/connector is this regarding?
<!--
Please add the component selected below to the beginning of the pull
request title
For example: [Spark] Title of my pull request
-->

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description


Renumber the AMT V4 manifest_info file/row count field IDs to match the
spec PR:
  replaced_files_count 520 -> 523
  modified_files_count 524 -> 525
  replaced_rows_count  521 -> 524
  modified_rows_count  525 -> 526

Separate DV cardinality Field id 523 is dropped now, readers have to
read the cardinality from the bitmap itself. This field ID is now used
for replaced_files_count. dv/dv_cardinality consistency invariant is now
moot since there's only a single place so that logic is all dropped.

format_version moves from top level field 157 into manifest_info as
field 521, matching the spec PR.

## How was this patch tested?

Existing unit tests, dropped no longer relevant assertions/tests for the
fields that were removed.

## Does this PR introduce _any_ user-facing changes?

<!--
If yes, please clarify the previous behavior and the change this PR
proposes - provide the console output, description and/or an example to
show the behavior difference if possible.
If possible, please also clarify if this is a user-facing change
compared to the released Delta Lake versions or within the unreleased
branches such as master.
If no, write 'No'.
-->

---------

Co-authored-by: Isaac <no-reply@databricks.com>
## Description

Use `snapshotAtSourceInit` to provide table tags for four existing
streaming source events: the unsafe column-mapping read warning, missing
trailing commits, schema changes, and the `ignoreFileDeletion`
deprecation warning.

This keeps event attribution tied to the snapshot the source was
initialized to read instead of the latest snapshot cached by `DeltaLog`.
Event names, payloads, exception handling, and streaming behavior are
unchanged. No additional snapshot fetches are introduced.

## Why this is safe

`recordDeltaEvent` uses its provider only for table-context tags.
Snapshots delegate to the same `DeltaLog.getCommonTags` helper; only the
source of the table identifier tag changes. This affects attribution,
not logging mechanics or streaming decisions.

The table identifier is the table's `metadata.id`, not its version.
Ordinary data writes and schema updates preserve it. Table replacement
or drop-and-recreation can create a new table identity and change the
ID, even at the same path.

- **Source construction:** `snapshotAtSourceInit` is the snapshot used
to construct the source. Initialization and deprecation warnings should
identify that source, regardless of later table changes.
- **Commit-processing diagnostics:** events describe the existing source
and should carry its pinned `tableId`, obtained from
`snapshotAtSourceInit.metadata.id`. When commit metadata validation
runs, an ID mismatch fails before that commit's files are processed;
schema-change logging also follows the ID check. Existing ID and
schema-tracking checks remain unchanged.

## Testing

- `git diff --check`
- Verified that `SnapshotDescriptor` implements `DeltaLoggingProvider`,
the provider type accepted by `recordDeltaEvent`.
- Builds and tests have not been run locally.
#### Which Delta project/connector is this regarding?

- [x] Spark

## Description

Decode CDC actions from Kernel commit batches into `AddCDCFile` actions,
preserving the path, partition values, size, and optional tags. These
actions previously caused an `UnsupportedOperationException` during
commit-batch conversion.

Rename the shared string-map converter to reflect its use for both
partition values and tags.

## How was this patch tested?

Added coverage in `KernelActionUtilsSuite` for two CDC cases in one test
method: an action without tags and an action with all supported fields
populated. Local tests have not been run in this checkout; CI validation
is pending.

## Does this PR introduce _any_ user-facing changes?

Kernel commit-batch conversion now supports CDC actions.

Signed-off-by: Hao Sun <86338940+HaoSun1993@users.noreply.github.com>
…elta-io#7848)

## Description

Update CDF schema change check to reject partition column changes, which
can otherwise result in `null` values in older versions for columns
upgraded to a partition column.

This also adds logging for incompatible schema changes encountered
during CDF reads.

## How was this patch tested?

Added tests in `CDCReaderSuite` (`CDCSchemaChangeTests`) that cover:
- A partition column change, with the check disabled (the usage event is
logged and the read succeeds) and enabled (the read fails).
- An intermediate partition change.
- A partition change followed by `RESTORE`.
- Additional test coverage for other schema changes: column dropped,
renamed, or changes type after the start version.

Co-authored-by: Isaac <no-reply@databricks.com>
…ta-io#7860)

#### Which Delta project/connector is this regarding?

  - [x] Spark
  - [ ] Standalone
  - [ ] Flink
  - [ ] Kernel
  - [ ] Other

  ## Description

  Converts `TableNotFoundException` to Scala as part of the V2
  connector migration. Preserves its constructor, getter, error
  message, subclassability, and field initialization order.

  The path field initializes after the superclass constructor so a
  getter called from an overridden `fillInStackTrace()` sees the
  same default value as before.

  ## How was this patch tested?

  No new test cases. Validation in this repository is pending;
  existing tests will exercise the converted class through its
  existing callers.

  ## Does this PR introduce any user-facing changes?

  No.

Signed-off-by: Rakesh Veeramacheneni <rakesh.veeramacheneni@databricks.com>
…or classes (delta-io#7618)

<!--
Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, please read our contributor guidelines:
https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md
2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP]
Your PR title ...'.
  3. Be sure to keep the PR description updated to reflect all changes.
  4. Please write your PR title to summarize what this PR proposes.
5. If possible, provide a concise example to reproduce the issue for a
faster review.
6. If applicable, include the corresponding issue number in the PR title
and link it in the body.
-->

#### Which Delta project/connector is this regarding?
<!--
Please add the component selected below to the beginning of the pull
request title
For example: [Spark] Title of my pull request
-->

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description
This PR is the first out of a stack of seven PRs in total. 
The combined stack adds support for the Concurrent Identity Column (CIC)
feature following the RFC
[here](delta-io#7272).
The TLDR for this RFC/feature: It allows for two concurrent 'inserters'
to both succeed by adding an identity sequence service that hands out
disjoint ranges of id values. This way the two writers do not conflict
and can both commit.

**_Stack overview:_** 
1. Foundation <- this PR
    (delta-io#7618)
2. Service abstraction + In-Memory one
    (delta-io#7619)
3. Transport and buffering for the service requests 
    (delta-io#7620)
4. Table feature and the insert path
    (delta-io#7621)
5. DDL integration 
    (delta-io#7622)
6. Merge support
    (delta-io#7623)
7. UC Client addition
    (delta-io#7785) 


This PR introduces no behaviour changes, just scaffolding. It adds:
- `ConcurrentIdentityColumnSchema`: the
`delta.identity.concurrent.sequenceId` column-metadata key that marks a
column as a CIC.
- `ConcurrentIdentityColumnErrors` + 8
DELTA_CONCURRENT_IDENTITY_COLUMN_* error classes (disabled,
too-many-columns, sequence-not-found, metadata-mismatch,
empty-reserve-range, service-timeout, conversion-incomplete,
using-wrong-generator).
- Config
`spark.databricks.delta.identityColumn.concurrent.maxColumnsPerTable`

## How was this patch tested?

<!--
If tests were added, say they were added here. Please make sure to test
the changes thoroughly including negative and positive cases if
possible.
If the changes were tested in any way other than unit tests, please
clarify how you tested step by step (ideally copy and paste-able, so
that other reviewers can test and check, and descendants can verify in
the future).
If the changes were not tested, please explain why.
-->
The ConcurrentIdentityColumnErrorsSuite adds three tests for the
`EMPTY_RESERVE_RANGE`, `METADATA_MISMATCH`, and `USING_WRONG_GENERATOR`
errors. The tests are only added for these errors because these errors
can not occur 'naturally' in the code and are therefore tested this way.


## Does this PR introduce _any_ user-facing changes?
<!--
If yes, please clarify the previous behavior and the change this PR
proposes - provide the console output, description and/or an example to
show the behavior difference if possible.
If possible, please also clarify if this is a user-facing change
compared to the released Delta Lake versions or within the unreleased
branches such as master.
If no, write 'No'.
-->
No

---------

Signed-off-by: Robert Weeke <weeke.robert@gmail.com>
Co-authored-by: mkroll-db <marco.kroll@databricks.com>
…taSQLCommandTest (delta-io#7863)

#### Which Delta project/connector is this regarding?

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description

In `DeleteSuiteBase`, replaces the `SharedSparkSession` test util with
`DeltaSQLCommandTest` as the latter is the 'delta wrapper' of the
former, to slightly decouple from Spark OSS.
Also replaces `withSQLConf` with the more generic `withConf` to set
confs via public APIs.

## How was this patch tested?

test-only change.

## Does this PR introduce _any_ user-facing changes?

No

Co-authored-by: Matthis Gördel <matthis.goerdel@databricks.com>
…ta-io#7854)

<!--
Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, please read our contributor guidelines:
https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md
2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP]
Your PR title ...'.
  3. Be sure to keep the PR description updated to reflect all changes.
  4. Please write your PR title to summarize what this PR proposes.
5. If possible, provide a concise example to reproduce the issue for a
faster review.
6. If applicable, include the corresponding issue number in the PR title
and link it in the body.
-->

#### Which Delta project/connector is this regarding?
<!--
Please add the component selected below to the beginning of the pull
request title
For example: [Spark] Title of my pull request
-->

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description

<!--
- Describe what this PR changes.
- Describe why we need the change.
 
If this PR resolves an issue be sure to include "Resolves #XXX" to
correctly link and close the issue upon merge.
-->

Problem:

Widening file statistics through the shared
AddFile.withoutTightBoundStats helper can round DECIMAL bounds inward,
causing data skipping to discard matching rows. The SQL below
demonstrates this through DELETE rebasing over a concurrent OPTIMIZE;
the fix also affects other callers of the shared helper.

```
CREATE TABLE t (id BIGINT, amount DECIMAL(18, 2)) USING DELTA
TBLPROPERTIES ('delta.enableDeletionVectors' = 'true',
               'delta.autoOptimize.autoCompact' = 'false');

INSERT INTO t
SELECT /*+ COALESCE(1) */ * FROM VALUES
  (1L, 0.64), (2L, 3647609576981584.64), (3L, 1.00),
  (4L, 2.00), (5L, -3647609576981584.64) AS test_data(id, amount);
DELETE FROM t WHERE id = 1;
INSERT INTO t VALUES (6L, 3.00);

-- Session 1: the test harness pauses this DELETE before commit.
DELETE FROM t WHERE id = 3;
-- Session 2: OPTIMIZE commits while Session 1 is paused.
OPTIMIZE t;
-- Resume Session 1 and let DELETE commit before running these queries.
-- With the fix disabled and data skipping enabled, both incorrectly return no rows.
SELECT id, amount FROM t WHERE amount = -3647609576981584.64; -- Expected: id=5
SELECT id, amount FROM t WHERE amount = 3647609576981584.64;  -- Expected: id=2
```

Root cause:

AddFile.withoutTightBoundStats parses fractional numbers as Double in
its JSON-tree rewrite while setting tightBounds=false. In the example,
DV conflict rebasing reaches this helper through
removeRows(updateStats=true). The amount bounds round inward from
[-3647609576981584.64, 3647609576981584.64] to [-3647609576981584.50,
3647609576981584.50]. Both surviving endpoints then fall outside the
recorded bounds, so data skipping discards their file. The precision
loss is in the shared helper and can affect any caller that rewrites
numeric bounds.

Fix:

Reuse Jackson's `copyCurrentEventExact` in `withoutTightBoundStats` to
preserve numeric precision while replacing tightBounds.


## How was this patch tested?

<!--
If tests were added, say they were added here. Please make sure to test
the changes thoroughly including negative and positive cases if
possible.
If the changes were tested in any way other than unit tests, please
clarify how you tested step by step (ideally copy and paste-able, so
that other reviewers can test and check, and descendants can verify in
the future).
If the changes were not tested, please explain why.
-->
UT
## Does this PR introduce _any_ user-facing changes?

<!--
If yes, please clarify the previous behavior and the change this PR
proposes - provide the console output, description and/or an example to
show the behavior difference if possible.
If possible, please also clarify if this is a user-facing change
compared to the released Delta Lake versions or within the unreleased
branches such as master.
If no, write 'No'.
-->
No

Signed-off-by: zhipeng.mao <zhipeng.mao@databricks.com>
…LCommandTest (delta-io#7864)

#### Which Delta project/connector is this regarding?

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description

Replaces `SharedSparkSession` with `DeltaSQLCommandTest`, which is a
'delta wrapper' of the former.
Also replaces `withSQLConf` with the more generic `withConf`.

## How was this patch tested?

test-only change.

## Does this PR introduce _any_ user-facing changes?

No

Co-authored-by: Matthis Gördel <matthis.goerdel@databricks.com>
…ta-io#7865)

#### Which Delta project/connector is this regarding?

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description

`delta.inCommitTimestampEnablementVersion` and
`delta.inCommitTimestampEnablementTimestamp` record the version and
timestamp at which In-Commit Timestamps (ICT) were enabled on a table.
They are written by the commit that enables ICT and describe that
table's log. A user-specified value, for example one copied from another
table, can point to a version that is not in the table's log. That
breaks later commits and ICT-based time travel.

This PR:
- Marks both properties as not user-configurable, so CREATE, REPLACE,
CTAS, RTAS, ALTER TABLE SET TBLPROPERTIES, DataFrameWriter options,
session defaults and the `DeltaTable` builder API fail with
`DELTA_CANNOT_MODIFY_TABLE_PROPERTY`.
- Stops CREATE TABLE LIKE and CLONE from copying them from the source
table. The target still gets `delta.enableInCommitTimestamps` from the
source, and the commit derives the target's own enablement info when
needed.
- Adds `InCommitTimestampUtils.ENABLEMENT_INFO_PROPERTY_KEYS` so the
filtering and the tests share one list of keys.

## How was this patch tested?

New tests in `InCommitTimestampSuite`:
- For each property, setting it fails through CREATE/REPLACE/CTAS/RTAS
TBLPROPERTIES, ALTER TABLE SET TBLPROPERTIES, DataFrameWriter options,
session defaults and the `DeltaTable` builder API.
- CREATE TABLE LIKE from a table whose ICT enablement info is set
produces a target that has ICT enabled but no copied enablement info.

## Does this PR introduce _any_ user-facing changes?

Yes. Setting `delta.inCommitTimestampEnablementVersion` or
`delta.inCommitTimestampEnablementTimestamp` now fails with
`DELTA_CANNOT_MODIFY_TABLE_PROPERTY`; before, the value was accepted.
CREATE TABLE LIKE and CLONE no longer copy these properties from the
source table. Tables that already have these properties are unaffected.

This pull request and its description were written by Isaac.

---------

Co-authored-by: Murali Ramanujam <murali.ramanujam@databricks.com>
… DeltaV2Table (delta-io#7866)

<!--
Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, please read our contributor guidelines:
https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md
2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP]
Your PR title ...'.
  3. Be sure to keep the PR description updated to reflect all changes.
  4. Please write your PR title to summarize what this PR proposes.
5. If possible, provide a concise example to reproduce the issue for a
faster review.
6. If applicable, include the corresponding issue number in the PR title
and link it in the body.
-->

#### Which Delta project/connector is this regarding?
<!--
Please add the component selected below to the beginning of the pull
request title
For example: [Spark] Title of my pull request
-->

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description

DeltaV2Table` built its identity partition transforms with
`Expressions.identity(String)`, which re-parses each partition column
name as a SQL multipart identifier. Partition column names are exact
top-level field names, so this goes wrong for names that need quoting:

- A dotted name such as `customer.id` silently becomes the two-part
reference `[customer, id]`. Consumers that expect one name part per
identity partition column (for example
`DataSourceV2Relation.partitionColumns`, which takes
`fieldNames().head`) see the wrong column.
- Names with spaces or backticks, which column mapping allows (for
example `order date`), throw a `ParseException` as soon as
`partitioning()` is called.

This PR builds each transform from `FieldReference.column(name)`, a
single exact name part. Partition-column order, case, and column-mapping
logical names are unchanged.

<!--
- Describe what this PR changes.
- Describe why we need the change.
 
If this PR resolves an issue be sure to include "Resolves #XXX" to
correctly link and close the issue upon merge.
-->

## How was this patch tested?

CI and new tests

<!--
If tests were added, say they were added here. Please make sure to test
the changes thoroughly including negative and positive cases if
possible.
If the changes were tested in any way other than unit tests, please
clarify how you tested step by step (ideally copy and paste-able, so
that other reviewers can test and check, and descendants can verify in
the future).
If the changes were not tested, please explain why.
-->

## Does this PR introduce _any_ user-facing changes?

<!--
If yes, please clarify the previous behavior and the change this PR
proposes - provide the console output, description and/or an example to
show the behavior difference if possible.
If possible, please also clarify if this is a user-facing change
compared to the released Delta Lake versions or within the unreleased
branches such as master.
If no, write 'No'.
-->
No
#### Which Delta project/connector is this regarding?

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description

Adds `DeleteAMT` and `RowTrackingDeleteAMT` dimensions to the
delta-suite-generator, plus a generated `deleteamt` group that runs the
DELETE tests against AMT (`adaptiveMetadata-preview`) tables, following
the UPDATE AMT suites.

- `DeleteAMTMixin` brackets each `executeDelete` with the
`AMTDMLTestUtils` checkpoints. `RowTrackingDeleteAMTMixin` brackets each
test table's body instead, since its row tracking checks inspect the
latest commit.
- AMT requires deletion vectors, so `AMTDMLTestUtils` now unsets a
suite's `delta.enableDeletionVectors = false` default for new tables for
all AMT DML suites (moved from the UPDATE AMT base), and keeps DELETE,
UPDATE and MERGE from writing deletion vectors where the tests assert
file rewrites.
- The DELETE schema pruning tests only collect scans over a
`TahoeFileIndex`, so they skip the checkpoint's tree read.
- Excludes the tests the row tracking and deletion vector DELETE suites
already exclude, and the row tracking tests that create tables with row
tracking disabled, which AMT rejects. `RowTrackingDeleteDvBase` is not
generated, since its only test disables deletion vectors on new tables.

## How was this patch tested?

Test-only change. The new generated `deleteamt` suites run the existing
DELETE tests on AMT tables.

## Does this PR introduce _any_ user-facing changes?

No.
…a-io#7870)

<!--
Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, please read our contributor guidelines:
https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md
2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP]
Your PR title ...'.
  3. Be sure to keep the PR description updated to reflect all changes.
  4. Please write your PR title to summarize what this PR proposes.
5. If possible, provide a concise example to reproduce the issue for a
faster review.
6. If applicable, include the corresponding issue number in the PR title
and link it in the body.
-->

#### Which Delta project/connector is this regarding?
<!--
Please add the component selected below to the beginning of the pull
request title
For example: [Spark] Title of my pull request
-->

- [x] Spark
- [ ] Standalone
- [ ] Flink
- [ ] Kernel
- [ ] Other (fill in here)

## Description

The V1 read path uses a LIMIT to prune files only when
DeltaSQLConf.DELTA_LIMIT_PUSHDOWN_ENABLED is on, but
DeltaV2ScanBuilder.pushLimit always accepted the limit, so the V2
connector ignored the config.

pushLimit now declines the limit and records nothing when the config is
off, so Spark applies its own LIMIT to an unlimited scan. Nothing
changes when the config is on, which is the default.

<!--
- Describe what this PR changes.
- Describe why we need the change.
 
If this PR resolves an issue be sure to include "Resolves #XXX" to
correctly link and close the issue upon merge.
-->

## How was this patch tested?

Added new tests

<!--
If tests were added, say they were added here. Please make sure to test
the changes thoroughly including negative and positive cases if
possible.
If the changes were tested in any way other than unit tests, please
clarify how you tested step by step (ideally copy and paste-able, so
that other reviewers can test and check, and descendants can verify in
the future).
If the changes were not tested, please explain why.
-->

## Does this PR introduce _any_ user-facing changes?

<!--
If yes, please clarify the previous behavior and the change this PR
proposes - provide the console output, description and/or an example to
show the behavior difference if possible.
If possible, please also clarify if this is a user-facing change
compared to the released Delta Lake versions or within the unreleased
branches such as master.
If no, write 'No'.
-->
No
Reconciling a shared file hides the winner's deletes in it from the
delete-read check. Require that none of the rows the winner newly deleted
match the current transaction's read predicates (one signed-weight scan of
the base-DV and winner-DV images), refuse whole-table reads, and accept a
MERGE without a scan only when it has no NOT MATCHED clause or has an
unconditional MATCHED clause. Previously a MERGE with a conditional matched
clause and an insert could commit after a winner deleted a row it matched
but left alone, skipping the insert that row's deletion requires.

Merged DV files are now written only once the whole resolution is known to
succeed, and the deletionVectorsMerged event is recorded after all checks
against the winning commit pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cy-dv-poc

Adapt to the onDiskWithRelativePath -> onDiskWithUuidRelativePath rename (delta-io#7473).
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.