pepijnve opened a new issue, #27209:
URL: https://github.com/apache/datafusion/issues/27209

   ### Is your feature request related to a problem or challenge?
   
   Table formats such as Delta Lake, Iceberg and Paimon can retains multiple 
versions of a
   table. These can be queried via SQL using `AS OF` syntax :
   
   ```sql
   SELECT * FROM t VERSION AS OF 3;
   SELECT * FROM t TIMESTAMP AS OF '2024-01-01';
   ```
   
   `sqlparser` parses these clauses for several dialects (`VERSION AS OF`,
   `TIMESTAMP AS OF`, `FOR SYSTEM_TIME AS OF`, `AT (...)`), but DataFusion has 
no
   way to pass the requested version to a catalog:
   
   - The default planner silently ignores the clause. With the Databricks,
     Snowflake, BigQuery or MsSQL dialects, `SELECT * FROM t VERSION AS OF 3`
     returns the *current* version of `t` (see also #24754).
   - `SchemaProvider::table` only takes a table name, so a catalog cannot be
     asked for a specific version.
   - The only workaround is a `RelationPlanner` extension, as Paimon uses. It
     intercepts the clause, looks up the current table and builds a scan of the
     requested snapshot itself. This approach works, but has a correctness 
problem due to `TableScan` equality and hashing ignoring the table source. 
Scans of different versions of the same table compare equal. Since a subquery's 
plan is part of its expression's equality, the optimizer can merge subqueries 
over
     different versions:
   
     ```sql
     SELECT
       (SELECT count(*) FROM t AT (VERSION => 1)) AS v1,
       (SELECT count(*) FROM t AT (VERSION => 2)) AS v2;
     -- expected: 1, 2
     -- with versions not taken into account in TableScan equality: 1, 1
     ```
   
   ### Describe the solution you'd like
   
   First-class time travel support in the SQL planner and catalog API:
   
   - Normalize the time travel syntaxes into a `TableVersion` which can be 
either a scalar value version identifier or a timestamp.
   - Evaluate the version expression during planning, so catalogs only see
     constants. `AT (TIMESTAMP => now() - INTERVAL '1 hour')` works; column
     references do not.
   - Add `SchemaProvider::table_at(name, &TableVersion)`, returning that
     version of the table. The default returns a "not implemented" error rather
     than the current version.
   - Let tables report the version they read (`TableProvider::version()` /
     `TableSource::version()`). The resolved version may differ from the 
requested
     one: a timestamp or a tag typically resolves to a snapshot id.
   - Include the resolved version in `TableScan` equality and hashing. This 
fixes the
     subquery problem above, and show it in `EXPLAIN`, e.g.
     `TableScan: t@3 projection=[id, name]`.
   - Keep existing `RelationPlanner` extensions working: they still run first
     and can keep handling time travel clauses themselves. This avoids breaking 
Paimon's current implementation.
   
   ### Describe alternatives you've considered
   
   - Adding the version to `TableReference`, similar to how DuckDB passes it
     with the table lookup. `TableReference` is also the column qualifier in
     DataFusion, so `SELECT t.a FROM t VERSION AS OF 3` would stop resolving 
`t.a`
     unless the version were stripped everywhere a qualifier is created.
   - Storing the version on `TableScan`. This grows `LogicalPlan`, and the
     version is easily lost when a scan is rebuilt from its source. Keeping it 
with
     the source avoids both.
   - Leaving time travel to `RelationPlanner` extensions. As described above,
     extensions cannot make DataFusion distinguish scans of different versions.
   - Encoding the version in the table name (e.g. BigQuery-style
     `t@1234`, discussed in #7292). This changes column qualifiers and is hard 
to
     use with computed versions.
   
   ### Additional context
   
   - Earlier discussion: #7292.
   - The default `GenericDialect` does not parse time travel clauses yet, and
     sqlparser only accepts number literals after `VERSION AS OF`. Both need
     sqlparser changes, which I will propose upstream. `AT`/`BEFORE` also need a
     guard there so they don't break unaliased table names like `FROM t at`.
   - Prepared statement parameters in time travel clauses (`VERSION AS OF $1`) 
are
     out of scope: parameters are bound after planning, while the version
     determines the table (and potentially its schema) during planning.
   
   ### Willingness to contribute
   
   I plan to contribute to this feature independently. I have a working
   implementation and will open a PR referencing this issue.
   


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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to