zhengxiang378928908-code opened a new issue, #12841:
URL: https://github.com/apache/seatunnel/issues/12841

   ### Search before asking
   
   - [x] I searched in the issues and found nothing similar.
   
   ### What happened
   
   Split out of #12814, where the Spark wide-decimal policy is discussed. That 
issue explicitly keeps this source-side clamp out of scope because it happens 
on every engine, not only Spark.
   
   `MySqlTypeConverter.convert` maps any signed `DECIMAL(p, s)` with `p > 38` 
to `DECIMAL(38, 18)`, whatever the original scale:
   
   ```java
   case MYSQL_DECIMAL:
       ...
       if (typeDefine.getPrecision() > DEFAULT_PRECISION) {   // 38
           log.warn("{} will probably cause value overflow.", MYSQL_DECIMAL);
           decimalType = new DecimalType(DEFAULT_PRECISION, DEFAULT_SCALE); // 
(38, 18)
       }
   ```
   
   From reading the current `dev` code (`51f553cb4`, not yet reproduced end to 
end), this causes:
   
   1. **Integer digits are cut to 20.** `DECIMAL(65, 0)` becomes `DECIMAL(38, 
18)`, which has room for only 20 integer digits. A column declared `DECIMAL(30, 
0)` loses 10 integer digits, even though it needs no scale at all.
   2. **Fraction digits above 18 are cut.** `DECIMAL(50, 30)` becomes scale 18.
   3. **The declared type no longer matches the values.** The JDBC row 
converter reads the value with `getBigDecimal` and does not rescale it 
(`AbstractJdbcRowConverter`, `case DECIMAL`), so a row can carry e.g. a 
26-digit integer while the column type says `DECIMAL(38, 18)`. The mismatch 
shows up later in the sink, far from its cause.
   4. **It even breaks MySQL -> MySQL.** MySQL supports precision up to 65, and 
the MySQL sink side (`reconvert`) accepts up to `MAX_PRECISION = 65`. But an 
auto-created sink table gets `DECIMAL(38, 18)`, so values with more than 20 
integer digits fail with `Out of range value` in strict mode, or are silently 
clamped to `99999999999999999999.999999999999999999` in non-strict mode. 
Fraction digits beyond 18 are rounded.
   5. **Signed and unsigned behave differently.** `DECIMAL UNSIGNED` is not 
clamped: `DECIMAL(p, s) UNSIGNED` becomes `DECIMAL(p + 1, s)`, so `DECIMAL(65, 
0) UNSIGNED` becomes `DECIMAL(66, 0)` while the signed `DECIMAL(65, 0)` becomes 
`DECIMAL(38, 18)`.
   6. **The only signal is a WARN.** The log line has no column name or target 
type in it, and `GenericTypeConverter` does the same clamp without any log at 
all.
   
   The same `p > 38 -> (38, 18)` clamp exists in:
   
   - `MySqlTypeConverter` (also used by MySQL-CDC through `MySqlTypeUtils`)
   - `OceanBaseMySqlTypeConverter`
   - `GenericTypeConverter`
   
   (Other converters such as Oracle, Dameng, Xugu and YashanDB use `(38, 18)` 
only for an unknown precision, or their database maximum is 38 anyway, so they 
are not affected.)
   
   For comparison, `PostgresTypeConverter` keeps the declared precision 
(`NUMERIC(50, 0)` -> `DECIMAL(50, 0)`), and sinks with a lower limit already 
handle wide decimals in their own `reconvert` (e.g. Oracle / SQL Server clamp 
to their maximum with a WARN that names the column).
   
   ### Possible directions (for discussion)
   
   - **A. Keep the declared type.** Map `DECIMAL(p, s)` to `DECIMAL(p, s)` as 
PostgreSQL already does, and leave narrowing to each sink's `reconvert`. Spark 
is covered separately by #12814. This changes the schema seen by sinks for `p > 
38`, so it needs an `incompatible-changes.md` entry.
   - **B. Keep a 38 cap, but keep integer digits first.** E.g. `DECIMAL(38, 
max(0, 38 - (p - s)))`, plus a WARN with the column name, the original type and 
the new type. Still lossy, but `DECIMAL(65, 0)` would at least become 
`DECIMAL(38, 0)` instead of `DECIMAL(38, 18)`.
   - Either way: make signed and unsigned consistent, apply the same rule to 
the OceanBase and generic converters, and add a WARN to the generic converter.
   
   I'd prefer A, because a value that the source can hold and the sink can hold 
should not be lost in between. I'd like a maintainer's view before writing code.
   
   ### SeaTunnel Version
   
   dev (`51f553cb4`)
   
   ### SeaTunnel Config
   
   ```conf
   env {
     parallelism = 1
     job.mode = "BATCH"
   }
   
   source {
     Jdbc {
       url = "jdbc:mysql://mysql:3306/test"
       driver = "com.mysql.cj.jdbc.Driver"
       user = "root"
       password = "***"
       table_path = "test.t_wide_decimal"   # id INT, amount DECIMAL(65, 0), 
ratio DECIMAL(50, 30)
     }
   }
   
   sink {
     Jdbc {
       url = "jdbc:mysql://mysql:3306/test"
       driver = "com.mysql.cj.jdbc.Driver"
       user = "root"
       password = "***"
       database = "test"
       table = "t_wide_decimal_copy"
       generate_sink_sql = true
       schema_save_mode = "CREATE_SCHEMA_WHEN_NOT_EXIST"
     }
   }
   ```
   
   With a row such as `amount = 12345678901234567890123456` (26 digits), the 
auto-created `t_wide_decimal_copy.amount` is expected to be `DECIMAL(38, 18)` 
and the write is expected to fail with `Out of range value` (strict mode).
   
   ### Running Command
   
   ```shell
   ./bin/seatunnel.sh --config mysql_wide_decimal.conf -m local
   ```
   
   ### Error Exception
   
   ```log
   Expected from the code path (not yet reproduced):
   WARN  MySqlTypeConverter - DECIMAL will probably cause value overflow.
   ...
   java.sql.SQLException: Out of range value for column 'amount' at row 1
   ```
   
   ### Zeta or Flink or Spark Version
   
   All engines (the clamp is in the source type converter). Spark has an 
additional limit tracked in #12814.
   
   ### Java or Scala Version
   
   Java 8 / 11
   
   ### Screenshots
   
   _No response_
   
   ### Are you willing to submit PR?
   
   - [x] Yes I am willing to submit a PR!
   
   ### Code of Conduct
   
   - [x] I agree to follow this project's [Code of 
Conduct](https://www.apache.org/foundation/policies/conduct)
   


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