Skip to content

Fix crash on skip index over a column with pending ALTER MODIFY COLUMN type change - #106988

Open
groeneai wants to merge 10 commits into
ClickHouse:masterfrom
groeneai:fix-skip-index-alter-modify-column-nullable-snapshot
Open

groeneai wants to merge 10 commits into
ClickHouse:masterfrom
groeneai:fix-skip-index-alter-modify-column-nullable-snapshot

Conversation

@groeneai

@groeneai groeneai commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Changelog category (leave one):

  • Bug Fix (user-visible misbehavior in an official stable release)

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Fix a LOGICAL_ERROR exception ("Sizes of nested column and null map ... are not equal after deserialization") when querying a table with a skip index over a column that has a pending ALTER MODIFY COLUMN type change while apply_mutations_on_fly and apply_patch_parts are both disabled.

Description

A pending ALTER TABLE ... MODIFY COLUMN leaves old parts with skip-index data serialized using the old column type. MergeTreeDataSelectExecutor::canUseIndex skips such an index for a part by checking the column against AlterConversions::getAllUpdatedColumns(), populated from the read snapshot's READ_COLUMN mutations.

createStorageSnapshot built that snapshot with need_alter_mutations = apply_mutations_on_fly || apply_patch_parts. With both off the pending mutation is dropped, the incompatible index is not excluded, and filterMarksUsingIndex deserializes an old granule with the new type. For String to Nullable(UInt64) that raises Sizes of nested column and null map of Nullable column are not equal after deserialization (an exception in release builds; debug and sanitizer builds abort). The implicit count() projection reaches the index read during planning, so a plain SELECT count() is enough.

Neither setting relates to column type changes, so the snapshot now requests alter mutations whenever one is pending. The request is need_alter_mutations_if_pending, resolved against num_alter inside getMutationsSnapshot, which already holds the lock guarding the counters and already reads them for its fast-path condition. A read with no pending alter still returns the empty snapshot without scanning current_mutations_by_version, and adds no lock acquisition.

Column data reads were already correct: IMergeTreeReader converts from the part's own stored type. The supportsSkipIndexesOnDataRead guard covers the data-read phase but not this planning-time path; both are pinned by the tests. Found by serverfuzz running 03702_alter_column_modify_secondary_index_rebuild.sh.

Repro:

CREATE TABLE tg (id UInt64, value String, INDEX idx_set (value) TYPE set(0) GRANULARITY 1)
ENGINE = MergeTree() ORDER BY id PARTITION BY id
SETTINGS add_minmax_index_for_numeric_columns = 0, min_bytes_for_wide_part = 0;
INSERT INTO tg VALUES (1, '10'), (2, '20'), (3, '300');
SYSTEM STOP MERGES tg;
ALTER TABLE tg MODIFY COLUMN value Nullable(UInt64) SETTINGS alter_sync = 0, mutations_sync = 0;
SELECT count() FROM tg WHERE value = 300 SETTINGS apply_mutations_on_fly = 0, apply_patch_parts = 0;

Related: #112213 (this fixes the pending-mutation half; the killed-mutation half is #112484).

…alysis

A pending ALTER MODIFY COLUMN that changes a column type leaves old parts
with on-disk skip-index data serialized using the old type. Index analysis
(MergeTreeDataSelectExecutor::canUseIndex) skips such an index for a part by
checking the column against AlterConversions::getAllUpdatedColumns(), which is
populated from the read snapshot's READ_COLUMN (alter) mutations.

createStorageSnapshot built the read snapshot with
  need_alter_mutations = apply_mutations_on_fly || apply_patch_parts
so when both settings were off the pending READ_COLUMN mutation was dropped
from the snapshot, getAllUpdatedColumns() returned empty, canUseIndex did not
exclude the type-incompatible index, and filterMarksUsingIndex deserialized the
old index granule with the new type. For a String to Nullable(UInt64) change
this aborted the server with a LOGICAL_ERROR:
"Sizes of nested column and null map of Nullable column are not equal after
deserialization". The implicit count() projection path
(optimizeUseAggregateProjections) reaches the index read during planning, so
even an EXPLAIN or a plain SELECT count() crashes.

apply_mutations_on_fly (UPDATE/DELETE on the fly) and apply_patch_parts
(lightweight updates) are unrelated to column type changes. Visibility of
pending column type changes is required for correct skip-index analysis
regardless of those flags, so the read snapshot now always requests alter
mutations. Column data reads were already correct: IMergeTreeReader converts
to the requested type from the part's own stored type, independent of the
snapshot.

The change only widens what the default path already did (apply_patch_parts
defaults to true, which already set need_alter_mutations), so it has no effect
on the common case; it only fixes the both-off case that serverfuzz exercised.

This is the same crash family addressed by the data-read-phase guard in
supportsSkipIndexesOnDataRead; that guard does not cover the primary-key
analysis phase, which relies on canUseIndex.

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

Copy link
Copy Markdown
Collaborator Author

Pre-PR validation gate

# Question Answer
a Deterministic repro? Yes. CREATE TABLE tg (id UInt64, value String, INDEX idx_set (value) TYPE set(0) GRANULARITY 1) ENGINE=MergeTree() ORDER BY id PARTITION BY id SETTINGS add_minmax_index_for_numeric_columns=0, min_bytes_for_wide_part=0; INSERT ... (3,'300'); SYSTEM STOP MERGES; ALTER TABLE tg MODIFY COLUMN value Nullable(UInt64) (alter_sync=0, mutations_sync=0); SELECT count() FROM tg WHERE value=300 SETTINGS apply_mutations_on_fly=0, apply_patch_parts=0 aborts the server every time on master HEAD (debug).
b Root cause explained? Yes. createStorageSnapshot set need_alter_mutations = apply_mutations_on_fly || apply_patch_parts. Both off => the pending READ_COLUMN alter mutation is filtered out of the read snapshot (addSupportedCommands) => AlterConversions::getAllUpdatedColumns() is empty => canUseIndex does not exclude the type-incompatible skip index => filterMarksUsingIndex deserializes the old String-serialized SET-index granule as Nullable(UInt64) => null map size 1, nested size 0 => LOGICAL_ERROR abort. Reached during planning via the implicit count() projection (optimizeUseAggregateProjections -> selectRangesToRead).
c Fix matches root cause? Yes. The fix makes the read snapshot always request alter mutations (need_alter_mutations = true), so pending column type changes are visible to skip-index analysis regardless of the unrelated on-fly apply flags. It targets the exact mechanism in (b), not the symptom.
d Test intent preserved / new tests added? New regression test 04151_skip_index_after_alter_modify_column_nullable.sql added. It pins apply_mutations_on_fly=0, apply_patch_parts=0 and a String -> Nullable(UInt64) change with a pending mutation, and checks the result with both flags off, with defaults, and after the mutation is materialized. Existing 04143/04144 tests (the prior fix in this area) are untouched.
e Both directions demonstrated? Yes. Without the fix (master snapshot binary): the query aborts the server (LOGICAL_ERROR, confirmed from fatal.log and locally). With the fix (rebuilt binary): the regression test prints 1 / 1 / 1 matching .reference, the server stays alive, and the trace shows Index idx_set is not used for part ... will be updated on the fly, confirming the index is now correctly excluded.
f Fix is general, not a narrow patch? Yes. The fix is at the source (snapshot construction), not a guard at the crash site, so it covers every skip-index type that would be read with an incompatible on-disk type after a pending ALTER MODIFY COLUMN, not just the SET index / Nullable case that fuzzing hit. The sibling read-path / write-path snapshots (MergeTask uses !patch_parts.empty(), MutateTask uses true) were reviewed and are correct as-is; only the query read path coupled alter-mutation visibility to the apply flags. Column data reads are unaffected (conversion uses the part's own stored type in IMergeTreeReader).

Session id: cron:clickhouse-worker-slot-6:20260610-141800

@groeneai

Copy link
Copy Markdown
Collaborator Author

cc @CurtizJ @alesapin — could you review this? The query read snapshot only requested alter (READ_COLUMN) mutations when apply_mutations_on_fly || apply_patch_parts, so with both off a pending ALTER MODIFY COLUMN type change was invisible to canUseIndex, and a stale skip index was read with the new (incompatible) type, aborting the server with a LOGICAL_ERROR. The fix makes the read snapshot always request alter mutations.

@PedroTadim PedroTadim added the can be tested Allows running workflows for external contributors label Jun 10, 2026
@clickhouse-gh

clickhouse-gh Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [137b450]

Summary:

job_name test_name status info comment
Stress test (arm_tsan) FAIL
Logical error: '(isConst() || isSparse() || isReplicated() || rhs.isConst() || rhs.isSparse() || rhs.isReplicated()) ? getDataType() == rhs.getDataType() : typeid(*this) == typeid(rhs)' (STID: 2508-30f6) FAIL cidb

AI Review

Summary

This PR fixes the missing pending-READ_COLUMN visibility in read snapshots when apply_mutations_on_fly = 0 and apply_patch_parts = 0, so skip-index analysis no longer reads stale on-disk index data with the post-ALTER MODIFY COLUMN type. In the current head, the earlier fast-path regression is fixed by resolving need_alter_mutations_if_pending under the existing getMutationsSnapshot locks, and the tests now cover the planning path, the direct skip-index read path, and the replicated snapshot path. I found no remaining correctness, safety, performance, or PR-metadata issues that still warrant review comments.

Final Verdict

✅ No new findings.

LLVM Coverage Report

Metric Baseline Current Δ
Lines 86.60% 86.60% +0.00%
Functions 91.90% 91.90% +0.00%
Branches 78.80% 78.80% +0.00%

Changed lines: Changed C/C++ lines covered: 31/33 (93.94%) · Uncovered code

Full report · Diff report

@clickhouse-gh clickhouse-gh Bot added the pr-bugfix Pull request with bugfix, not backported by default label Jun 10, 2026
…index analysis

Request alter mutations in the read snapshot only when a pending READ_COLUMN
mutation actually exists (num_alter > 0), instead of unconditionally. Reads
without a pending ALTER MODIFY COLUMN keep hitting the empty-snapshot fast path
in getMutationsSnapshot rather than taking the mutation lock and scanning
current_mutations_by_version.

In the replicated queue, initialize seen_all_data_mutations from
need_data_mutations only: alter and metadata mutations carry alter_version != -1
and are handled by the metadata branch, so a snapshot that needs only alter
visibility no longer walks data-mutation entries.

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

Copy link
Copy Markdown
Collaborator Author

Pre-PR validation gate (updated for the fast-path fix, HEAD 6e4dd00)

# Question Answer
a Deterministic repro? Yes. Built the base commit fc73f46 (pre-fix) and ran the new test against it: the server aborts every time with Logical error: 'Sizes of nested column and null map of Nullable column are not equal after deserialization (null map size = 1, nested column size = 0)' via SerializationNullable::deserializeBinaryBulkWithMultipleStreams -> MergeTreeIndexSet::deserializeBinary -> filterMarksUsingIndex. Repro is the test SQL: ALTER ... MODIFY COLUMN value Nullable(UInt64) with alter_sync=0, mutations_sync=0, then SELECT count() ... WHERE value=300 SETTINGS apply_mutations_on_fly=0, apply_patch_parts=0.
b Root cause explained? Yes. createStorageSnapshot set need_alter_mutations = apply_mutations_on_fly || apply_patch_parts. Both off => the pending READ_COLUMN alter mutation is dropped from the read snapshot => AlterConversions::getAllUpdatedColumns() is empty => canUseIndex does not exclude the type-incompatible skip index => filterMarksUsingIndex deserializes the old String-serialized SET-index granule as Nullable(UInt64) (null map size 1, nested size 0) => LOGICAL_ERROR.
c Fix matches root cause? Yes, and it now preserves the no-alter fast path per the AI Major. need_alter_mutations is requested when a pending READ_COLUMN mutation actually exists, gated on getMutationCounters().num_alter > 0 (a cheap 3-int read), instead of unconditionally true. Reads with no pending column-type change keep need_data_mutations=false && need_alter_mutations=false, so getMutationsSnapshot still hits the empty-snapshot fast path (no mutation lock, no current_mutations_by_version scan). The replicated queue initializes seen_all_data_mutations from need_data_mutations only, so an alter-only snapshot no longer walks data-mutation entries (alter/metadata mutations carry alter_version != -1 and use the metadata branch).
d Test intent preserved / new tests added? Yes. New regression test 04151_skip_index_after_alter_modify_column_nullable.sql (unchanged from prior HEAD) pins apply_mutations_on_fly=0, apply_patch_parts=0 for a String -> Nullable(UInt64) change with a pending mutation, and checks the result with both flags off, with defaults, and after materialization.
e Both directions demonstrated? Yes. Base binary (pre-fix): server aborts with the LOGICAL_ERROR above. Fixed binary (HEAD 6e4dd00): the test prints 1/1/1 matching .reference and the server stays alive. Additionally verified the fast path is preserved: a read with apply_patch_parts=0 and a pending UPDATE (data mutation, num_alter=0) returns the correct result with the server alive. Sibling tests 03702_alter_column_modify_secondary_index_general, 03702_alter_column_update_and_delete_secondary_index_general, 03702_alter_codec_index pass and match references.
f Fix is general, not a narrow patch? Yes. The fix is at snapshot construction, not a guard at the crash site, so it covers every skip-index type read with an incompatible on-disk type after any pending ALTER MODIFY COLUMN. The gating uses the existing num_alter counter (already maintained for all alter mutations), and the symmetric replicated read path is handled too. The write/merge snapshots (MergeTask uses !patch_parts.empty(), MutateTask uses true) were reviewed and are unaffected; column data reads use the part's own stored type in IMergeTreeReader, independent of the snapshot.

Session id: cron:clickhouse-worker-slot-14:20260610-200400

@groeneai

Copy link
Copy Markdown
Collaborator Author

Thanks for the review. Addressed both points in 6e4dd00.

Major (preserve the no-alter fast path). need_alter_mutations is no longer unconditional. It is now apply_mutations_on_fly || apply_patch_parts || has_alter_mutations, where has_alter_mutations = getMutationCounters().num_alter > 0 (the count of pending READ_COLUMN mutations, a cheap 3-int read under the existing lock). A read on a table with no pending column-type change keeps need_data_mutations = false and need_alter_mutations = false, so StorageMergeTree::getMutationsSnapshot still returns the empty snapshot via the !need_data_mutations && !need_alter_mutations && num_metadata <= 0 fast path, without taking the mutation lock or scanning current_mutations_by_version. Only a genuine pending READ_COLUMN/metadata mutation forces the walk.

For the replicated queue, seen_all_data_mutations is now initialized from !params.need_data_mutations only. Alter and metadata mutations always carry alter_version != -1 and are served by the metadata branch, so a snapshot needing only alter visibility no longer walks data-mutation entries (those have alter_version == -1 and are excluded by needIncludeMutationToSnapshot when need_data_mutations is false). This avoids the data-mutation traversal the review flagged.

Metadata. Reworded the changelog entry to "Fix a LOGICAL_ERROR exception ..." instead of "server crash".

Verified locally: the new test crashes the pre-fix binary with the documented LOGICAL_ERROR and passes on the fixed binary; a read with apply_patch_parts = 0 plus a pending UPDATE (no alter) returns correct results and stays on the fast path; sibling skip-index/alter tests pass.

@groeneai

Copy link
Copy Markdown
Collaborator Author

CI finished on HEAD 6e4dd00c7256. Both failures are non-PR-caused (one known infra outage, one tracked chronic-flaky integration test). Moving to review.

Check Failure Disposition
Stateless (arm_asan_ubsan, azure, sequential) Segfault STID 0883-65ab + Server died Infra — the 2026-06-10/11 ASan secondary-allocator OOM (sanitizer_allocator_secondary.h) outage that hit many unrelated PRs and master in the same window. Not this PR.
Integration tests (amd_tsan, 1/6) test_refreshable_mat_view_replicated::test_circular_dependencies_survive_restart Known chronic flaky (14 PRs / 30d, 1 master); tracked under issue #106651. Unrelated to skip-index ALTER MODIFY COLUMN.

Fast test, Style check, and Bugfix validation are green on this HEAD.

@groeneai

Copy link
Copy Markdown
Collaborator Author

CI summary, covered HEAD 6e4dd00c

CI fully finished (Finish Workflow pass, aggregator complete, >20 min buffer). Both failures are non-PR-caused; the createStorageSnapshot skip-index fix is not implicated.

Check Failure Classification Disposition
Stateless tests (arm_asan_ubsan, azure, sequential) Segmentation fault (STID 0883-65ab), then Server died Inherited trunk corruption (multistage distributed-queries thread-teardown UAF, STID 0883-* family) Not PR-caused. This HEAD predates the master revert #107122 (merged 01:37 UTC 06-11), so it inherited the now-fixed corruption. The arm_asan_ubsan, azure, sequential check is clean on master across 55 commits since 03:00 UTC 06-11 (0 failures); the same pre-revert window shows this family on 16+ unrelated PRs as singletons. A rebase past 01:37 UTC 06-11 clears it.
Integration tests (amd_tsan, 1/6) test_refreshable_mat_view_replicated::test_circular_dependencies_survive_restart Chronic flaky (38 hits / 33 distinct PRs / 3 master in 30d) Not PR-caused (this PR only touches MergeTreeData::createStorageSnapshot). Tracked under the chronic refreshable-MV task (issue #106651).

No PR-caused failures. The fix (skip-index over a column with a pending ALTER MODIFY COLUMN type change; regression test 04151) is unaffected.

@groeneai

Copy link
Copy Markdown
Collaborator Author

Fresh independent P0 reproduction of this bug surfaced on an unrelated PR's Stress test (amd_tsan) run, reinforcing this fix:

  • The same crash class hit the data-read skip-index path (not just the planning path): Logical error: 'Too large size (9223372036854775808) passed to allocator' via MergeTreeIndexBulkGranulesSet::deserializeBinary (MergeTreeIndexSet.cpp:192) -> SerializationNullable -> SerializationNumber<char8_t>::deserializeBinaryBulk, reached through MergeTreeSkipIndexReader::read -> filterMarksUsingIndex.
  • Root cause is identical: with apply_mutations_on_fly = 0 and apply_patch_parts = 0, a pending ALTER MODIFY COLUMN is dropped from the read snapshot, so supportsSkipIndexesOnDataRead()'s hasAlterMutations() guard does not fire and the data-read phase deserializes the old-type set-index granule with the new (incompatible) type.

I verified this PR's change fixes it on current master HEAD (67a77862327), in both directions:

CREATE TABLE t (id UInt64, value String, INDEX idx (value) TYPE set(0) GRANULARITY 1)
  ENGINE = MergeTree ORDER BY id SETTINGS index_granularity = 8, min_bytes_for_wide_part = 0;
INSERT INTO t SELECT number, toString(number * 1000000000) FROM numbers(128);
SYSTEM STOP MERGES t;
ALTER TABLE t MODIFY COLUMN value Nullable(UInt64);  -- alter_sync = 0, mutations_sync = 0
SELECT count() FROM t WHERE value = 300
  SETTINGS force_data_skipping_indices = 'idx', use_skip_indexes_on_data_read = 1,
           max_rows_to_read = 0, apply_mutations_on_fly = 0, apply_patch_parts = 0;
  • Without the fix (master HEAD): garbage huge-size allocation (would use 1.00 EiB), i.e. the same abort seen in CI.
  • With the createStorageSnapshot change cherry-picked onto master HEAD: clean result, server alive.

One note for a rebase: master's ReplicatedMergeTreeQueue::getMutationsSnapshot now computes seen_all_data_mutations from hasDataMutations()/hasAlterMutations() directly (plus a part->info.isPatch() clause), so that part of this PR may need reconciling; the MergeTreeData::createStorageSnapshot change alone resolves the reproduction on current master.
Session id: cron:clickhouse-worker-slot-5:20260629-041200

groeneai added 3 commits July 29, 2026 20:27
The fix gates `need_alter_mutations` on a pending ALTER MODIFY COLUMN, and that
snapshot flag has two consumers: the planning-time exact-ranges path
(`filterMarksUsingIndex`) and `ReadFromMergeTree::supportsSkipIndexesOnDataRead`,
which decides whether skip indexes are applied during the data read. The test only
covered the first, so a future change could reintroduce the abort in
`MergeTreeSkipIndexReader::read` with the test still green.

Verified both directions on a debug build. Reverting only the `has_alter_mutations`
term from the gate and rebuilding (Build ID ba9b530f -> dbbf2576) makes the new
query abort on its own with
"Sizes of nested column and null map of Nullable column are not equal after
deserialization", through
MergeTreeIndexBulkGranulesSet::deserializeBinary <- MergeTreeSkipIndexReader::read;
the pre-existing planning-time query was removed for that run so the new row is
proven load-bearing rather than carried by an earlier assertion. With the fix
restored (Build ID back to ba9b530f) the query returns the correct row.

`max_rows_to_read = 0` is required because the data-read phase disables itself when
clickhouse-test injects `read_overflow_mode = throw` together with a row limit, the
same reason 04143 sets it. Every setting the row depends on is pinned at statement
level, so runner randomization of `use_skip_indexes_on_data_read` cannot flip it.
`force_data_skipping_indices` does not throw when skip indexes are disabled
globally: measured on a debug build, the new direct-read row still returns the
correct row with `use_skip_indexes = 0`, so the value assertion alone does not
prove the index was consulted. Pin the setting so the row cannot pass while
exercising nothing.

The abort-based proof is unaffected (reverting the gate makes the row abort on
its own), and `use_skip_indexes` is not runner-randomized today, so this closes a
latent hole rather than an active failure.
The shortcut skipped the data-mutation walk whenever data mutations were not
requested, on the assumption that every alter mutation carries
`alter_version != -1` and is therefore served by the metadata branch. That
assumption does not hold for entries serialized before `alter version` was
written: `ReplicatedMergeTreeMutationEntry::readText` reads the field only
`if (checkString("\nalter version: ", in))`, so such an entry keeps the `-1`
default and is routed through the data branch, where
`needIncludeMutationToSnapshot` would have accepted its `READ_COLUMN` command.
Skipping that walk could drop a pending alter for an old znode, which is the
exact condition this PR exists to prevent.

The shortcut was only an optimization for alter-only snapshots, not part of the
fix, so revert it: this file is now identical to master and the PR is reduced to
the single `createStorageSnapshot` gate plus its test. The empty-snapshot fast
path that motivated the earlier review round is preserved by the `num_alter`
gate in MergeTreeData.cpp, which is untouched.

Re-verified on a debug build after the revert: 04151 matches its reference, the
direct-read row still returns the correct row, and 04143 still passes.
@clickhouse-gh

clickhouse-gh Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 86.40% 86.30% -0.10%
Functions 91.90% 91.80% -0.10%
Branches 78.50% 78.40% -0.10%

Changed lines: Changed C/C++ lines covered: 11/11 (100.00%) · Uncovered code

Full report · Diff report

@groeneai groeneai added the groeneai-origin-review-human PR origin: human review feedback on a groeneai PR label Aug 19, 2026
@Ergus Ergus self-assigned this Sep 1, 2026

@Ergus Ergus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regression tests is not failing over master without the fix, so the issue was already fixed or the reproduction is nor determninistic?

@groeneai

groeneai commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

You are right. It stopped reproducing, because this PR was superseded by my own #112484.

04151 exactly as committed here, run just now:

  • 26.7.6.57 (official build): Code: 49, LOGICAL_ERROR
  • 26.8.2.7 (official build): passes, 1 1 3 1
  • master 38a9ca20767a (debug, build id ed4622ff): passes, 1 1 3 1

It is not a vacuous pass on master: the mutation is still pending on 3 of 3 parts, system.columns already reports Nullable(UInt64), and EXPLAIN indexes = 1 still lists Skip / Name: idx_value. The predicate is armed, the read just no longer mis-decodes.

The fix is 4bb044d321bcef2, "Do not deserialize a skip index whose on-disk type is stale", from #112484, merged 2026-08-15. It is in master and 26.8 and not in 26.7, which matches the three results above. It compares the part's own recorded type against the type the granule is about to be decoded with, in IMergeTreeIndex::getDeserializedFormat, and its commit message lists "the mutation is pending but dropped from the storage snapshot" as one of the cases it covers. That is precisely what this PR gates in createStorageSnapshot, so the code change here is redundant and sits at a worse layer.

Same for the other shape, UInt64 to Float64, where the old bytes reinterpret silently instead of failing to deserialize: 26.7.6.57 returns 0 where 128 is correct, 26.8.2.7 and master return 128.

Two consequences. With pr-bugfix this can no longer pass its own gate, since Bugfix validation needs the added test to fail on the master binary, which is what you saw. And only the tests are still worth anything: 04165_skip_index_stale_type_after_alter, added by #112484, never sets apply_mutations_on_fly or apply_patch_parts, so the both-flags-off combination is left unpinned.

I would close this and move those two queries into 04165 as a test-only PR. Say so if you would rather I reduce this PR to the tests instead.

@abashkeev abashkeev added the comp-mergetree MergeTree* family: parts, merges, primary index, column statistics, background data transformation. label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

can be tested Allows running workflows for external contributors comp-mergetree MergeTree* family: parts, merges, primary index, column statistics, background data transformation. groeneai-origin-review-human PR origin: human review feedback on a groeneai PR pr-bugfix Pull request with bugfix, not backported by default

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants