vlsi commented on PR #2488:
URL: https://github.com/apache/calcite/pull/2488#issuecomment-6113508853

   I suggest closing this PR, and either resolving CALCITE-2489 as "Won't Fix" 
or narrowing it to an opt-in ordering. The PR fixes an instability nobody has 
reported, and in exchange it changes the column order of existing reflective 
tables on upgrade.
   
   **The instability has not been observed.** `Class.getFields()` has no 
specified order, but HotSpot 8, 11, 17, 21 and 25, OpenJ9 17 and 21, and 
GraalVM 17 all return public instance fields in declaration order, subclass 
fields first. JIRA has no report of a reflective table whose columns came out 
in an unexpected order: CALCITE-2489, CALCITE-4436, and CALCITE-4503, and the 
review comments on CALCITE-2404, all come from reading the code. On 
CALCITE-2489 Julian wrote: "I doubt that anyone has ever suffered from this 
issue in the real world." The 2021 dev@ thread observed the same: when a plan 
rebuilds a row of the table's class, generated code calls `new C(...)` with 
arguments in `getFields()` order, so a JVM that reordered fields would already 
break those queries, and none has been reported.
   
   **The new default changes column order for existing users.** `CONSTRUCTOR` 
falls back to alphabetical order, base-class fields first, whenever the class 
is compiled without `-parameters` (javac, Maven, and Gradle leave it off by 
default) or has no constructor whose parameters name its fields. Take a typical 
row class:
   
   ```java
   public class Order {
     public final int id;
     public final String customer;
     public final double amount;
   
     public Order(int id, String customer, double amount) { ... }
   }
   ```
   
   Today its columns are `id, customer, amount`. With this PR they become 
`amount, customer, id`. After an upgrade, with no change on the user's side:
   
   - `select * from orders` returns the columns in a different order, so any 
code that maps a `ResultSet` by position breaks.
   - `rs.getInt(1)` returns `(int) amount` instead of `id`. Avatica converts a 
`DOUBLE` to `int` silently, so there is no exception, only wrong data.
   - `select * from orders union all select * from archive.orders`, where 
`archive.orders` is a JDBC table with `id, customer, amount`, still validates: 
column 1 is derived as `DOUBLE` and mixes amounts with ids. `insert into t 
select * from orders` misaligns the same way.
   - `order by 1` in queries and in model views now sorts by `amount`.
   - Tests that pin plans see different input references; this PR had to 
rewrite the `depts2` plan in `MaterializedViewRelOptRulesTest` (`FLOOR($t3, …)` 
became `FLOOR($t7, …)`, and the join key `$4` became `$3`).
   - `select * from "metadata".COLUMNS` starts with `bufferLength, 
charOctetLength, columnDef, columnName` instead of `tableCat, tableSchem, 
tableName`. Only `MetaTable` is special-cased.
   - Classes compiled with `-parameters` (Spring Boot's Gradle plugin and its 
Maven parent POM turn it on) get constructor order instead: in 
`testDateColumn`, `hireDate` of `EmployeeWithHireDate` moves from the first 
column to the last. Column order therefore starts to depend on a compiler flag 
of the user's build, and turning that flag on or off reorders the columns again.
   - A Geode region whose value class has private fields loses those columns, 
because `JavaTypeFactoryExtImpl` now keeps public fields only; queries that 
name them fail with "Column ... not found".
   
   **The opt-in modes return wrong rows.** Under `EXPLICIT`, generated code 
still resolves column i through `Types.nthField`, which uses `CONSTRUCTOR` 
order, while the row type follows the `fields` list. `select "name", "empid" 
from emps where "empid" > 100` throws `ClassCastException: Float cannot be cast 
to Integer`; under `EXPLICIT_TOLERANT`, `select "empid" from emps where "empid" 
> 100` returns 0 rows instead of 3. With `"fieldsOrdering": "ALPHABETICAL"` and 
no `fields`, the row type stays in constructor order while `scan()` returns 
values alphabetically.
   
   **The branch is far behind.** It conflicts with main in 11 files, including 
`ReflectiveSchema.Factory` (`@ReflectiveSchema.Input`, CALCITE-7712) and the 
record handling in `ConstantExpression` (CALCITE-6244).
   
   If someone needs a stable order, a new PR could keep `getFields()` order as 
the default and add the strategies from the 2021 dev@ thread as opt-in model 
operands, with every ordinal lookup (`nthField`, `RecordEnumeratorCursor`, 
`TableScanNode`) reading the same ordering as the row type. Java records are a 
separate and real gap: their fields are private, so a record-backed table in 
`ReflectiveSchema` has no columns today, and `Class#getRecordComponents()` 
gives them a defined order.
   


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