[FLINK-39780] Make TABLE keyword optional in LATERAL context#28276
[FLINK-39780] Make TABLE keyword optional in LATERAL context#28276fhueske wants to merge 2 commits into
Conversation
Extend the SQL parser accept the implicit table-function-call form (e.g. "FROM t, LATERAL fn(args)") in addition to the explicit "LATERAL TABLE(fn(args))" form. Mirrors FLINK-36824, which made the TABLE wrapper optional outside LATERAL. Add tests in FlinkSqlParserImplTest and overwrites methods in Calcite's SqlParserTest that need adjustments.
| | | ||
| // LATERAL with implicit table function call syntax, | ||
| // e.g. "FROM t, LATERAL fn(...)" instead of "FROM t, LATERAL TABLE(fn(...))". | ||
| // The non-LATERAL implicit form is handled by the CompoundTableIdentifier | ||
| // branch above. | ||
| LOOKAHEAD(2) | ||
| <LATERAL> { lateral = true; } | ||
| tableName = CompoundTableIdentifier() | ||
| tableRef = ImplicitTableFunctionCallArgs(tableName) | ||
| tableRef = addLateral(tableRef, lateral) |
There was a problem hiding this comment.
this is the Calcite file from https://github.com/apache/calcite/blob/main/core/src/main/codegen/templates/Parser.jj
The only reason we have it here is a number of backports from Calcite.
In case we need to make changes here: ideally first need to make them in Calcite and then backport here
There was a problem hiding this comment.
Ideally this file should be removed once we update Calcite to versions containing all the backports
There was a problem hiding this comment.
Agree this is danger zone
| [ TABLE ] tablePath [ dynamicTableOptions ] [systemTimePeriod] [[AS] correlationName] | ||
| | LATERAL TABLE '(' functionName '(' expression [, expression ]* ')' ')' | ||
| | [ LATERAL ] functionName '(' expression [, expression ]* ')' | ||
| | [ LATERAL ] TABLE '(' functionName '(' expression [, expression ]* ')' ')' |
There was a problem hiding this comment.
If table is optional
| | [ LATERAL ] TABLE '(' functionName '(' expression [, expression ]* ')' ')' | |
| | [ LATERAL ] [TABLE] '(' functionName '(' expression [, expression ]* ')' ')' |
| 在 Table API 中,表值函数是通过 `.joinLateral(...)` 或者 `.leftOuterJoinLateral(...)` 来使用的。`joinLateral` 算子会把外表(算子左侧的表)的每一行跟跟表值函数返回的所有行(位于算子右侧)进行 (cross)join。`leftOuterJoinLateral` 算子也是把外表(算子左侧的表)的每一行跟表值函数返回的所有行(位于算子右侧)进行(cross)join,并且如果表值函数返回 0 行也会保留外表的这一行。 | ||
|
|
||
| 在 SQL 里面用 `JOIN` 或者 以 `ON TRUE` 为条件的 `LEFT JOIN` 来配合 `LATERAL TABLE(<TableFunction>)` 的使用。 | ||
| 在 SQL 里面用 `JOIN` 或者 以 `ON TRUE` 为条件的 `LEFT JOIN` 来配合 `LATERAL <TableFunction>(...)` 的使用(等价的写法为 `LATERAL TABLE(<TableFunction>(...))`)。 |
There was a problem hiding this comment.
AFAK we only add the english text as place holder and let folks translate it to Chinese
| [ TABLE ] tablePath [ dynamicTableOptions ] [systemTimePeriod] [[AS] correlationName] | ||
| | LATERAL TABLE '(' functionName '(' expression [, expression ]* ')' ')' | ||
| | [ LATERAL ] functionName '(' expression [, expression ]* ')' | ||
| | [ LATERAL ] TABLE '(' functionName '(' expression [, expression ]* ')' ')' |
There was a problem hiding this comment.
| | [ LATERAL ] TABLE '(' functionName '(' expression [, expression ]* ')' ')' | |
| | [ LATERAL ] [TABLE] '(' functionName '(' expression [, expression ]* ')' ')' |
| | | ||
| // LATERAL with implicit table function call syntax, | ||
| // e.g. "FROM t, LATERAL fn(...)" instead of "FROM t, LATERAL TABLE(fn(...))". | ||
| // The non-LATERAL implicit form is handled by the CompoundTableIdentifier | ||
| // branch above. | ||
| LOOKAHEAD(2) | ||
| <LATERAL> { lateral = true; } | ||
| tableName = CompoundTableIdentifier() | ||
| tableRef = ImplicitTableFunctionCallArgs(tableName) | ||
| tableRef = addLateral(tableRef, lateral) |
There was a problem hiding this comment.
Agree this is danger zone
| /** | ||
| * Overrides the parent {@link org.apache.calcite.sql.parser.SqlParserTest#testLateral()} | ||
| * because Flink makes the {@code TABLE} keyword optional inside {@code LATERAL}. With this | ||
| * change, {@code LATERAL <identifier>} is the start of an implicit table-function call; the | ||
| * parser expects an argument list next, so the error position shifts. | ||
| */ |
There was a problem hiding this comment.
Do we really need so many javadocs and comments on tests? Claude loves to do it but IMO test should be clean enough to read with a good function name
| * unexpected token ({@code for}). | ||
| */ | ||
| @Test | ||
| void testTemporalTable() { |
There was a problem hiding this comment.
Seems like a good candidate for parasitized tests. WDYT?
What is the purpose of the change
Extend the SQL parser accept the implicit table-function-call form (e.g. "FROM t, LATERAL fn(args)") in addition to the explicit "LATERAL TABLE(fn(args))" form.
Mirrors FLINK-36824, which made the TABLE wrapper optional outside LATERAL.
Brief change log
Parser.jjto accept table function calls inLATERALwithoutTABLEkeyword.Verifying this change
FlinkSqlParserImplTest(by overwriting them) to fix the error positionsFlinkSqlParserImplTestDoes this pull request potentially affect one of the following parts:
@Public(Evolving): yesDocumentation
Documentation is adjusted. We now use the shorter, implicit syntax for table function calls in LATERAL.
The SQL syntax overview is adjusted to cover both cases (LATERAL with and without TABLE keyword).
Also extended the overview with syntax for non-LATERAL table function calls with and without TABLE keyword which was missing so far.
I double-checked that the AI-generated Chinese text corresponds to the English version using Google Translate 🤖
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code 2.1.148 (Opus 4.7)