From 8cd098dc8173fa89bc7712ea7bed0c07d1cf3e6d Mon Sep 17 00:00:00 2001 From: Daniel Henneberger Date: Fri, 27 Oct 2023 19:06:16 -0700 Subject: [PATCH 1/8] Table function support Signed-off-by: Daniel Henneberger --- .../java/com/datasqrl/calcite/SqrlToSql.java | 13 ++++++++++ .../calcite/visitor/SqlNodeVisitor.java | 7 ++++- .../calcite/visitor/SqlRelationVisitor.java | 5 ++-- .../plan/validate/ScriptValidator.java | 12 +++++++++ .../plan/validate/SqrlToValidatorSql.java | 26 +++++++++++++++++++ .../org/apache/calcite/jdbc/SqrlSchema.java | 4 +-- .../plan/local/analyze/QuerySnapshotTest.java | 17 ++++++++++++ .../QuerySnapshotTest/callTableFunction.txt | 23 ++++++++++++++++ .../chainedTableFncCallTest.txt | 18 +++++++++++++ 9 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/callTableFunction.txt create mode 100644 sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/chainedTableFncCallTest.txt diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java index 87d2ab82e..3037f4cac 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java @@ -1,5 +1,6 @@ package com.datasqrl.calcite; +import static com.datasqrl.plan.validate.ScriptValidator.addError; import static com.datasqrl.plan.validate.ScriptValidator.isSelfTable; import static com.datasqrl.plan.validate.ScriptValidator.isVariable; @@ -16,6 +17,7 @@ import com.datasqrl.calcite.visitor.SqlNodeVisitor; import com.datasqrl.calcite.visitor.SqlRelationVisitor; import com.datasqrl.canonicalizer.ReservedName; +import com.datasqrl.error.ErrorLabel; import com.datasqrl.plan.hints.TopNHint.Type; import com.google.common.base.Preconditions; import java.util.ArrayList; @@ -375,6 +377,17 @@ public Result visitSetOperation(SqlCall node, Context context) { operandResults.get(0).params); } + @Override + public Result visitTableFunction(SqlCall node, Context context) { + //Let sql validator resolve table function + return new Result(node, List.of(), List.of(), List.of(), Optional.empty(), parameters); + } + + @Override + public Result visitCall(SqlCall node, Context context) { + throw new RuntimeException("Expected call"); + } + @Value public static class PullupColumn { String columnName; diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlNodeVisitor.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlNodeVisitor.java index 40940d4ef..842277eae 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlNodeVisitor.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlNodeVisitor.java @@ -1,5 +1,6 @@ package com.datasqrl.calcite.visitor; +import com.google.common.base.Preconditions; import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlJoin; @@ -17,6 +18,7 @@ import org.apache.calcite.sql.SqrlSqlQuery; import org.apache.calcite.sql.SqrlStreamQuery; import org.apache.calcite.sql.StatementVisitor; +import org.apache.calcite.sql.fun.SqlCollectionTableOperator; public abstract class SqlNodeVisitor implements SqlRelationVisitor, @@ -48,6 +50,7 @@ public static R accept(StatementVisitor visitor, SqlNode node, C co } public static R accept(SqlRelationVisitor visitor, SqlNode node, C context) { + Preconditions.checkNotNull(node, "Could not rewrite query."); if (node.getKind() == SqlKind.AS) { return visitor.visitAliasedRelation((SqlCall) node, context); } else if (node instanceof SqlIdentifier) { @@ -59,8 +62,10 @@ public static R accept(SqlRelationVisitor visitor, SqlNode node, C } else if (node instanceof SqlCall && SqlKind.SET_QUERY.contains(node.getKind())) { return visitor.visitSetOperation((SqlCall) node, context); - } else if (node instanceof SqlCall) { + } else if (node instanceof SqlCall && ((SqlCall) node).getOperator() instanceof SqlCollectionTableOperator) { return visitor.visitTableFunction((SqlCall) node, context); + } else if (node instanceof SqlCall) { + return visitor.visitCall((SqlCall) node, context); } throw new RuntimeException("Unknown sql statement node:" + node); } diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlRelationVisitor.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlRelationVisitor.java index dda2ad3b7..500612757 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlRelationVisitor.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlRelationVisitor.java @@ -8,7 +8,6 @@ public interface SqlRelationVisitor { R visitTable(SqlIdentifier node, C context); R visitJoin(SqlJoin node, C context); R visitSetOperation(SqlCall node, C context); - default R visitTableFunction(SqlCall node, C context) { - return null; - } + R visitTableFunction(SqlCall node, C context); + R visitCall(SqlCall node, C context); } \ No newline at end of file diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java index 72ae03742..e0d3fca54 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java @@ -469,6 +469,18 @@ public SqlNode visitSetOperation(SqlCall node, Object context) { .map(o->SqlNodeVisitor.accept(this, o, context)) .collect(Collectors.toList())); } + + @Override + public SqlNode visitTableFunction(SqlCall node, Object context) { + SqlCall sqlNode = (SqlCall)node.getOperandList().get(0); + return node.getOperator().createCall(node.getParserPosition(), + sqlNode.accept(rewriteVariables(parameterList, materializeSelf))); + } + + @Override + public SqlNode visitCall(SqlCall node, Object context) { + throw addError(ErrorLabel.GENERIC, node, "Unsupported call: %s", node.getOperator().getName()); + } }, query, null); return Pair.of(parameterList, node); diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java index 975312746..a88250c66 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java @@ -36,11 +36,14 @@ import org.apache.calcite.schema.TableFunction; import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlFunction; +import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlJoin; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlSelect; +import org.apache.calcite.sql.SqlSyntax; import org.apache.calcite.sql.SqrlTableFunctionDef; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParserPos; @@ -248,6 +251,12 @@ public Result visitTable(SqlIdentifier node, Context context) { return new Result(call1, pathWalker.getAbsolutePath(), plannerFns); } + @Override + public Result visitCall(SqlCall node, Context context) { + throw addError(errorCollector, ErrorLabel.GENERIC, node, "Call not yet supported %s", + node.getOperator().getName()); + } + private String getDisplay(SqlIdentifier node) { return String.join(".", node.names); } @@ -291,6 +300,23 @@ public Result visitSetOperation(SqlCall node, Context context) { List.of(), plannerFns); } + @Override + public Result visitTableFunction(SqlCall node, Context context) { + // Wrapped in a SqlCollectionTableOperator + SqlCall tableFunctionCall = (SqlCall)node.getOperandList().get(0); + + List operators = new ArrayList<>(); + planner.getOperatorTable().lookupOperatorOverloads(tableFunctionCall.getOperator().getNameAsId(), + SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION, SqlSyntax.FUNCTION, operators, + planner.getCatalogReader().nameMatcher()); + + if (operators.isEmpty()) { + throw addError(errorCollector, ErrorLabel.GENERIC, tableFunctionCall, "Could not find table function %s", + tableFunctionCall.getOperator().getName()); + } + //We don't actually fully resolve the function, just check that it exists and let the sql validator do the rest + return new Result(node, List.of(), List.of()); + } @AllArgsConstructor public class WalkSubqueries extends SqlShuttle { diff --git a/sqrl-planner/sqrl-common/src/main/java/org/apache/calcite/jdbc/SqrlSchema.java b/sqrl-planner/sqrl-common/src/main/java/org/apache/calcite/jdbc/SqrlSchema.java index 27e9ed027..4ceeed14f 100644 --- a/sqrl-planner/sqrl-common/src/main/java/org/apache/calcite/jdbc/SqrlSchema.java +++ b/sqrl-planner/sqrl-common/src/main/java/org/apache/calcite/jdbc/SqrlSchema.java @@ -84,8 +84,8 @@ public void add(ResolvedExport export) { public void addTable(RootSqrlTable root) { removePrefix(this.pathToAbsolutePathMap.keySet(),root.getName().toNamePath()); plus().add(String.join(".", root.getPath().toStringList()) + "$" - + sqrlFramework.getUniqueMacroInt().incrementAndGet(), root - ); + + sqrlFramework.getUniqueMacroInt().incrementAndGet(), root); + plus().add(root.getName().getDisplay(), root); } private void removePrefix(Set set, NamePath prefix) { diff --git a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java index 75e68cd96..457103ec9 100644 --- a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java +++ b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java @@ -1111,6 +1111,23 @@ public void groupTest() { + " GROUP BY p._uuid;"); } + @Test + public void callTableFunction() { + validateScript("IMPORT ecommerce-data.Orders;\n" + + "X(@id: Int) := SELECT * FROM Orders WHERE id = @id;\n" + + "X(@id: Int, @customerid: Int) := SELECT * FROM Orders WHERE id = @id AND customerid = @customerid;\n" + + "Y(@id: Int) := SELECT * FROM TABLE(X(2));\n" + + "Z(@id: Int) := SELECT * FROM TABLE(X(2, 3));\n"); + } + + @Test + public void chainedTableFncCallTest() { + validateScript("IMPORT ecommerce-data.Orders;\n" + + "X(@id: Int) := SELECT * FROM Orders WHERE id = @id;\n" + + "Y(@id: Int) := SELECT * FROM TABLE(X(@id));\n" + + "Z := SELECT * FROM TABLE(Y(3));\n"); + } + @Test public void orderTest() { validateScript("IMPORT ecommerce-data.Orders;" diff --git a/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/callTableFunction.txt b/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/callTableFunction.txt new file mode 100644 index 000000000..69b12df21 --- /dev/null +++ b/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/callTableFunction.txt @@ -0,0 +1,23 @@ +>>>orders$2 +LogicalTableScan(table=[[orders$1]]) + +>>>x$1 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[=($2, ?0)]) + LogicalTableScan(table=[[orders$2]]) + +>>>x$2 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[AND(=($2, ?0), =($3, ?1))]) + LogicalTableScan(table=[[orders$2]]) + +>>>y$1 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[=($2, 2)]) + LogicalTableScan(table=[[orders$2]]) + +>>>z$1 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[AND(=($2, 2), =($3, 3))]) + LogicalTableScan(table=[[orders$2]]) + diff --git a/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/chainedTableFncCallTest.txt b/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/chainedTableFncCallTest.txt new file mode 100644 index 000000000..1576356c6 --- /dev/null +++ b/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/chainedTableFncCallTest.txt @@ -0,0 +1,18 @@ +>>>orders$2 +LogicalTableScan(table=[[orders$1]]) + +>>>x$1 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[=($2, ?0)]) + LogicalTableScan(table=[[orders$2]]) + +>>>y$1 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[=($2, ?0)]) + LogicalTableScan(table=[[orders$2]]) + +>>>z$1 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[=($2, 3)]) + LogicalTableScan(table=[[orders$2]]) + From f66a179074efb91494459350396d6afbd7e1bc1f Mon Sep 17 00:00:00 2001 From: Daniel Henneberger Date: Fri, 27 Oct 2023 19:29:57 -0700 Subject: [PATCH 2/8] Add support for lateral joins Signed-off-by: Daniel Henneberger --- .../java/com/datasqrl/calcite/SqrlToSql.java | 7 +++++ .../plan/validate/ScriptValidator.java | 5 ++++ .../plan/validate/SqrlToValidatorSql.java | 14 +++++----- .../plan/local/analyze/QuerySnapshotTest.java | 8 ++++++ .../QuerySnapshotTest/lateralJoinTest.txt | 27 +++++++++++++++++++ 5 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/lateralJoinTest.txt diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java index 3037f4cac..64934ac62 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java @@ -44,6 +44,7 @@ import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlJoin; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLateralOperator; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlOperatorTable; @@ -385,6 +386,12 @@ public Result visitTableFunction(SqlCall node, Context context) { @Override public Result visitCall(SqlCall node, Context context) { + if (node.getOperator() instanceof SqlLateralOperator) { + Result result = SqlNodeVisitor.accept(this, node.getOperandList().get(0), context); + SqlCall call = node.getOperator().createCall(node.getParserPosition(), result.sqlNode); + return new Result(call, result.currentPath, result.pullupColumns, result.tableReferences, + result.condition, result.params); + } throw new RuntimeException("Expected call"); } diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java index e0d3fca54..9cdee6d5f 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java @@ -56,6 +56,7 @@ import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.SqlJoin; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLateralOperator; import org.apache.calcite.sql.SqlLiteral; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlNodeList; @@ -479,6 +480,10 @@ public SqlNode visitTableFunction(SqlCall node, Object context) { @Override public SqlNode visitCall(SqlCall node, Object context) { + if (node.getOperator() instanceof SqlLateralOperator) { + SqlNode op = SqlNodeVisitor.accept(this, node.getOperandList().get(0), context); + return node.getOperator().createCall(node.getParserPosition(), op); + } throw addError(ErrorLabel.GENERIC, node, "Unsupported call: %s", node.getOperator().getName()); } }, query, null); diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java index a88250c66..abcde7e44 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java @@ -40,6 +40,7 @@ import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlJoin; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLateralOperator; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlSelect; @@ -80,18 +81,16 @@ public Result visitQuerySpecification(SqlSelect call, Context context) { if (ident.isStar() && ident.names.size() == 1) { for (List path : newContext.getAliasPathMap().values()) { Optional sqrlTable = planner.getTableFunction(path); - if (sqrlTable.isEmpty()) { - throw addError(errorCollector, ErrorLabel.GENERIC, node, "Could not find table %s",getDisplay(ident)); + if (sqrlTable.isPresent()) { + isA.put(context.root, sqrlTable.get().getFunction()); } - isA.put(context.root, sqrlTable.get().getFunction()); } } else if (ident.isStar() && ident.names.size() == 2) { List path = newContext.getAliasPath(ident.names.get(0)); Optional sqrlTable = planner.getTableFunction(path); - if (sqrlTable.isEmpty()) { - throw addError(errorCollector, ErrorLabel.GENERIC, node, "Could not find table %s",getDisplay(ident)); + if (sqrlTable.isPresent()) { + isA.put(context.root, sqrlTable.get().getFunction()); } - isA.put(context.root, sqrlTable.get().getFunction()); } } } @@ -253,6 +252,9 @@ public Result visitTable(SqlIdentifier node, Context context) { @Override public Result visitCall(SqlCall node, Context context) { + if (node.getOperator() instanceof SqlLateralOperator) { + return new Result(node, List.of(), List.of()); + } throw addError(errorCollector, ErrorLabel.GENERIC, node, "Call not yet supported %s", node.getOperator().getName()); } diff --git a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java index 457103ec9..3a52e75ee 100644 --- a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java +++ b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java @@ -1127,6 +1127,14 @@ public void chainedTableFncCallTest() { + "Y(@id: Int) := SELECT * FROM TABLE(X(@id));\n" + "Z := SELECT * FROM TABLE(Y(3));\n"); } + + @Test + public void lateralJoinTest() { + validateScript("IMPORT ecommerce-data.Orders;\n" + + "X(@id: Int) := SELECT * FROM Orders WHERE id = @id;\n" + + "X(@id: Int, @customerid: Int) := SELECT * FROM Orders WHERE id = @id AND customerid = @customerid;\n" + + "Y(@id: Int) := SELECT * FROM TABLE(X(2)) AS t JOIN LATERAL TABLE(X(t.id, 3));\n"); + } @Test public void orderTest() { diff --git a/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/lateralJoinTest.txt b/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/lateralJoinTest.txt new file mode 100644 index 000000000..438e78806 --- /dev/null +++ b/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/lateralJoinTest.txt @@ -0,0 +1,27 @@ +>>>orders$2 +LogicalTableScan(table=[[orders$1]]) + +>>>x$1 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[=($2, ?0)]) + LogicalTableScan(table=[[orders$2]]) + +>>>x$2 +LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5]) + LogicalFilter(condition=[AND(=($2, ?0), =($3, ?1))]) + LogicalTableScan(table=[[orders$2]]) + +>>>y$1 +LogicalProject(_uuid=[$0], _uuid0=[$6], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5], _ingest_time0=[$7], id0=[$8], customerid0=[$9], time0=[$10], entries0=[$11], __timestamp16=[CASE(<($4, $15), $15, $4)]) + LogicalJoin(condition=[=($2, $12)], joinType=[inner]) + LogicalFilter(condition=[=($2, 2)]) + LogicalTableScan(table=[[orders$2]]) + LogicalProject(_uuid=[$0], _ingest_time=[$1], id=[$2], customerid=[$3], time=[$4], entries=[$5], id0=[$6], time0=[$7], _rownum=[$8], __timestamp=[CASE(<($4, $7), $7, $4)]) + LogicalJoin(condition=[=($2, CAST($6):INTEGER NOT NULL)], joinType=[inner]) + LogicalFilter(condition=[=($3, 3)]) + LogicalTableScan(table=[[orders$2]]) + LogicalFilter(condition=[=($2, 1)]) + LogicalProject(id=[$2], time=[$4], _rownum=[ROW_NUMBER() OVER (PARTITION BY $2 ORDER BY $4 DESC NULLS LAST)]) + LogicalFilter(condition=[=($2, 2)]) + LogicalTableScan(table=[[orders$2]]) + From 4d133425b843abe36585a156889d3ba164fc16e2 Mon Sep 17 00:00:00 2001 From: Daniel Henneberger Date: Fri, 27 Oct 2023 20:01:45 -0700 Subject: [PATCH 3/8] Add table literal for direct access of nested tables Signed-off-by: Daniel Henneberger --- .../main/java/org/apache/calcite/jdbc/SqrlSchema.java | 7 ++++++- .../plan/local/analyze/QuerySnapshotTest.java | 10 +++++++++- .../QuerySnapshotTest/joinTableFncCallTest.txt | 11 +++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/joinTableFncCallTest.txt diff --git a/sqrl-planner/sqrl-common/src/main/java/org/apache/calcite/jdbc/SqrlSchema.java b/sqrl-planner/sqrl-common/src/main/java/org/apache/calcite/jdbc/SqrlSchema.java index 4ceeed14f..b476116e2 100644 --- a/sqrl-planner/sqrl-common/src/main/java/org/apache/calcite/jdbc/SqrlSchema.java +++ b/sqrl-planner/sqrl-common/src/main/java/org/apache/calcite/jdbc/SqrlSchema.java @@ -85,7 +85,9 @@ public void addTable(RootSqrlTable root) { removePrefix(this.pathToAbsolutePathMap.keySet(),root.getName().toNamePath()); plus().add(String.join(".", root.getPath().toStringList()) + "$" + sqrlFramework.getUniqueMacroInt().incrementAndGet(), root); - plus().add(root.getName().getDisplay(), root); + if (!root.getParameters().isEmpty()) { + plus().add(root.getName().getDisplay(), root); + } } private void removePrefix(Set set, NamePath prefix) { @@ -107,6 +109,9 @@ public void addRelationship(Relationship relationship) { this.sysTableToRelationshipMap.put(relationship.getFromTable(), relationship); plus().add(String.join(".", relationship.getPath().toStringList()) + "$" + sqrlFramework.getUniqueMacroInt().incrementAndGet(), relationship); + if (!relationship.getParameters().isEmpty()) { + plus().add(String.join(".", relationship.getPath().toStringList()), relationship); + } } public void addTableMapping(NamePath path, String nameId) { diff --git a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java index 3a52e75ee..273cf1886 100644 --- a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java +++ b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java @@ -1127,7 +1127,15 @@ public void chainedTableFncCallTest() { + "Y(@id: Int) := SELECT * FROM TABLE(X(@id));\n" + "Z := SELECT * FROM TABLE(Y(3));\n"); } - + + @Test + public void joinTableFncCallTest() { + validateScript("IMPORT ecommerce-data.Orders;\n" + + "IMPORT ecommerce-data.Product;\n" + + "Orders.entries.product(@id: Int) := JOIN Product p ON p.productid = @id;\n" + + "Y(@id: Int) := SELECT * FROM TABLE(`Orders.entries.product`(@id));"); + } + @Test public void lateralJoinTest() { validateScript("IMPORT ecommerce-data.Orders;\n" diff --git a/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/joinTableFncCallTest.txt b/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/joinTableFncCallTest.txt new file mode 100644 index 000000000..b4e5d9c1c --- /dev/null +++ b/sqrl-planner/sqrl-planner-local/src/test/resources/snapshots/com/datasqrl/plan/local/analyze/QuerySnapshotTest/joinTableFncCallTest.txt @@ -0,0 +1,11 @@ +>>>orders$2 +LogicalTableScan(table=[[orders$1]]) + +>>>product$2 +LogicalTableScan(table=[[product$1]]) + +>>>y$1 +LogicalProject(_uuid=[$0], _ingest_time=[$1], productid=[$2], name=[$3], description=[$4], category=[$5]) + LogicalFilter(condition=[=($2, ?0)]) + LogicalTableScan(table=[[product$2]]) + From b8e265b6de562a995bb100865b8bb708f9037c82 Mon Sep 17 00:00:00 2001 From: Daniel Henneberger Date: Fri, 27 Oct 2023 20:09:29 -0700 Subject: [PATCH 4/8] Add edge case Signed-off-by: Daniel Henneberger --- .../main/java/com/datasqrl/plan/validate/ScriptValidator.java | 1 + .../com/datasqrl/plan/local/analyze/QuerySnapshotTest.java | 3 +++ 2 files changed, 4 insertions(+) diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java index 9cdee6d5f..d7331e817 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java @@ -355,6 +355,7 @@ public void validateTable(SqrlAssignment statement, SqlNode query, } } + System.out.println(sqlNode); validated = validator.validate(sqlNode); } catch (CalciteContextException e) { throw addError(ErrorLabel.GENERIC, e); diff --git a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java index 273cf1886..9183e29d6 100644 --- a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java +++ b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java @@ -1129,10 +1129,13 @@ public void chainedTableFncCallTest() { } @Test + @Disabled + //todo: Illegal use of dynamic param error public void joinTableFncCallTest() { validateScript("IMPORT ecommerce-data.Orders;\n" + "IMPORT ecommerce-data.Product;\n" + "Orders.entries.product(@id: Int) := JOIN Product p ON p.productid = @id;\n" + + "Orders.entries.product(@id: Int) := JOIN Product p ON p.productid = @id;\n" + "Y(@id: Int) := SELECT * FROM TABLE(`Orders.entries.product`(@id));"); } From 6e628840dbec195a289b7cc351779dd6c56a2398 Mon Sep 17 00:00:00 2001 From: Daniel Henneberger Date: Fri, 27 Oct 2023 20:09:49 -0700 Subject: [PATCH 5/8] Remove print Signed-off-by: Daniel Henneberger --- .../main/java/com/datasqrl/plan/validate/ScriptValidator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java index d7331e817..9cdee6d5f 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java @@ -355,7 +355,6 @@ public void validateTable(SqrlAssignment statement, SqlNode query, } } - System.out.println(sqlNode); validated = validator.validate(sqlNode); } catch (CalciteContextException e) { throw addError(ErrorLabel.GENERIC, e); From 3c621674334beb52426252ab3c7f557432629652 Mon Sep 17 00:00:00 2001 From: Daniel Henneberger Date: Fri, 27 Oct 2023 20:27:31 -0700 Subject: [PATCH 6/8] Fix test Signed-off-by: Daniel Henneberger --- .../datasqrl/plan/local/analyze/QuerySnapshotTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java index 9183e29d6..074d7d239 100644 --- a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java +++ b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java @@ -1132,6 +1132,15 @@ public void chainedTableFncCallTest() { @Disabled //todo: Illegal use of dynamic param error public void joinTableFncCallTest() { + validateScript("IMPORT ecommerce-data.Orders;\n" + + "IMPORT ecommerce-data.Product;\n" + + "Orders.entries.product(@id: Int) := JOIN Product p ON p.productid = @id;\n" + + "Y(@id: Int) := SELECT * FROM TABLE(`Orders.entries.product`(@id));"); + } + @Test + @Disabled + //todo: Illegal use of dynamic param error + public void joinTableFncCall2Test() { validateScript("IMPORT ecommerce-data.Orders;\n" + "IMPORT ecommerce-data.Product;\n" + "Orders.entries.product(@id: Int) := JOIN Product p ON p.productid = @id;\n" From e0382364bb8e5fef3f6ebf29d1dd3ab6736dce05 Mon Sep 17 00:00:00 2001 From: Daniel Henneberger Date: Fri, 27 Oct 2023 20:27:41 -0700 Subject: [PATCH 7/8] Fix test Signed-off-by: Daniel Henneberger --- .../java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java index 074d7d239..2d5fc71f1 100644 --- a/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java +++ b/sqrl-planner/sqrl-planner-local/src/test/java/com/datasqrl/plan/local/analyze/QuerySnapshotTest.java @@ -1129,8 +1129,6 @@ public void chainedTableFncCallTest() { } @Test - @Disabled - //todo: Illegal use of dynamic param error public void joinTableFncCallTest() { validateScript("IMPORT ecommerce-data.Orders;\n" + "IMPORT ecommerce-data.Product;\n" From ee4c82672a687706162a8f52664d0a0003bde805 Mon Sep 17 00:00:00 2001 From: Daniel Henneberger Date: Fri, 27 Oct 2023 22:54:31 -0700 Subject: [PATCH 8/8] Make a bit more generic Signed-off-by: Daniel Henneberger --- .../java/com/datasqrl/calcite/SqrlToSql.java | 31 +++++++++++----- .../calcite/visitor/SqlNodeVisitor.java | 16 ++++++++- .../calcite/visitor/SqlRelationVisitor.java | 5 ++- .../plan/validate/ScriptValidator.java | 33 ++++++++++++----- .../plan/validate/SqrlToValidatorSql.java | 35 +++++++++++++++---- 5 files changed, 95 insertions(+), 25 deletions(-) diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java index 64934ac62..62f2ec4ea 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/SqrlToSql.java @@ -379,19 +379,34 @@ public Result visitSetOperation(SqlCall node, Context context) { } @Override - public Result visitTableFunction(SqlCall node, Context context) { - //Let sql validator resolve table function + public Result visitCollectTableFunction(SqlCall node, Context context) { + return visitAugmentedTable(node, context); + } + + @Override + public Result visitLateralFunction(SqlCall node, Context context) { + return visitAugmentedTable(node, context); + } + + @Override + public Result visitUnnestFunction(SqlCall node, Context context) { + return visitAugmentedTable(node, context); + } + + public Result visitAugmentedTable(SqlCall node, Context context) { + Result result = SqlNodeVisitor.accept(this, node.getOperandList().get(0), context); + SqlCall call = node.getOperator().createCall(node.getParserPosition(), result.sqlNode); + return new Result(call, result.currentPath, result.pullupColumns, result.tableReferences, + result.condition, result.params); + } + + @Override + public Result visitUserDefinedTableFunction(SqlCall node, Context context) { return new Result(node, List.of(), List.of(), List.of(), Optional.empty(), parameters); } @Override public Result visitCall(SqlCall node, Context context) { - if (node.getOperator() instanceof SqlLateralOperator) { - Result result = SqlNodeVisitor.accept(this, node.getOperandList().get(0), context); - SqlCall call = node.getOperator().createCall(node.getParserPosition(), result.sqlNode); - return new Result(call, result.currentPath, result.pullupColumns, result.tableReferences, - result.condition, result.params); - } throw new RuntimeException("Expected call"); } diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlNodeVisitor.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlNodeVisitor.java index 842277eae..067686504 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlNodeVisitor.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlNodeVisitor.java @@ -5,8 +5,11 @@ import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlJoin; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLateralOperator; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlSelect; +import org.apache.calcite.sql.SqlUnnestOperator; +import org.apache.calcite.sql.SqlUnresolvedFunction; import org.apache.calcite.sql.SqrlAssignTimestamp; import org.apache.calcite.sql.SqrlAssignment; import org.apache.calcite.sql.SqrlDistinctQuery; @@ -19,6 +22,7 @@ import org.apache.calcite.sql.SqrlStreamQuery; import org.apache.calcite.sql.StatementVisitor; import org.apache.calcite.sql.fun.SqlCollectionTableOperator; +import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction; public abstract class SqlNodeVisitor implements SqlRelationVisitor, @@ -63,7 +67,17 @@ public static R accept(SqlRelationVisitor visitor, SqlNode node, C && SqlKind.SET_QUERY.contains(node.getKind())) { return visitor.visitSetOperation((SqlCall) node, context); } else if (node instanceof SqlCall && ((SqlCall) node).getOperator() instanceof SqlCollectionTableOperator) { - return visitor.visitTableFunction((SqlCall) node, context); + return visitor.visitCollectTableFunction((SqlCall) node, context); + } else if (node instanceof SqlCall && ((SqlCall) node).getOperator() instanceof SqlLateralOperator) { + return visitor.visitLateralFunction((SqlCall) node, context); + } else if (node instanceof SqlCall && ((SqlCall) node).getOperator() instanceof SqlUnnestOperator) { + return visitor.visitUnnestFunction((SqlCall) node, context); + } else if (node instanceof SqlCall && + ((SqlCall) node).getOperator() instanceof SqlUserDefinedTableFunction) { + return visitor.visitUserDefinedTableFunction((SqlCall) node, context); + } else if (node instanceof SqlCall && + ((SqlCall) node).getOperator() instanceof SqlUnresolvedFunction) { + return visitor.visitUserDefinedTableFunction((SqlCall) node, context); } else if (node instanceof SqlCall) { return visitor.visitCall((SqlCall) node, context); } diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlRelationVisitor.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlRelationVisitor.java index 500612757..674c9eb15 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlRelationVisitor.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/calcite/visitor/SqlRelationVisitor.java @@ -8,6 +8,9 @@ public interface SqlRelationVisitor { R visitTable(SqlIdentifier node, C context); R visitJoin(SqlJoin node, C context); R visitSetOperation(SqlCall node, C context); - R visitTableFunction(SqlCall node, C context); + R visitCollectTableFunction(SqlCall node, C context); + R visitLateralFunction(SqlCall node, C context); + R visitUnnestFunction(SqlCall node, C context); + R visitUserDefinedTableFunction(SqlCall node, C context); R visitCall(SqlCall node, C context); } \ No newline at end of file diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java index 9cdee6d5f..b5abbf4cc 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/ScriptValidator.java @@ -472,18 +472,35 @@ public SqlNode visitSetOperation(SqlCall node, Object context) { } @Override - public SqlNode visitTableFunction(SqlCall node, Object context) { - SqlCall sqlNode = (SqlCall)node.getOperandList().get(0); - return node.getOperator().createCall(node.getParserPosition(), - sqlNode.accept(rewriteVariables(parameterList, materializeSelf))); + public SqlNode visitCollectTableFunction(SqlCall node, Object context) { + return visitAugmentedTable(node, context); + } + + @Override + public SqlNode visitLateralFunction(SqlCall node, Object context) { + return visitAugmentedTable(node, context); + } + + @Override + public SqlNode visitUnnestFunction(SqlCall node, Object context) { + return visitAugmentedTable(node, context); + } + + private SqlNode visitAugmentedTable(SqlCall node, Object context) { + SqlNode op = SqlNodeVisitor.accept(this, node.getOperandList().get(0), context); + return node.getOperator().createCall(node.getParserPosition(), op); + } + + @Override + public SqlNode visitUserDefinedTableFunction(SqlCall node, Object context) { + List operands = node.getOperandList().stream() + .map(f->f.accept(rewriteVariables(parameterList, materializeSelf))) + .collect(Collectors.toList()); + return node.getOperator().createCall(node.getParserPosition(), operands); } @Override public SqlNode visitCall(SqlCall node, Object context) { - if (node.getOperator() instanceof SqlLateralOperator) { - SqlNode op = SqlNodeVisitor.accept(this, node.getOperandList().get(0), context); - return node.getOperator().createCall(node.getParserPosition(), op); - } throw addError(ErrorLabel.GENERIC, node, "Unsupported call: %s", node.getOperator().getName()); } }, query, null); diff --git a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java index abcde7e44..242f99356 100644 --- a/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java +++ b/sqrl-planner/sqrl-common/src/main/java/com/datasqrl/plan/validate/SqrlToValidatorSql.java @@ -5,6 +5,7 @@ import static org.apache.calcite.sql.SqlUtil.stripAs; import com.datasqrl.calcite.QueryPlanner; +import com.datasqrl.calcite.SqrlToSql; import com.datasqrl.calcite.function.SqrlTableMacro; import com.datasqrl.calcite.schema.PathWalker; import com.datasqrl.calcite.schema.sql.SqlBuilders.SqlAliasCallBuilder; @@ -18,6 +19,7 @@ import com.datasqrl.plan.validate.SqrlToValidatorSql.Context; import com.datasqrl.plan.validate.SqrlToValidatorSql.Result; import com.datasqrl.util.CalciteUtil.RelDataTypeFieldBuilder; +import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import java.util.ArrayList; @@ -303,20 +305,39 @@ public Result visitSetOperation(SqlCall node, Context context) { } @Override - public Result visitTableFunction(SqlCall node, Context context) { - // Wrapped in a SqlCollectionTableOperator - SqlCall tableFunctionCall = (SqlCall)node.getOperandList().get(0); + public Result visitCollectTableFunction(SqlCall node, Context context) { + return visitAugmentedTable(node, context); + } + + @Override + public Result visitLateralFunction(SqlCall node, Context context) { + return visitAugmentedTable(node, context); + } + + @Override + public Result visitUnnestFunction(SqlCall node, Context context) { + return visitAugmentedTable(node, context); + } + private Result visitAugmentedTable(SqlCall node, Context context) { + Preconditions.checkState(node.getOperandList().size() == 1, "Expected a single table condition (LATERAL, UNNEST, ...)"); + Result result = SqlNodeVisitor.accept(this, node.getOperandList().get(0), context); + SqlCall call = node.getOperator().createCall(node.getParserPosition(), result.sqlNode); + //We don't actually fully resolve the function, just check that it exists and let the sql validator do the rest + return new Result(call, result.currentPath, result.fncs); + } + + @Override + public Result visitUserDefinedTableFunction(SqlCall node, Context context) { List operators = new ArrayList<>(); - planner.getOperatorTable().lookupOperatorOverloads(tableFunctionCall.getOperator().getNameAsId(), + planner.getOperatorTable().lookupOperatorOverloads(node.getOperator().getNameAsId(), SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION, SqlSyntax.FUNCTION, operators, planner.getCatalogReader().nameMatcher()); if (operators.isEmpty()) { - throw addError(errorCollector, ErrorLabel.GENERIC, tableFunctionCall, "Could not find table function %s", - tableFunctionCall.getOperator().getName()); + throw addError(errorCollector, ErrorLabel.GENERIC, node, "Could not find table function %s", + node.getOperator().getName()); } - //We don't actually fully resolve the function, just check that it exists and let the sql validator do the rest return new Result(node, List.of(), List.of()); }