Repository navigation
Conversation
… conflicts Conflict detection filters candidate files by partition only; for unpartitioned (incl. liquid-clustered) tables the append check conflicts on any concurrently-added file. This adds column-statistics data skipping: added files whose stats prove they cannot match the current transaction's read predicates are excluded, so operations touching disjoint data ranges no longer falsely conflict. - DataSkippingReaderBase.buildDataSkippingPredicate + filterFilesByDataSkipping reuse the reader's DataFiltersBuilder / verifyStatsForFilter and the shared parseAndDecodeStats, applying 'expr || !verifyStatsForFilter(...)' for one-way safety (skip only when stats prove no match; missing stats keep the file). - ConflictChecker.getFirstFileMatchingPartitionPredicates applies skipping per read predicate and unions survivors (OR across reads; a read's own data predicates are ANDed), and never skips for a whole-table read. Emits a delta.conflictDetection.dataSkipping.filesSkipped event. - Gated by spark.databricks.delta.conflictDetection.dataSkipping.enabled (internal, default off). - Adds ConflictDataSkippingSuite (disjoint / overlapping / disabled / missing-stats / partitioned). Part of #3. Implements #4. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sezruby
force-pushed
the
conflict-data-skipping
branch
from
July 8, 2026 20:38
cacf70d to
a2ad862
Compare
1 of 6 tasks
This was referenced Jul 30, 2026
sezruby
added a commit
that referenced
this pull request
Jul 30, 2026
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>
Fix a Serializable soundness gap in reader-side conflict-time data skipping. A non-blind-append transaction can record no read predicates (e.g. it removes files without reading the table). With skipping enabled its empty read set produced an empty survivor set, so getFirstFileMatchingPartitionPredicates returned None and a real ConcurrentAppendException was silently suppressed. Skipping is now applied only when the transaction has at least one read predicate; otherwise every concurrently added file stays a conflict candidate, matching behavior with the feature disabled. Add ConflictDataSkippingSuite coverage: - empty read predicates on a non-blind-append txn still conflict (regression test for the fix above); - a whole-table read is never skipped and conflicts with a concurrent append; - filterFilesByDataSkipping AND-combines predicates within a single read. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Combine per-read data-skipping into one Spark job and fail safe on evaluation errors, per review comments on delta-io#7358. - filterFilesMatchingAnyReadPredicate(files, dataFiltersPerRead): builds one skipping predicate per read and OR-combines them into a single `where`, so all read predicates are evaluated in one Spark job instead of one job per read. A read with no usable predicate matches everything -> short-circuit and keep all files, no job. - filterFilesByDataSkipping is now the single-read case delegating to it. - Wrap build+eval in try/catch(NonFatal): on failure log a warning and return all candidate files (default conflict behavior), so a skipping error can never abort a valid commit. - ConflictChecker calls the new method once instead of flatMap-per-read. - Test: OR-combines independent reads in one call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move buildDataSkippingPredicate / filterFilesByDataSkipping / filterFilesMatchingAnyReadPredicate out of DataSkippingReaderBase into a new self-typed trait ConflictDataSkippingReader (self: DataSkippingReaderBase), mixed into the base. Existing data-skipping code is left untouched: withStatsInternal0 is reverted to its original inline form, so the only change to DataSkippingReader.scala is the one-line trait mixin. parseAndDecodeStats is duplicated (trait-private) rather than shared with withStatsInternal0, keeping the feature a fully isolated additive unit per review feedback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…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.
…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
… probe Fuse the conflict-time data-skipping filter into the per-read partition probe so a file conflicts iff it could match (P_i AND D_i) for some read, instead of the looser (P_1 OR P_2) AND (D_1 OR D_2). Partition pruning now runs first and no extra Spark job is launched. Move the evaluation fallback into ConflictChecker and record it as a delta event; record the noConflict event only after all checks against the winning commit pass. Add multi-read, null-stats, and event tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # spark/src/main/scala/org/apache/spark/sql/delta/ConflictChecker.scala
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, report on commit - Check whether a skipping condition can be built before constructing any DataFrame; an unpartitioned check with nothing to skip returns files.headOption directly. - Evaluate candidates in bounded batches (internal conf conflictDetection.dataSkipping.batchSize, default 1000) and stop at the first batch holding a conflicting file: a filter over a local relation is evaluated over every row before the limit applies. - Group reads by their partition predicate after widening. - Report delta.conflictDetection.dataSkipping.applied once, from OptimisticTransaction, when the transaction commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Eunjin Song <sezruby@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the row-level-concurrency umbrella (#3) — Case 1. Full design and rationale: #4.
Summary
Reader-side conflict-time data skipping. Conflict detection currently filters candidate files by partition only. On an unpartitioned table — which includes every liquid-clustered table —
getFirstFileMatchingPartitionPredicatesreturnsfiles.headOption, so the append check conflicts on any concurrently-added file, even when the two operations touch entirely disjoint data ranges. This is the dominant source of false conflicts for liquid clustering, where incremental clustering and DML usually touch different clustering-key regions.This teaches conflict detection to apply the reader's column-statistics data skipping: an added file whose stats prove it cannot match the current transaction's data predicates is excluded from the conflict check.
What it does
DataSkippingReaderBase.filterFilesByDataSkipping(files, dataFilters)— reuses the reader'sDataFiltersBuilder+verifyStatsForFilterto build the skipping predicate, parses each file's stats against the snapshot'sstatsSchema, and appliesexpr || !verifyStatsForFilter(...).ConflictChecker.getFirstFileMatchingPartitionPredicatesconsumes the already-capturedDeltaTableReadPredicate.dataPredicates(previously ignored). Skipping is applied per read predicate with the survivors unioned (OR across independent reads; a read's own predicates are ANDed), and is not applied for a whole-table read — so it can only ever narrow, never miss, a conflict. Emits adelta.conflictDetection.dataSkipping.filesSkippedevent.spark.databricks.delta.conflictDetection.dataSkipping.enabled(internal, default off). No protocol / reader / action changes.Which operations benefit (not just append)
The improved check is the added-files (append) check, which fires whenever the winner adds data files — so it is not only INSERT.
UPDATE/MERGEadd new row-image files, and because those are non-blind changed-data files they are checked under the default WriteSerializable (a blind INSERT is only checked under Serializable). So the headline beneficiaries are concurrent UPDATE / DELETE / MERGE on disjoint clustering ranges (different files), under the default isolation.This complements the same-file DV merge (#5), splitting the space by which check fires:
Roles: the loser supplies the read predicate (
WHERE/ MERGEON); the winner supplies the added files whose stats are pruned. (A DV-DELETE winner re-adds the same file, so that's #5's same-file case, not this PR's.)One-way safety
Data skipping here is sound in one direction only — a file is skipped only when its stats prove no overlap; a wrongly-skipped file would be a missed conflict. Guaranteed by reusing the reader's machinery:
expr || !verifyStatsForFilter(referencedStats)keeps any file whose referenced stats are missing/NULL.tightBounds=false) stats are safe:updateStatsToWideBoundskeeps min/max as valid outer bounds (a superset of live values), so wide bounds only cause fewer skips, never an unsafe one — the same property the reader uses to skip DV'd files. The effect on DV-heavy files is reduced benefit (conservative keep), not incorrectness.filesForScan).Correct across clusters/JVMs by construction — conflict detection is log-based: the loser reads the winner's committed stats from
_delta_logand evaluates skipping against its own local predicates (no shared state; deterministic; other-engine/no-stats writers are kept).Supersedes #5's op-type append-suppression
The #2 POC (issue #5) shortcut the concurrent-
UPDATEcase with a blanket append-suppression for rewrite-only DML winners — skipping all their new image files in the append check, keyed only on the winner's op type. That is unsound: anUPDATEcan flip a row into the loser's predicate (winnerSET x=15, loserDELETE WHERE x>10, row wasx=5), a genuine write-skew the suppression hides. Those image files are ordinary non-blind changed-data files and are correctly arbitrated by this PR's stats-based skip — abort on stats overlap (real flip), reconcile only on proven no-overlap, keep on missing stats. #5 drops the suppression and depends on this PR; they stack (this data skipping under the DV merge).Testing
ConflictDataSkippingSuite(new), phase-locking harness on a Serializable table (10 files,idin [0,1000)):DELETE id<50vs append[1000,1100)) → added file skipped, both commit.DELETE id>=950vs append[1000,1100)) → still conflicts (ConcurrentAppendException).dataSkippingNumIndexedCols=0) → disjoint ranges still conflict (one-way-safety invariant).With the flag off,
getFirstFileMatchingPartitionPredicatesis equivalent to before (candidateFiles = files), so no default-path regression.🤖 Generated with Claude Code