tomnewton opened a new pull request, #39846:
URL: https://github.com/apache/beam/pull/39846

   ClickHouseIO's `TableSchema` and column-type parser had no notion of 
ClickHouse's fixed-point `Decimal` family, so `ClickHouseIO.getTableSchema()` 
failed on the `DESCRIBE TABLE` output of any table containing a Decimal column 
and pipelines carrying monetary/fixed-point values could not write to 
ClickHouse at all.
   
   This change adds first-class `Decimal(P, S)` support to `ClickHouseIO`:
   
   * **Schema model** — new `TypeName.DECIMAL`; `ColumnType` carries `scale` 
alongside the existing `precision` (validated: P in [1, 76], S in [0, P]), with 
a `ColumnType.decimal(precision, scale)` factory. Bare `Decimal` is 
`Decimal(10, 0)` and `Decimal(P)` is `Decimal(P, 0)`, matching ClickHouse's 
defaults.
   * **Parser** — JavaCC grammar rules for `Decimal[(P[, S])]` and the width 
aliases `Decimal32(S)` / `Decimal64(S)` / `Decimal128(S)` / `Decimal256(S)` 
(pinned to precisions 9/18/38/76 per ClickHouse's synonym rule; `DESCRIBE 
TABLE` canonicalizes the aliases to `Decimal(P, S)` anyway). Reachable through 
`Nullable(...)` and `Array(...)` via the existing `primitive()` rule. Lexical, 
syntactic and range-validation failures all surface as the parser's uniform 
`failed to parse` error.
   * **Beam field-type mapping** — `Decimal(P, S)` maps to 
`FixedPrecisionNumeric.of(P, S)` (base type `DECIMAL`, values are 
`BigDecimal`), preserving the declared precision/scale through 
`getEquivalentSchema` and giving an early, loud failure at Row construction for 
values whose digits genuinely exceed the declared precision. This is the same 
logical type JdbcIO uses for NUMERIC.
   * **Writer** — serializes the unscaled value as a little-endian signed 
integer whose width (32/64/128/256 bits) is chosen from the declared precision, 
delegating to the ClickHouse client's `BinaryStreamUtils.writeDecimal`. 
Fractional digits beyond the column scale are truncated toward zero, consistent 
with ClickHouse's own "excessive digits in a fraction are discarded (not 
rounded)" semantics; values exceeding the storage width fail loudly.
   * **Defaults** — `ColumnType.parseDefaultExpression` handles Decimal 
literals (`DEFAULT 1.23`), so `getTableSchema` no longer throws for tables with 
defaulted Decimal columns, and `writeRow`'s existing null-substitution works 
for them.
   
   Tests:
   
   * `TableSchemaTest` — parser cases for `Decimal(10, 2)`, `Decimal(5)`, bare 
`Decimal`, all four width aliases, `Nullable(Decimal(10, 2))`, 
`Array(Decimal(38, 10))`; uniform `failed to parse` errors for `Decimal(77, 
2)`, `Decimal(0)`, `Decimal(9, 10)`, `Decimal(9, -1)` and `Decimal(abc)`; 
factory range validation; schema-mapping tests including nullable; a test 
pinning that Row construction under the mapped `FixedPrecisionNumeric` type 
rejects values exceeding the declared precision (the layer that guards the 
declared range, since the writer checks only the storage width); 
`parseDefaultExpression` for positive and negative literals.
   * `ClickHouseWriterTest` — byte-level tests locking the wire format for 
every width bucket: little-endian unscaled Int32, scaling below the column 
scale, 16 × 0xFF two's-complement sign extension for a negative Decimal128, a 
32-byte Decimal256 encoding, truncation-toward-zero of excess fractional 
digits, storage-width overflow rejection, and null/non-null through 
`writeNullableValue`.
   * `ClickHouseIOIT` — round-trip integration tests against the ClickHouse 
test container for `Decimal(9, 2)`, `Decimal(18, 4)`, `Decimal(38, 10)` and 
`Decimal(76, 20)` (including negative values), `Nullable(Decimal(10, 2))` with 
a null row, `Array(Decimal(9, 2))`, a `DEFAULT 2.25` column exercised 
end-to-end by writing a null row, a `getTableSchema` round-trip over a table 
declared with the width aliases (verifying we parse the server's canonicalized 
`DESCRIBE` output), the user-visible truncation of over-scaled values (`-1.239` 
→ `-1.23`), and a test pinning that a binary insert exceeding the declared 
precision but fitting the storage width is stored and read back verbatim 
(ClickHouse checks the declared range only on conversion from a string).
   
   fixes #39840
   
   ------------------------
   
   Thank you for your contribution! Follow this checklist to help us 
incorporate your contribution quickly and easily:
   
    - [x] Mention the appropriate issue in your description (for example: 
\`addresses #123\`), if applicable. This will automatically add a link to the 
pull request in the issue. If you would like the issue to automatically close 
on merging the pull request, comment \`fixes #<ISSUE NUMBER>\` instead.
    - [x] Update \`CHANGES.md\` with noteworthy changes.
    - [ ] If this contribution is large, please file an Apache [Individual 
Contributor License Agreement](https://www.apache.org/licenses/icla.pdf).
   
   See the [Contributor Guide](https://beam.apache.org/contribute) for more 
tips on [how to make review process 
smoother](https://github.com/apache/beam/blob/master/CONTRIBUTING.md#make-the-reviewers-job-easier).
   
   To check the build health, please visit 
[https://github.com/apache/beam/blob/master/.test-infra/BUILD_STATUS.md](https://github.com/apache/beam/blob/master/.test-infra/BUILD_STATUS.md)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to