Repository navigation
fix(writer): value fidelity -- NULL never defaulted, column-zone DateTime64, DST gap, geometry, loud clamps, case-mismatch, unbindable fields - #1475
Merged
Conversation
…as_default=0 A source NULL bound for a non-Nullable ClickHouse column was silently replaced by the column DEFAULT: the connector binds NULL explicitly (Spec 07.07 §3.2), but the server-side setting input_format_null_as_default defaults to 1, so ClickHouse swapped in 0 / '' / 1970-01-01 and reported the batch successful. Measured with clickhouse local 24.8.14 on `v Int32 DEFAULT 7`: the row stored 7 under the server default and is rejected (Code 53 TYPE_MISMATCH) with the setting at 0. BaseDbWriter.customSettings() now produces the default list input_format_null_as_default=0,allow_experimental_object_type=1,insert_allow_materialized_columns=1, and appends input_format_null_as_default=0 to a user-provided clickhouse.jdbc.settings list that does not mention the key (an explicit user value is honoured). The failure is now loud and the batch is retried; the remediation is on the ClickHouse side (make the column Nullable). Spec: 07.07 §3.2.1 (new), §5; 03.05 §3.1. Tests: JdbcCustomSettingsTest (3 tests; 2 fail on the pre-fix code, mutation check flipping the default to =1 turns defaultSettingsDisableNullAsDefault red). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…o DST-gap shift MySQL DATETIME is zone-less wall-clock digits, which Debezium encodes as a UTC epoch (LocalDateTime.toInstant(UTC)). TimestampConverter (DATETIME(0..3)) decoded that epoch through TimeZone.getRawOffset/inDaylightTime of the source zone and then formatted it in the session zone. When both zones are the same DST zone this moved every spring-forward gap wall time back by an hour: 2026-03-08 02:30:00 in America/Chicago was written as 01:30:00, silently, with row counts intact. MicroTimestampConverter (DATETIME(4..6)) already took the same-zone shortcut (format the epoch as UTC), so the two converters disagreed on the same digits. TimestampConverter now takes the same shortcut when the source and session zones are equal, and when they differ it interprets the digits as a source-zone wall time through ZoneRules, using the pre-transition offset for a gap time exactly as MicroTimestampConverter does. Spec: 07.03 §3.1 / §3.1.1 (new), §5. Test: DebeziumConverterTest.testTimestampConverterGapTimePreserved (pre-fix: expected 02:30:00.000 but was 01:30:00.000; mutation check disabling the shortcut turns it red again). All 17 DebeziumConverterTest tests pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…pty source zone means the session zone Two related zone defects in the value path. 1. ClickHouse parses a DateTime literal in the COLUMN's zone, but the converters rendered every instant in the ClickHouse session zone. The auto-create path declares DateTime64(p, 'UTC') columns, so on a session whose zone is not UTC every TIMESTAMP (Debezium ZonedTimestamp) and every zone-shifted DATETIME was stored off by the session offset: a TIMESTAMP of 16:00Z through an America/Chicago session landed as 10:00 UTC. Measured with clickhouse local 24.8.14: '2022-01-01 10:00:00' into DateTime64(3,'UTC') reads back 04:00:00 in America/Chicago. PreparedStatementFieldMapper now resolves the zone declared by the target column type (ClickHouseColumn.getTimeZone) and ClickHouseDataTypeMapper / DebeziumConverter render instants in it, falling back to the session zone only when the column declares none. The same-zone DATETIME digits decode is unaffected (digits are digits in any column zone). 2. An empty database.connectionTimeZone was taken as UTC while an empty clickhouse.datetime.timezone fell back to the server zone, so the default configuration on a non-UTC server ran the "different zones" branch with a source zone nobody configured: DATETIME '10:00:00' was written as '04:00:00' on an America/Chicago server. resolveSourceTimeZone() now resolves an empty source zone to the session zone (digits preserved; no basis for a shift without a declared source zone), logged once at INFO. A wall-clock shift happens only when the operator declares a source zone that differs from the session zone. Spec: 07.03 §3 table, §3.1.2 and §3.1.3 (new), §5. Tests: PreparedStatementFieldMapperColumnZoneTest (3 tests, all fail on the pre-fix code: 04:00:00 / 10:00:00 where 10:00:00 / 16:00:00 are expected), DebeziumConverterTest.testTimestampIntoUtcColumnWhenServerZoneIsChicago, ClickHouseDataTypeMapperTimeZoneTest (4). Mutation checks: ignoring the column zone turns the TIMESTAMP and explicit-shift tests red; resolving the empty source zone to UTC turns the digits test red. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
….of.range (default false) fails the batch DebeziumConverter clamped every DATE / DATETIME / TIMESTAMP outside the ClickHouse type's range to the bound with no log line at all, and BigDecimalConverter clamped with a WARN naming neither table nor column. A MySQL DATETIME '9999-12-31 23:59:59' (the customary open-ended sentinel) landed as 2299-12-31 23:59:59 with the batch reported successful -- a value MySQL never held. New connector setting clamp.out.of.range (Boolean, default false): - false: DebeziumConverter.RangePolicy throws ValueOutOfRangeException naming the column (db.table.column), the source value, the ClickHouse type, its bounds and the setting that would saturate. The batch fails and is retried; nothing is written. - true: the value is saturated as before and every saturation logs a WARN naming the column, the source value and the stored value. PreparedStatementFieldMapper builds the policy per bound column and ClickHouseDataTypeMapper.convert threads it into TimestampConverter, MicroTimestampConverter, DateConverter, ZonedTimestampConverter and BigDecimalConverter. The pre-existing policy-less converter overloads keep saturating but now WARN (the existing DebeziumConverterTest range tests pin that behaviour). ZonedTimestampConverter bounds the parsed instant outside the format-probing loop so a rejection is not swallowed as a format mismatch. PostgreSQL infinity / -infinity keep their documented saturation. Spec: 07.03 §3.3 (new, with upgrade note), §5; doc/configuration.md row. Tests: DebeziumConverterRangePolicyTest (6; the two mapper-level default tests fail on the pre-fix code: "Expected RuntimeException to be thrown, but nothing was thrown"), PreparedStatementFieldMapperOutOfRangeTest (1). Mutation check: forcing clamp=true in RangePolicy.of turns the default tests red. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…on-POLYGON spatial types are String (WKB hex) ClickHouseDataTypeMapper wrote an EMPTY polygon ([]) whenever it could not represent a Debezium Geometry value: a non-Polygon geometry (LINESTRING, MULTI*, GEOMETRY) bound for a Polygon column, an unparseable WKB payload, a missing wkb field, or a non-Struct carrier. A non-Struct Point became the origin, a non-Struct variable-scale decimal became 0, and an unparseable ZonedTimestamp became '' after an ERROR line. Every one of these stored a value the source never held with the batch reported successful. - Geometry / Point / VariableScaleDecimal / ZonedTimestamp: every such case now throws IllegalArgumentException naming the column (when known), the geometry type and the reason; the batch fails and is retried. - A Geometry value bound for a String column is stored as its exact WKB bytes, hex-encoded (LOWER(HEX(ST_AsWKB(col))) on MySQL), or raw with persist.raw.bytes=true. - Record-schema type mapping (auto-create and schema.evolution ADD COLUMN): a propagated source type of LINESTRING / MULTIPOINT / MULTILINESTRING / MULTIPOLYGON / GEOMETRYCOLLECTION / GEOMCOLLECTION / GEOMETRY maps to String (Nullable(String) when optional); POLYGON, and Geometry without a propagated type, stay Polygon; POINT stays Point. - ClickHouseDataTypeMapper.canBeNullable centralises the "may be wrapped in Nullable" rule (not for Array/Map/Tuple/Nested or the geo types) and is used by getColumnNameToCHDataTypeMapping and by createTableSyntax / isNullableColumn, which previously emitted Nullable(Polygon) for an optional POLYGON column -- a type ClickHouse rejects. Spec: 07.06 rewritten (§3.1 type mapping, §3.2 value binding, §3.3 other substitutions, §5). Tests: ClickHouseDataTypeMapperGeometryTest (10; 8 fail on the pre-fix code: "[]" bound / nothing thrown / Nullable(Polygon)). Mutation check: restoring the empty-polygon fallback for a non-Polygon geometry turns nonPolygonIntoPolygonColumnThrows red. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eld only in letter case INSERT membership matches ClickHouse column names to record fields case-insensitively (QueryFormatter.createColumns), but the bind-time value read used the ClickHouse name verbatim (struct.getWithoutDefault(colName)), which Kafka Connect resolves case-sensitively. A table created by hand as `ID` Int32, `Amount` Float64 for source columns id, amount therefore threw DataException on every row; PreparedStatementFieldMapper classified that as a stale cache and the batch was retried forever against a cache that was never stale -- a permanent stall with nothing written. PreparedStatementFieldMapper.resolveSourceField(struct, colName) now resolves the field against the record's schema (exact match, then case-insensitive) and the value is read with getWithoutDefault(field.name()). The record's schema is used rather than the modified-field list because that list omits NULL-valued fields, and a NULL in a case-mismatched column must still be bound as NULL. The private getFieldByColumnName(list) helper is replaced by it. A name matching no field under either comparison still reaches the stale-cache branch, which is the condition it was written for. Spec: 04.03 §3.4 (new), §5. Test: PreparedStatementFieldMapperColumnCaseTest (1; pre-fix: StaleSchemaCacheException "Column ID is present in the ClickHouse table but absent from the record's schema"). Mutation check: reverting the read to the verbatim column name turns it red again. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mapping, matching what is bound
ClickHouseDataTypeMapper.mapDebeziumSchemaToDDL (used by the PostgreSQL
schema reconciler for ADD COLUMN) declared io.debezium.time.MicroTime as
Nullable(Int64) ("microseconds since midnight"), while the value path binds
MicroTime as the formatted text [-]HH:mm:ss.ffffff (Spec 07.03 §3.2). Every
insert into such a reconciled TIME column therefore failed to parse. The
declared type and the bound representation must agree: Nullable(String).
Both existing tests pinned the mismatch (ClickHouseDataTypeMapperDDLTest and
PostgresSchemaReconcilerTest asserted Nullable(Int64)); they are flipped
deliberately, with the reason in the fixture.
Spec: 07.03 §3.2, §5.
Tests: ClickHouseDataTypeMapperDDLTest.testDebeziumLogicalTypes (MicroTime
row; pre-fix: expected Nullable(String) but was Nullable(Int64)),
PostgresSchemaReconcilerTest.testDebeziumLogicalTypes. Mutation check:
reverting the mapping to Nullable(Int64) turns both red.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…SON, ENUM/SET, TIME, BOOL, FLOAT and binary values Spec-only. The type-system specs described target types but not the exact text the writer stores, which is what any value-level comparison between MySQL and ClickHouse must expect: - 07.02 §3.1: MySQL FLOAT is delivered by Debezium as FLOAT64 and stored in Float64 on both paths -- a widening that renders 1.1 as 1.100000023841858; REAL is Float32 on the DDL path; comparisons must use toFloat32 or a common rendering. - 07.04 §3.1 (new): verbatim strings, ENUM labels, comma-joined SET labels, Debezium's canonical JSON rendering, TIME always with six fraction digits, BIT(1) as Bool, BOOL/TINYINT(1) delivered as INT16 (0/1), binary as hex. - 07.05 §3.2 (new): the stored text of a binary value by binary.handling.mode x persist.raw.bytes -- lower-case hex (default), raw bytes, base64 text (the shipped deployment templates set base64, so persist.raw.bytes has no effect there), or Debezium's upper-case hex. No code changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…y addBatch, raw JSON without schema defaults
The V2 JDBC driver's PreparedStatementImpl.addBatch() substitutes the bound
values into the SQL template and keeps them (verified from the 0.9.8
bytecode: only clearParameters() resets values[]). Two consequences in the
writer:
1. PreparedStatementFieldMapper logged "DATA TYPE NOT HANDLED" and continued
when ClickHouseDataTypeMapper.convert had no handler for a field (e.g. a
MAP), leaving the parameter unbound. On the first row the driver failed
the batch; on every later row the PREVIOUS row's value was written at that
index, silently. It now throws DataException naming the type, the column
and the table.
2. PreparedStatementExecutor now calls ps.clearParameters() immediately after
every ps.addBatch() (ordinary row and relocation tombstone), so no bind
state survives from one row to the next on either driver.
Also, TableMetaDataWriter.convertRecordToJSON (the store.raw.data copy of the
row) read fields with Struct.get, which substitutes the Connect-schema
default for a source NULL; it now reads with getWithoutDefault (NULL fields
stay omitted, as before).
Spec: 07.07 §3.2.2 (new), §5; 03.06 §3.2, §5.
Tests: PreparedStatementFieldMapperUnhandledTypeTest (pre-fix: nothing
thrown), PreparedStatementExecutorClearParametersTest (pre-fix call sequence
[setObject, setString, addBatch, setObject, setString, addBatch,
executeBatch, close] -- no clearParameters),
TableMetaDataWriterTest.testConvertRecordToJSONDoesNotSubstituteSchemaDefault
(pre-fix: {"id":1,"status":"new"}). Mutation checks: removing the
clearParameters calls, the throw, and the getWithoutDefault read each turn
the respective test red.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ted source type (BIGINT UNSIGNED hazard) In Kafka Connect mode nothing forces column.propagate.source.type, so the record-schema auto-create path sees a BIGINT UNSIGNED as a bare INT64 field, declares Int64, and a value at or above 2^63 is stored as the wrapped negative number (the UInt64 restore of Spec 07.01 §3.1 cannot apply to an Int64 column). The connector cannot resolve the ambiguity without source metadata; it now says so. getColumnNameToCHDataTypeMapping logs one ERROR per table (static, per JVM) naming the INT64 column(s) that carry no __debezium.source.column.type and the remedy (column.propagate.source.type=.* or a hand-created table). ClickHouseAlterTable.alterTable passes the table name through so the ADD COLUMN path is attributable too; overrides still require both names and stay skipped there, as before. Spec: 07.01 §3.2 (new), §5. Test: ClickHouseTableOperationsBaseUntypedInt64Test (1; pre-fix: no ERROR logged). Mutation check: dropping the report turns it red. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ath; engine column collision; no legacy engine on a metadata failure The Kafka-mode auto-create and schema.evolution ADD COLUMN path declared ClickHouse types that differed from what the DDL translator declares for the same MySQL column, so a table created by one path and evolved by the other carried two types for one source type, and value-level comparisons rendered the same instant differently. Record-schema path (ClickHouseTableOperationsBase.getColumnNameToCHDataTypeMapping), using Debezium's propagated source metadata where present: - signed TINYINT -> Int8 (was Int16; DDL path says Int8); - unsigned integers are Nullable(UIntN) when optional (the unsigned branch skipped the Nullable wrap, so ADD COLUMN declared a non-Nullable unsigned column for a nullable source column and the first NULL failed the batch); - DECIMAL: Decimal(p, s) from the parameters, a missing precision is max(10, s), a missing scale 0 -- a dimensionless DECIMAL is Decimal(10,0) as MySQL defines it and the DDL path declares (was Decimal(10,2)); - DATETIME(p)/TIMESTAMP(p): DateTime64(p, 'UTC') with p from __debezium.source.column.length (a propagated DATETIME/TIMESTAMP without a length is p = 0); without a propagated type the previous 3/6 defaults. The shared helpers (getSignedTinyIntType, decimalType, temporalPrecision) live in ClickHouseDataTypeMapper. createTableSyntax renames the engine delete-marker column to _is_deleted when the source table has its own is_deleted column (as the DDL translator does) instead of declaring the name twice, which ClickHouse rejects; the source column keeps its name and nullability. DbWriter.isNewReplacingMergeTreeEngine no longer returns false when SELECT VERSION() fails: the failure propagates and no table is created in that initialisation. It previously created the legacy ReplacingMergeTree(_version) + _sign layout permanently after a transient metadata failure. Spec: 08.05 §3.1 / §3.1.1 (new, incl. the residual DDL-path differences), §5; 06.05 §3.4.1 (new), §5. Tests: ClickHouseTableOperationsBaseTest (Decimal(10,2) assertion flipped to Decimal(10,0); new getColumnNameToCHDataTypeMappingSourceTypeParityTest), ClickHouseAutoCreateTableTest.testSourceIsDeletedColumnRenamesEngineColumn (pre-fix DDL declares `is_deleted` twice), DbWriterEngineSelectionTest (pre-fix: nothing thrown), RecordSchemaVsDdlTypeAgreementTest in the lightweight module (table-driven; pre-fix disagreements: TINYINT Int16 vs Int8, DATETIME DateTime64(3) vs (0), TIMESTAMP DateTime64(6) vs (0)). Mutation check (one run, three reversions): reverting the TINYINT mapping, the engine-column rename and the version-failure propagation turn the parity test, testSourceIsDeletedColumnRenamesEngineColumn and DbWriterEngineSelectionTest red respectively (3 failures out of 14). The DDL path's zone-less DateTime64(p, 0) is left as is: clickhouse local 24.8.14 stores DateTime64(3, 0) as DateTime64(3). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
minguyen9988
added a commit
that referenced
this pull request
Sep 22, 2026
…-commit-order Resolves eight conflicts of merging the current 2.11.0 head (now containing #1475/#1476/#1477) into the version-floor branch. Additive/kept-both in the Lean and spec files: both BatchOrder (#1476) and VersionFloor (this branch) listed in lakefile.lean, formal_specs/lean/README.md, CONSTITUTION.md, and spec 11.03 (numbered proof list renumbered so BatchOrder is 13 and VersionFloor 14-16); spec 04.04 keeps #1476's collapsing/grouping bullets and this branch's composite-PK bullet; spec 01.06 keeps #1477's terminal classifier plus this branch's heartbeat-sequence-state test. The one semantic conflict is the version-assignment hot loop of DebeziumChangeEventCapture. Both behaviours are kept: #1478's VersionAssignment machinery (nextVersionAssignment + coverAssignedVersion, control records excluded from the sequence, chStruct versioned from assignment.sequenceNumber/effectiveTs) AND #1477's loud-failure path (rejectUnrepresentableValue before the sequence; parse() wrapped so a parser throw becomes RecordReplicationException; a null-parsed ROW record is terminal, not a WARN skip; the RecordReplicationException/DDLReplicationException rethrows ahead of the catch-all). NullParsedRowRecordIsTerminalTest keeps its terminal assertion, adapted to the VersionAssignment reflective signature. Verification: lightweight compile clean; NullParsedRowRecordIsTerminalTest + ControlRecordLogLevelTest + DebeziumChangeEventCaptureTest + UnparseableRowRecordIsTerminalTest 38/38; spec validator PASSED (59 specs, 519 citations); lake build completed successfully (BatchOrder + VersionFloor both built, 0 sorry); full sink-connector unit suite 547 run, 0 failures, 20 errors all Docker-environment (identical to base). config.yml.j2 rendered (minimal + full variants) and YAML-linted with duplicate-key detection: no unrendered markers, no duplicate keys. All 42 destructive sites in this merge are #1475/#1476/#1477 code already carrying DESTRUCTIVE: markers or test-code literals (statement text parsed/classified/logged, no execution against any live database), unchanged by this merge. Jinja-Render-Check: rendered=2; formats=yaml; result=pass Destructive-Op-Check: sites=42; result=pass Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
minguyen9988
added a commit
that referenced
this pull request
Sep 22, 2026
…ypes; fix inherited DdlIgnoreRulesTest signature Resolves nine conflicts of merging the current 2.11.0 head (now containing #1475/#1476/#1477/#1478) into the ddl-translation branch. MySqlDDLParserListenerImpl.java: both sides added independent fields (this branch's PK-change/ddlEventTimestamp fields and #1477's dropOrTruncateStatement field + isDropOrTruncateStatement method) - kept both. Lean lakefile/README, CONSTITUTION and spec 11.03 list all of BatchOrder (#1476), VersionFloor (#1478) and this branch's CreateTable. spec 07.01 keeps this branch's DDL-path unsigned rule (§3.2) and #1478's record-path untyped-Int64 report (renumbered §3.3). spec 07.02 keeps this branch's IEEE section plus #1475's value-comparison caveat. spec 06.05 keeps both verification sets including #1478's RecordSchemaVsDdlTypeAgreementTest bullet. spec 07.06 (spatial) is a genuine two-path merge: the record-schema path keeps POINT/POLYGON native (#1475), the DDL path types every spatial column String/WKB-hex (this branch, new §3.4); they agree on every non-polygon type and the RecordSchemaVsDdlTypeAgreementTest does not cover spatial, so the code coexists. CI fix: 2.11.0 itself is internally inconsistent - #1478 changed processEveryChangeRecord's last parameter to VersionAssignment but DdlIgnoreRulesTest (added by #1477) still reflected long.class, so disableDropTruncateIsScopedAndLive and disableDropTruncateExemptsSnapshotDdl threw NoSuchMethodException. This is the java-tests-lightweight failure the branch inherits through the merge. Fixed the reflective lookup and call to use VersionAssignment (mirroring NullParsedRowRecordIsTerminalTest). The same one-line fix should be back-ported to 2.11.0 directly. Verification: spec validator PASSED (59 specs, 534 citations); lightweight compiles; DdlIgnoreRulesTest + MySqlDDLParserListenerImplTest + DataTypeConverterTest + CreateTableNoKeySortKeyTest + CreateTableUniqueKeySortKeyTest + NullParsedRowRecordIsTerminalTest 175/175 (1 Docker skip); full lightweight unit suite 556 run, 0 failures, 62 errors all Testcontainers/Docker (no daemon here). config.yml.j2 rendered (minimal + full) and YAML-linted, no duplicate keys. All destructive sites in this merge are #1475/#1476/#1477/#1478 code with DESTRUCTIVE: markers or test-code literals, unchanged by this merge. Jinja-Render-Check: rendered=2; formats=yaml; result=pass Destructive-Op-Check: sites=42; result=pass Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
minguyen9988
added a commit
that referenced
this pull request
Sep 22, 2026
Rebases the CollapsingMergeTree clear-parameters fix onto the current 2.11.0 head, which now contains #1475-#1480. #1476 was squash-merged, so its commits on this branch conflicted with the same content already in 2.11.0; every such conflict (Lean lakefile/README, CONSTITUTION, specs 04.04/10.04/11.03, DbWriter) is resolved by taking the 2.11.0 side, which carries the accumulated #1476-#1480 content that subsumes this branch's #1476 commits. The only net change this branch adds over 2.11.0 is the one-file CollapsingMergeTree fix in PreparedStatementExecutor.java, re-applied on top of 2.11.0's version. The fix: the CollapsingMergeTree UPDATE branch stages the -1 cancel row with ps.addBatch() but did not call ps.clearParameters() afterward, unlike the ReplacingMergeTree relocation-tombstone branch. The V2 JDBC driver retains bound values across addBatch(), so a parameter the after image does not rebind could inherit the cancel row's sign=-1 and lose the UPDATE. Defensive-consistency fix (Gemini panel finding on #1476, demoted-unproven because the sign is always rebound in practice); matches the RMT tombstone branch and the general per-row path (Spec 07.07 s3.2.2, Spec 05.04 s3.1). Verification: net PR diff vs 2.11.0 is exactly PreparedStatementExecutor.java; sink-connector compiles; PreparedStatementExecutorCollapsingSignTest + ClearParametersTest + SortingKeyTombstoneTest + TruncateTest 17/17; spec validator PASSED (59 specs, 534 citations); config.yml.j2 rendered (minimal + full) and YAML-linted, no duplicate keys. All 25 destructive sites in this merge are #1475-#1480 code with DESTRUCTIVE: markers or test-code literals, unchanged by this merge. Jinja-Render-Check: rendered=2; formats=yaml; result=pass Destructive-Op-Check: sites=25; result=pass Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.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.
Base: 2.11.0 at 5780b62 (after 1470-1474). Part of the replication-correctness audit; every change is spec-first (spec updated in the same commit), with a failing-then-passing test and a mutation check per finding.
Each finding is a case where a ClickHouse row could differ from its MySQL row while row counts still matched.
Verification: sink-connector unit suite and sink-connector-lightweight unit suite green (Docker-backed ITs not runnable in the build environment used, so they rely on CI). Spec validator passes. No Lean file changed.
Merge note: the six audit PRs are independent branches off 5780b62 and touch shared registration files (specs README, CONSTITUTION, Lean roots, validator module list). Suggested order: this one, then fix-writer-engine-semantics, fix-engine-lifecycle-no-silent-loss, fix-version-floor-restart-and-gtid-commit-order, fix-ddl-translation-identity-and-types, fix-db-compare-checksum-fidelity; after each merge, forward-merge 2.11.0 into the remaining branches (no force-push).