Found by code review of draft PR #712 (branch chtypes-v2, internal/typelayer/records.go, bomFloor).
Problem. For a BOM-led CSV/TSV body, bomFloor takes the lower of two record-count readings. For first columns of type Array(String), any Map, JSON, Dynamic, or a Tuple/Variant with a String element, ClickHouse keeps the mark, but the lower reading treats the BOM-led names line as a header. The floor ends one below the real count, so one swallowed record goes unnoticed. Measured by the reviewer on a ClickHouse 26.8 artifact.
Repro. Table (a Array(String), id UUID, b String), bare text/csv, body: BOM a,id,b / "['x']",<uuid>,/1 / "['x']",zzz,/2 / "['x']",<uuid>,/3 / "['x']",<uuid>,/4. chtypes returns 4 verdicts (zzz refused, its UUID window takes /3), the floor is min(4,5)=4, so no decline. The caller reads the names line as a header and sees /1 as failed, though it was published (a retry duplicates it). The same body on a String-first table is declined.
Types that keep the mark (measured): Array(String), Map(UInt8,UInt8), Map(UInt8,String), Map(String,UInt8), Tuple(a String), JSON, Dynamic, Variant(String,UInt64). Types that skip it: Tuple(a UInt8), Variant(UInt64), Array(UInt8), UUID, IPv6, Bool, Decimal, Enum8.
Impact is narrow: needs a BOM, bare CSV/TSV, one of these first-column types, and an invalid value that swallows a record.
Found by code review of draft PR #712 (branch
chtypes-v2,internal/typelayer/records.go,bomFloor).Problem. For a BOM-led CSV/TSV body,
bomFloortakes the lower of two record-count readings. For first columns of typeArray(String), anyMap,JSON,Dynamic, or aTuple/Variantwith a String element, ClickHouse keeps the mark, but the lower reading treats the BOM-led names line as a header. The floor ends one below the real count, so one swallowed record goes unnoticed. Measured by the reviewer on a ClickHouse 26.8 artifact.Repro. Table
(a Array(String), id UUID, b String), baretext/csv, body: BOMa,id,b/"['x']",<uuid>,/1/"['x']",zzz,/2/"['x']",<uuid>,/3/"['x']",<uuid>,/4. chtypes returns 4 verdicts (zzz refused, its UUID window takes /3), the floor ismin(4,5)=4, so no decline. The caller reads the names line as a header and sees /1 as failed, though it was published (a retry duplicates it). The same body on a String-first table is declined.Types that keep the mark (measured): Array(String), Map(UInt8,UInt8), Map(UInt8,String), Map(String,UInt8), Tuple(a String), JSON, Dynamic, Variant(String,UInt64). Types that skip it: Tuple(a UInt8), Variant(UInt64), Array(UInt8), UUID, IPv6, Bool, Decimal, Enum8.
Impact is narrow: needs a BOM, bare CSV/TSV, one of these first-column types, and an invalid value that swallows a record.