zhengxiang378928908-code commented on issue #12814:
URL: https://github.com/apache/seatunnel/issues/12814#issuecomment-6207773389

   @SEZ9 @DanielLeens My lean is **C for top-level fields + B for nested 
types**. Below is how it would cover each path in 
`seatunnel-translation-spark-common`, from reading the current `dev` code.
   
   **One thing that shapes the design:** row conversion in both directions is 
driven by the SeaTunnel row type of the table, not by the Spark `StructType`. 
`InternalRowConverter.convert` / `reconvert` switch on 
`SeaTunnelDataType.getSqlType()`, and `MultiTableManager` keeps one serializer 
per table id built from its `CatalogTable`. I found no main-code caller of 
`TypeConverterUtils.convert(DataType)` (Spark schema → SeaTunnel) outside the 
class itself, so the sink side does not rebuild its schema from Spark metadata 
today. The metadata in C is still worth carrying so that the Spark schema 
round-trips on its own, but the value conversion does not depend on it.
   
   **Proposed handling per path** (`DECIMAL(p, s)` with `p > 38`, called "wide 
decimal" below):
   
   1. **Schema (SeaTunnel → Spark), `TypeConverterUtils.convert`:**
      - Top-level field, including fields of a nested `ROW` (they go through 
`convert(SeaTunnelRowType)` and get `StructField` metadata): `StringType` plus 
metadata `seatunnel_decimal_precision = p`, `seatunnel_decimal_scale = s`.
      - Element of `ARRAY`, or key/value of `MAP`: fail before any row is read 
with a `SeaTunnelRuntimeException` that names the table, the column path (e.g. 
`t.col.element`, `t.col.key`), the declared precision, the Spark limit of 38, 
and the workaround (`Sql` transform `CAST(col AS STRING)`, or `CAST(... AS 
DECIMAL(38, 0))` if the range allows).
   2. **Row write (SeaTunnel → Spark), `InternalRowConverter.convert`, `case 
DECIMAL`:** wide decimal → `UTF8String.fromString(value.toPlainString())`; `p 
<= 38` unchanged (`Decimal.apply`).
   3. **Row read (Spark → SeaTunnel), `InternalRowConverter.reconvert`, `case 
DECIMAL`:** `engineRow.get(i, convert(fieldType))` returns a `UTF8String` for a 
wide decimal, so add `UTF8String` → `new BigDecimal(field.toString())`, then 
check that the scale matches the declared type. Side note: this case currently 
falls through into `case ARRAY` when the value is neither `Decimal` nor 
`BigDecimal`; the PR would add the missing `break` / error.
   4. **Schema read-back (Spark → SeaTunnel), `convert(StructType)`:** if the 
decimal metadata is present, return `DecimalType(p, s)`, the same way the 
`LOGICAL_TIME_TYPE_FLAG` and `TIMESTAMP_TZ` flags are handled now.
   5. **Nested types:** covered by the preflight failure in (1).
   
   **Open trade-off on nested types.** Because rows are converted from the 
SeaTunnel type, `ARRAY` elements and `MAP` keys/values could use the same 
string carrier (`ArrayType(StringType)`, with the converters handling the 
elements). The only loss is that the Spark schema alone would not show the 
original type, since array elements and map entries cannot carry `StructField` 
metadata. With B for nested types, a Doris `ARRAY<largeint>` / `MAP<..., 
largeint>` column on Spark goes from working on `DECIMAL(20, 0)` to a clear 
failure. That is still a regression for those columns. I'd keep nested as B in 
the first PR to limit scope, unless you'd rather cover nested with the carrier 
from the start.
   
   **Tests and docs for that PR:**
   - Unit tests for schema conversion and row round trip at the top level, 
inside a nested `ROW`, and the preflight failure for `ARRAY` / `MAP`. Values: 
`-2^127`, `2^127 - 1`, `0`, `NULL`, and a non-zero scale such as `DECIMAL(50, 
10)`. Run on Spark 2.4 and 3.3.
   - Keep the `DorisIT` / `DorisMultiReadIT` Spark containers enabled, and add 
a Spark case for a wide decimal reaching a sink with the full value.
   - EN/ZH docs for Spark behavior with `DECIMAL` precision > 38, plus an 
`incompatible-changes.md` entry for the Spark-visible type change.
   
   Still open from the issue: whether the MySQL `DECIMAL(65)` → `DECIMAL(38, 
18)` clamp belongs here or in a separate issue. I'd track it separately, 
because it is a source-side clamp that happens on every engine, not only Spark.
   


-- 
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