jonathansudhakar1 opened a new issue, #41017:
URL: https://github.com/apache/beam/issues/41017

   ### What happened?
   
   A Beam SQL Parquet table can write a `DATE` column, but reading it back 
fails. On master (74ab567d2e3), `SELECT *` throws a `DateTimeException`:
   
   ```
   java.time.DateTimeException: Invalid value for EpochDay (valid values 
-365243219162 - 365241780471): 1752451200000
        at java.time.LocalDate.ofEpochDay(LocalDate.java:345)
        at 
org.apache.beam.sdk.extensions.sql.impl.rel.BeamCalcRel.toBeamObject(BeamCalcRel.java:443)
   ```
   
   Projecting the column (`SELECT d FROM t` on a table `t (id INTEGER, d 
DATE)`) throws a `ClassCastException` instead:
   
   ```
   java.lang.ClassCastException: class org.joda.time.Instant cannot be cast to 
class java.time.LocalDate
        at 
org.apache.beam.sdk.schemas.logicaltypes.Date.toBaseType(Date.java:35)
   ```
   
   Minimal repro, run as a JUnit test in `sdks/java/extensions/sql` with the 
DirectRunner (`writePipeline` and `readPipeline` are `TestPipeline`s):
   
   ```java
   BeamSqlEnv env = BeamSqlEnv.inMemory(new ParquetTableProvider());
   env.executeDdl("CREATE EXTERNAL TABLE t (d DATE) TYPE parquet LOCATION 
'/tmp/date-table/'");
   BeamSqlRelUtils.toPCollection(
       writePipeline, env.parseQuery("INSERT INTO t VALUES (DATE 
'2025-07-14')"));
   writePipeline.run().waitUntilFinish(); // OK: d is written as an Avro int 
(date)
   
   BeamSqlRelUtils.toPCollection(readPipeline, env.parseQuery("SELECT * FROM 
t"));
   readPipeline.run().waitUntilFinish(); // DateTimeException above
   ```
   
   1752451200000 is 2025-07-14T00:00Z in epoch **millis**, while 
`LocalDate.ofEpochDay` expects epoch days.
   
   **Cause.** The read path gives SQL rows whose schema is inferred from Avro, 
not the table's schema:
   - `ParquetTable.buildIOReader` reads with 
`ParquetIO.read(schema).withBeamSchemas(true)` and then applies 
`Convert.toRows()` 
([L62-L63](https://github.com/apache/beam/blob/74ab567d2e389b61dec063839325bf88bab4484e/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/provider/parquet/ParquetTable.java#L62-L63),
 
[L71-L77](https://github.com/apache/beam/blob/74ab567d2e389b61dec063839325bf88bab4484e/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/provider/parquet/ParquetTable.java#L71-L77)).
   - With `withBeamSchemas(true)`, ParquetIO's coder is 
`AvroUtils.schemaCoder(avroSchema)` ([ParquetIO 
L730-L732](https://github.com/apache/beam/blob/74ab567d2e389b61dec063839325bf88bab4484e/sdks/java/io/parquet/src/main/java/org/apache/beam/sdk/io/parquet/ParquetIO.java#L730-L732)).
 That infers the Beam schema from Avro, and an Avro `date` becomes `DATETIME` 
([AvroUtils 
L1058-L1059](https://github.com/apache/beam/blob/74ab567d2e389b61dec063839325bf88bab4484e/sdks/java/extensions/avro/src/main/java/org/apache/beam/sdk/extensions/avro/schemas/utils/AvroUtils.java#L1058-L1059)).
 Its value is a Joda `Instant` ([`convertDateStrict`, 
L1665-L1668](https://github.com/apache/beam/blob/74ab567d2e389b61dec063839325bf88bab4484e/sdks/java/extensions/avro/src/main/java/org/apache/beam/sdk/extensions/avro/schemas/utils/AvroUtils.java#L1665-L1668)).
   - The table schema says the column is 
`FieldType.logicalType(SqlTypes.DATE)`, backed by `java.time.LocalDate` 
([CalciteUtils 
L111](https://github.com/apache/beam/blob/74ab567d2e389b61dec063839325bf88bab4484e/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/utils/CalciteUtils.java#L111)).
 So SQL receives an `Instant` where it expects a `LocalDate`.
   
   The write side was fixed in #39414, so only reads are affected now. In the 
same temporary test, every other scalar type round-trips through `INSERT` and 
`SELECT *`: TINYINT, SMALLINT, INTEGER, BIGINT, FLOAT, DOUBLE, DECIMAL, 
BOOLEAN, TIMESTAMP, CHAR, VARCHAR, BINARY and VARBINARY.
   
   **Expected:** `SELECT` on a DATE column returns the `LocalDate` that was 
written.
   
   **Possible fix** (I'd like a maintainer's view before sending one, since 
`AvroUtils` is shared by many IOs):
   1. In `AvroUtils`, let the strict Avro-to-Beam conversion turn a `date` 
value into a `LocalDate` when the target field type is `SqlTypes.DATE`. Today 
`convertDateStrict` rejects every target except `DATETIME`. This is additive: 
callers that infer the schema from Avro still get `DATETIME`.
   2. In `ParquetTable`, convert the records with the table's own Beam schema, 
or its projected subset, instead of the Avro-inferred one. For example, use 
`ParquetIO.read(schema)` without `withBeamSchemas`, followed by `MapElements` 
with `AvroUtils.getGenericRecordToRowFunction(tableSchema)` and 
`setRowSchema(tableSchema)`.
   
   Changing `AvroUtils.toBeamSchema` to infer `SqlTypes.DATE` from Avro `date` 
would also work, but it changes behavior for every Avro-schema user, so I'd 
rather not.
   
   ### Issue Priority
   
   Priority: 2 (default / most bugs should be filed as P2)
   
   ### Issue Components
   
   - [ ] Component: Python SDK
   - [x] Component: Java SDK
   - [ ] Component: Go SDK
   - [ ] Component: Typescript SDK
   - [ ] Component: IO connector
   - [ ] Component: Beam YAML
   - [ ] Component: Beam examples
   - [ ] Component: Beam playground
   - [ ] Component: Beam katas
   - [ ] Component: Website
   - [ ] Component: Infrastructure
   - [ ] Component: Spark Runner
   - [ ] Component: Flink Runner
   - [ ] Component: Prism Runner
   - [ ] Component: Hazelcast Jet Runner
   - [ ] Component: Google Cloud Dataflow Runner
   


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