fskorgen opened a new issue, #8216:
URL: https://github.com/apache/hop/issues/8216

   ### Apache Hop version?
   
   2.19
   
   ### Java version?
   
   21
   
   ### Operating system
   
   Windows
   
   ### What happened?
   
   Regression against 2.18 — introduced by `7ab2b1266` ("issue #7251:
   fine-tuning of DDL generation", #7287).
   
   The new hard-coded JDBC mapper redefines what `length` means for 
DECIMAL/NUMERIC:
   
   ```java
   // Database.java, getDataTypeFromKnownSqlType
   length = rm.getPrecision(columnIndex);
   precision = rm.getScale(columnIndex);
   // Precision in the database means the number of significant digits.
   // In Hop, it means the digits before the decimal.
   if (length > 0 && length > precision) {
     length -= precision;
   }
   ```
   
   `DECIMAL(24,15)` now yields Hop length 9, `DECIMAL(10,6)` yields length 4. 
Before 2.19 both kept
   the database precision as `length`.
   
   The commit changed `Database.java`, `Const.java` and the docs only. No 
`getFieldDefinition`
   implementation was migrated to the new meaning. 30 dialects emit a scaled 
`DECIMAL`/`NUMERIC`
   of their own in 2.19.0, and 29 of them still pass Hop `length` through as 
the database precision:
   
   `as400`, `cache`, `clickhouse`, `db2`, `derby`, `doris`, `duckdb`, 
`exasol4`, `firebird`,
   `generic`, `h2`, `hive`, `hypersonic`, `impala`, `informix`, `ingres`, 
`interbase`, `iris`,
   `kingbasees`, `monetdb`, `mssql`, `mysql`, `sapdb`, `singlestore`, 
`snowflake`, `sybase`,
   `sybaseiq`, `teradata`, `universe`.
   
   Only PostgreSQL matches the new meaning — `NUMERIC(length + precision, 
precision)`
   (`PostgreSqlDatabaseMeta`).
   
   Subclasses that do not override `getFieldDefinition` follow their parent: 
`mssqlnative`, `mariadb`
   and `infobright` inherit the old meaning, `greenplum`, `redshift` and 
`cockroachdb` inherit
   PostgreSQL's.
   
   ### Steps to reproduce
   
   No server needed — an in-memory H2 database is enough to read the metadata, 
and `getFieldDefinition`
   shows what each dialect would write back:
   
   1. `create table t (d decimal(24,15), e decimal(10,6), g int)` on an 
in-memory H2.
   2. `Database.getQueryFields("select * from t")` gives:
   
      ```
      d  Number  length=9  precision=15   (origType=3 DECIMAL)
      e  Number  length=4  precision=6    (origType=3 DECIMAL)
      g  Integer length=9  precision=0
      ```
   
   3. Ask the dialects what they write back for that same row metadata:
   
      ```
      MSSQL / MSSQLNATIVE   d -> DECIMAL(9,15)     e -> DECIMAL(4,6)
      MYSQL                 d -> DOUBLE            e -> DOUBLE
      POSTGRESQL            d -> NUMERIC(24, 15)   e -> NUMERIC(10, 6)
      ```
   
   Only PostgreSQL round-trips. SQL Server rejects both `DECIMAL(9,15)` and 
`DECIMAL(4,6)` because the
   scale exceeds the precision. On MySQL the effect is quieter: the branch is
   `length > 15 ? DECIMAL(length, precision) : DOUBLE`, so a column that used 
to come back as an exact
   `DECIMAL(24,15)` now becomes floating-point `DOUBLE`.
   
   A pipeline that read and re-created a table in 2.18 therefore no longer 
round-trips.
   
   ### The same change also alters the in-memory Hop type, on every database
   
   The type choice below the subtraction is `if (length > 15 || precision > 15) 
valtype =
   TYPE_BIGNUMBER;`, and it now tests the reduced length. An exact decimal 
whose integer part is 15
   digits or fewer therefore drops from BigNumber (`java.math.BigDecimal`) to 
Number
   (`java.lang.Double`), silently, on read:
   
   | Column | 2.19 (default) | `HOP_DB_DDL_COMPATIBLE=true` |
   |---|---|---|
   | `decimal(16,1)` | Number, length 15 | BigNumber, length 16 |
   | `decimal(18,10)` | Number, length 8 | BigNumber, length 18 |
   | `decimal(24,15)` | Number, length 9 | BigNumber, length 24 |
   | `decimal(20,2)` | BigNumber, length 18 | BigNumber, length 20 |
   | `decimal(30,20)` | BigNumber, length 10 | BigNumber, length 30 |
   
   A `double` holds roughly 15-17 significant decimal digits, so every value in 
a `decimal(24,15)`
   column now goes through a type that cannot represent it exactly. This 
affects reading alone — no DDL
   generation, no SQL Server — and applies to any JDBC source with such a 
column. The rows above were
   produced against an in-memory H2 by flipping only `HOP_DB_DDL_COMPATIBLE`.
   
   Reproduced against Apache Hop 2.19.0 using an in-memory H2 connection for 
the read side and
   `MsSqlServerDatabaseMeta`, `MySqlDatabaseMeta` and `PostgreSqlDatabaseMeta` 
for DDL generation.
   
   ### Suggested fix
   
   Two defects come out of the same three lines, and a fix has to address both.
   
   *DDL generation.* Either revert the subtraction, or migrate every 
`getFieldDefinition` (and any
   other reader of `IValueMeta.getLength()` for numeric types) to the new 
meaning. The first is the
   backward-compatible one; the second is presumably what the change intended, 
but it is a much wider
   edit than one method: 29 dialects, plus every other reader of `getLength()` 
on a numeric field.
   
   *The read-side type change.* Migrating `getFieldDefinition` does not fix it. 
The
   `length > 15 || precision > 15` test sits below the subtraction and must be 
evaluated against the
   database precision whichever meaning `length` ends up carrying — otherwise 
an exact
   `decimal(24,15)` keeps arriving as `java.lang.Double` on every database, DDL 
or no DDL. Reordering
   the test above the subtraction is enough if the subtraction stays.
   
   `HOP_DB_DDL_COMPATIBLE=true` restores the old behaviour, which is a good 
escape hatch — but note it
   is read once into a `static` field from a system property, so it cannot be 
set per connection or
   per pipeline.
   
   
   ### Issue Priority
   
   Priority: 1
   
   ### Issue Component
   
   Component: Database


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