diff --git a/api/README.md b/api/README.md index 91651aa3153..76c64167658 100644 --- a/api/README.md +++ b/api/README.md @@ -8,7 +8,7 @@ This module provides components organized into two main areas aligned with the [ ### Unified Language Specification -- **`UnifiedQueryPlanner`**: Accepts PPL (Piped Processing Language) queries and returns Calcite `RelNode` logical plans as intermediate representation. +- **`UnifiedQueryPlanner`**: Accepts PPL (Piped Processing Language) or SQL queries and returns Calcite `RelNode` logical plans as intermediate representation. - **`UnifiedQueryTranspiler`**: Converts Calcite logical plans (`RelNode`) into SQL strings for various target databases using different SQL dialects. ### Unified Execution Runtime @@ -17,7 +17,7 @@ This module provides components organized into two main areas aligned with the [ - **`UnifiedFunction`**: Engine-agnostic function interface that enables functions to be evaluated across different execution engines without engine-specific code duplication. - **`UnifiedFunctionRepository`**: Repository for discovering and loading functions as `UnifiedFunction` instances, providing a bridge between function definitions and external execution engines. -Together, these components enable complete workflows: parse PPL queries into logical plans, transpile those plans into target database SQL, compile and execute queries directly, or export PPL functions for use in external execution engines. +Together, these components enable complete workflows: parse PPL or SQL queries into logical plans, transpile those plans into target database SQL, compile and execute queries directly, or export PPL functions for use in external execution engines. ### Experimental API Design @@ -33,7 +33,7 @@ Create a context with catalog configuration, query type, and optional settings: ```java UnifiedQueryContext context = UnifiedQueryContext.builder() - .language(QueryType.PPL) + .language(QueryType.PPL) // or QueryType.SQL for SQL .catalog("opensearch", opensearchSchema) .catalog("spark_catalog", sparkSchema) .defaultNamespace("opensearch") @@ -44,7 +44,7 @@ UnifiedQueryContext context = UnifiedQueryContext.builder() ### UnifiedQueryPlanner -Use `UnifiedQueryPlanner` to parse and analyze PPL queries into Calcite logical plans. The planner accepts a `UnifiedQueryContext` and can be reused for multiple queries. +Use `UnifiedQueryPlanner` to parse and analyze PPL or SQL queries into Calcite logical plans. The planner accepts a `UnifiedQueryContext` and can be reused for multiple queries. ```java // Create planner with context @@ -53,6 +53,9 @@ UnifiedQueryPlanner planner = new UnifiedQueryPlanner(context); // Plan multiple queries (context is reused) RelNode plan1 = planner.plan("source = logs | where status = 200"); RelNode plan2 = planner.plan("source = metrics | stats avg(cpu)"); + +// SQL queries are also supported (with QueryType.SQL context) +RelNode plan3 = planner.plan("SELECT * FROM logs WHERE status = 200"); ``` ### UnifiedQueryTranspiler @@ -226,5 +229,4 @@ public class MySchema extends AbstractSchema { ## Future Work -- Expand support to SQL language. - Extend planner to generate optimized physical plans using Calcite's optimization frameworks. diff --git a/api/src/main/java/org/opensearch/sql/api/UnifiedQueryContext.java b/api/src/main/java/org/opensearch/sql/api/UnifiedQueryContext.java index 3e0a1f972bd..026dac86e2a 100644 --- a/api/src/main/java/org/opensearch/sql/api/UnifiedQueryContext.java +++ b/api/src/main/java/org/opensearch/sql/api/UnifiedQueryContext.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.Objects; import lombok.Value; +import org.apache.calcite.avatica.util.Casing; import org.apache.calcite.jdbc.CalciteSchema; import org.apache.calcite.plan.RelTraitDef; import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider; @@ -176,13 +177,18 @@ private FrameworkConfig buildFrameworkConfig() { SchemaPlus defaultSchema = findSchemaByPath(rootSchema, defaultNamespace); return Frameworks.newConfigBuilder() - .parserConfig(SqlParser.Config.DEFAULT) + .parserConfig(buildParserConfig()) .defaultSchema(defaultSchema) .traitDefs((List) null) .programs(Programs.calc(DefaultRelMetadataProvider.INSTANCE)) .build(); } + private SqlParser.Config buildParserConfig() { + // Preserve identifier case for lowercase OpenSearch index names + return SqlParser.Config.DEFAULT.withUnquotedCasing(Casing.UNCHANGED); + } + private SchemaPlus findSchemaByPath(SchemaPlus rootSchema, String defaultPath) { if (defaultPath == null) { return rootSchema; diff --git a/api/src/main/java/org/opensearch/sql/api/UnifiedQueryPlanner.java b/api/src/main/java/org/opensearch/sql/api/UnifiedQueryPlanner.java index 91e35335e20..e6fcb0f5b16 100644 --- a/api/src/main/java/org/opensearch/sql/api/UnifiedQueryPlanner.java +++ b/api/src/main/java/org/opensearch/sql/api/UnifiedQueryPlanner.java @@ -5,17 +5,21 @@ package org.opensearch.sql.api; +import lombok.RequiredArgsConstructor; import org.antlr.v4.runtime.tree.ParseTree; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelCollations; import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.RelRoot; import org.apache.calcite.rel.core.Sort; import org.apache.calcite.rel.logical.LogicalSort; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.tools.Frameworks; +import org.apache.calcite.tools.Planner; import org.opensearch.sql.ast.statement.Query; import org.opensearch.sql.ast.statement.Statement; import org.opensearch.sql.ast.tree.UnresolvedPlan; import org.opensearch.sql.calcite.CalciteRelNodeVisitor; -import org.opensearch.sql.common.antlr.Parser; import org.opensearch.sql.common.antlr.SyntaxCheckException; import org.opensearch.sql.executor.QueryType; import org.opensearch.sql.ppl.antlr.PPLSyntaxParser; @@ -28,15 +32,9 @@ * such as Spark or command-line tools, abstracting away Calcite internals. */ public class UnifiedQueryPlanner { - /** The parser instance responsible for converting query text into a parse tree. */ - private final Parser parser; - /** Unified query context containing CalcitePlanContext with all configuration. */ - private final UnifiedQueryContext context; - - /** AST-to-RelNode visitor that builds logical plans from the parsed AST. */ - private final CalciteRelNodeVisitor relNodeVisitor = - new CalciteRelNodeVisitor(new EmptyDataSourceService()); + /** Planning strategy selected at construction time based on query type. */ + private final PlanningStrategy strategy; /** * Constructs a UnifiedQueryPlanner with a unified query context. @@ -44,20 +42,22 @@ public class UnifiedQueryPlanner { * @param context the unified query context containing CalcitePlanContext */ public UnifiedQueryPlanner(UnifiedQueryContext context) { - this.parser = buildQueryParser(context.getPlanContext().queryType); - this.context = context; + this.strategy = + context.getPlanContext().queryType == QueryType.SQL + ? new CalciteNativeStrategy(context) + : new CustomVisitorStrategy(context); } /** * Parses and analyzes a query string into a Calcite logical plan (RelNode). TODO: Generate * optimal physical plan to fully unify query execution and leverage Calcite's optimizer. * - * @param query the raw query string in PPL or other supported syntax + * @param query the raw query string in PPL or SQL syntax * @return a logical plan representing the query */ public RelNode plan(String query) { try { - return preserveCollation(analyze(parse(query))); + return strategy.plan(query); } catch (SyntaxCheckException e) { // Re-throw syntax error without wrapping throw e; @@ -66,38 +66,63 @@ public RelNode plan(String query) { } } - private Parser buildQueryParser(QueryType queryType) { - if (queryType == QueryType.PPL) { - return new PPLSyntaxParser(); - } - throw new IllegalArgumentException("Unsupported query type: " + queryType); + /** Strategy interface for language-specific planning logic. */ + private interface PlanningStrategy { + RelNode plan(String query) throws Exception; } - private UnresolvedPlan parse(String query) { - ParseTree cst = parser.parse(query); - AstStatementBuilder astStmtBuilder = - new AstStatementBuilder( - new AstBuilder(query, context.getSettings()), - AstStatementBuilder.StatementBuilderContext.builder().build()); - Statement statement = cst.accept(astStmtBuilder); + /** ANSI SQL planning using Calcite's native SqlParser → SqlValidator → SqlToRelConverter. */ + @RequiredArgsConstructor + private static class CalciteNativeStrategy implements PlanningStrategy { + private final UnifiedQueryContext context; - if (statement instanceof Query) { - return ((Query) statement).getPlan(); + @Override + public RelNode plan(String query) throws Exception { + try (Planner planner = Frameworks.getPlanner(context.getPlanContext().config)) { + SqlNode parsed = planner.parse(query); + SqlNode validated = planner.validate(parsed); + RelRoot relRoot = planner.rel(validated); + return relRoot.project(); + } } - throw new UnsupportedOperationException( - "Only query statements are supported but got " + statement.getClass().getSimpleName()); } - private RelNode analyze(UnresolvedPlan ast) { - return relNodeVisitor.analyze(ast, context.getPlanContext()); - } + /** AST-based planning via ANTLR parser → UnresolvedPlan → CalciteRelNodeVisitor. */ + @RequiredArgsConstructor + private static class CustomVisitorStrategy implements PlanningStrategy { + private final UnifiedQueryContext context; + private final PPLSyntaxParser parser = new PPLSyntaxParser(); + private final CalciteRelNodeVisitor relNodeVisitor = + new CalciteRelNodeVisitor(new EmptyDataSourceService()); + + @Override + public RelNode plan(String query) { + UnresolvedPlan ast = parse(query); + RelNode logical = relNodeVisitor.analyze(ast, context.getPlanContext()); + return preserveCollation(logical); + } + + private UnresolvedPlan parse(String query) { + ParseTree cst = parser.parse(query); + AstStatementBuilder astStmtBuilder = + new AstStatementBuilder( + new AstBuilder(query, context.getSettings()), + AstStatementBuilder.StatementBuilderContext.builder().build()); + Statement statement = cst.accept(astStmtBuilder); + + if (statement instanceof Query) { + return ((Query) statement).getPlan(); + } + throw new UnsupportedOperationException( + "Only query statements are supported but got " + statement.getClass().getSimpleName()); + } - private RelNode preserveCollation(RelNode logical) { - RelNode calcitePlan = logical; - RelCollation collation = logical.getTraitSet().getCollation(); - if (!(logical instanceof Sort) && collation != RelCollations.EMPTY) { - calcitePlan = LogicalSort.create(logical, collation, null, null); + private RelNode preserveCollation(RelNode logical) { + RelCollation collation = logical.getTraitSet().getCollation(); + if (!(logical instanceof Sort) && collation != RelCollations.EMPTY) { + return LogicalSort.create(logical, collation, null, null); + } + return logical; } - return calcitePlan; } } diff --git a/api/src/test/java/org/opensearch/sql/api/UnifiedQueryContextTest.java b/api/src/test/java/org/opensearch/sql/api/UnifiedQueryContextTest.java index a3ad73f700a..ad2eba0fea5 100644 --- a/api/src/test/java/org/opensearch/sql/api/UnifiedQueryContextTest.java +++ b/api/src/test/java/org/opensearch/sql/api/UnifiedQueryContextTest.java @@ -63,14 +63,15 @@ public void testMissingQueryType() { UnifiedQueryContext.builder().catalog("opensearch", testSchema).build(); } - @Test(expected = IllegalArgumentException.class) - public void testUnsupportedQueryType() { + @Test + public void testSqlQueryType() { UnifiedQueryContext context = UnifiedQueryContext.builder() - .language(QueryType.SQL) // only PPL is supported for now + .language(QueryType.SQL) .catalog("opensearch", testSchema) .build(); - new UnifiedQueryPlanner(context); + UnifiedQueryPlanner planner = new UnifiedQueryPlanner(context); + assertNotNull("SQL planner should be created", planner); } @Test(expected = IllegalArgumentException.class) diff --git a/api/src/test/java/org/opensearch/sql/api/UnifiedQueryPlannerSqlTest.java b/api/src/test/java/org/opensearch/sql/api/UnifiedQueryPlannerSqlTest.java new file mode 100644 index 00000000000..53accd49715 --- /dev/null +++ b/api/src/test/java/org/opensearch/sql/api/UnifiedQueryPlannerSqlTest.java @@ -0,0 +1,214 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.api; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; + +import java.util.Map; +import org.apache.calcite.schema.Schema; +import org.apache.calcite.schema.impl.AbstractSchema; +import org.junit.Test; +import org.opensearch.sql.executor.QueryType; + +public class UnifiedQueryPlannerSqlTest extends UnifiedQueryTestBase { + + private final AbstractSchema testDeepSchema = + new AbstractSchema() { + @Override + protected Map getSubSchemaMap() { + return Map.of("opensearch", testSchema); + } + }; + + @Override + protected QueryType queryType() { + return QueryType.SQL; + } + + @Test + public void testSqlQueryPlanning() { + givenQuery( + """ + SELECT * + FROM catalog.employees\ + """) + .assertPlan( + """ + LogicalProject(id=[$0], name=[$1], age=[$2], department=[$3]) + LogicalTableScan(table=[[catalog, employees]]) + """); + } + + @Test + public void testSqlSelectSpecificColumns() { + givenQuery( + """ + SELECT id, name + FROM catalog.employees\ + """) + .assertPlan( + """ + LogicalProject(id=[$0], name=[$1]) + LogicalTableScan(table=[[catalog, employees]]) + """); + } + + @Test + public void testSqlFilterQueryPlanning() { + givenQuery( + """ + SELECT name + FROM catalog.employees + WHERE age > 30\ + """) + .assertPlan( + """ + LogicalProject(name=[$1]) + LogicalFilter(condition=[>($2, 30)]) + LogicalTableScan(table=[[catalog, employees]]) + """); + } + + @Test + public void testSqlAggregateQueryPlanning() { + givenQuery( + """ + SELECT department, count(*) AS cnt + FROM catalog.employees + GROUP BY department\ + """) + .assertPlan( + """ + LogicalAggregate(group=[{0}], cnt=[COUNT()]) + LogicalProject(department=[$3]) + LogicalTableScan(table=[[catalog, employees]]) + """); + } + + @Test + public void testSqlJoinQueryPlanning() { + givenQuery( + """ + SELECT a.id, b.name + FROM catalog.employees a + JOIN catalog.employees b ON a.id = b.age\ + """) + .assertPlan( + """ + LogicalProject(id=[$0], name=[$5]) + LogicalJoin(condition=[=($0, $6)], joinType=[inner]) + LogicalTableScan(table=[[catalog, employees]]) + LogicalTableScan(table=[[catalog, employees]]) + """); + } + + @Test + public void testSqlOrderByQueryPlanning() { + givenQuery( + """ + SELECT name + FROM catalog.employees + ORDER BY age DESC\ + """) + .assertPlan( + """ + LogicalProject(name=[$0]) + LogicalSort(sort0=[$1], dir0=[DESC]) + LogicalProject(name=[$1], age=[$2]) + LogicalTableScan(table=[[catalog, employees]]) + """); + } + + @Test + public void testSqlSubqueryPlanning() { + // Calcite represents scalar subqueries as $SCALAR_QUERY{...} with embedded plan text whose + // formatting (whitespace, line breaks) may vary across versions. Assert output fields only. + givenQuery( + """ + SELECT name + FROM catalog.employees + WHERE age > (SELECT avg(age) FROM catalog.employees)\ + """) + .assertFields("name"); + } + + @Test + public void testSqlCteQueryPlanning() { + // CTE is inlined by Calcite — same plan as a direct filter query + givenQuery( + """ + WITH seniors AS ( + SELECT name, age FROM catalog.employees WHERE age > 30 + ) + SELECT name + FROM seniors\ + """) + .assertPlan( + """ + LogicalProject(name=[$1]) + LogicalFilter(condition=[>($2, 30)]) + LogicalTableScan(table=[[catalog, employees]]) + """); + } + + @Test + public void testSqlQueryPlanningWithDefaultNamespace() { + UnifiedQueryContext sqlContext = + UnifiedQueryContext.builder() + .language(QueryType.SQL) + .catalog("opensearch", testSchema) + .defaultNamespace("opensearch") + .build(); + UnifiedQueryPlanner sqlPlanner = new UnifiedQueryPlanner(sqlContext); + + assertNotNull("Plan should be created", sqlPlanner.plan("SELECT * FROM opensearch.employees")); + assertNotNull("Plan should be created", sqlPlanner.plan("SELECT * FROM employees")); + } + + @Test + public void testSqlQueryPlanningWithDefaultNamespaceMultiLevel() { + UnifiedQueryContext sqlContext = + UnifiedQueryContext.builder() + .language(QueryType.SQL) + .catalog("catalog", testDeepSchema) + .defaultNamespace("catalog.opensearch") + .build(); + UnifiedQueryPlanner sqlPlanner = new UnifiedQueryPlanner(sqlContext); + + assertNotNull( + "Plan should be created", sqlPlanner.plan("SELECT * FROM catalog.opensearch.employees")); + assertNotNull("Plan should be created", sqlPlanner.plan("SELECT * FROM employees")); + + assertThrows( + IllegalStateException.class, () -> sqlPlanner.plan("SELECT * FROM opensearch.employees")); + } + + @Test + public void testSqlQueryPlanningWithMultipleCatalogs() { + UnifiedQueryContext sqlContext = + UnifiedQueryContext.builder() + .language(QueryType.SQL) + .catalog("catalog1", testSchema) + .catalog("catalog2", testSchema) + .build(); + UnifiedQueryPlanner sqlPlanner = new UnifiedQueryPlanner(sqlContext); + + assertNotNull( + "Plan should be created", + sqlPlanner.plan( + """ + SELECT a.id + FROM catalog1.employees a + JOIN catalog2.employees b ON a.id = b.id\ + """)); + } + + @Test + public void testInvalidSqlThrowsException() { + assertThrows(IllegalStateException.class, () -> planner.plan("SELECT FROM")); + } +} diff --git a/api/src/testFixtures/java/org/opensearch/sql/api/UnifiedQueryTestBase.java b/api/src/testFixtures/java/org/opensearch/sql/api/UnifiedQueryTestBase.java index 000b145695a..eafbfbbeeda 100644 --- a/api/src/testFixtures/java/org/opensearch/sql/api/UnifiedQueryTestBase.java +++ b/api/src/testFixtures/java/org/opensearch/sql/api/UnifiedQueryTestBase.java @@ -7,6 +7,7 @@ import static org.apache.calcite.sql.type.SqlTypeName.INTEGER; import static org.apache.calcite.sql.type.SqlTypeName.VARCHAR; +import static org.junit.Assert.assertEquals; import java.util.List; import java.util.Map; @@ -15,6 +16,8 @@ import org.apache.calcite.DataContext; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Linq4j; +import org.apache.calcite.plan.RelOptUtil; +import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.schema.ScannableTable; @@ -55,14 +58,25 @@ protected Map getTableMap() { } }; - context = - UnifiedQueryContext.builder() - .language(QueryType.PPL) - .catalog(DEFAULT_CATALOG, testSchema) - .build(); + context = buildContext(queryType()); planner = new UnifiedQueryPlanner(context); } + /** + * Returns the query type for this test class. Subclasses override to test different languages. + */ + protected QueryType queryType() { + return QueryType.PPL; + } + + /** Builds a UnifiedQueryContext with the test schema for the given query type. */ + protected UnifiedQueryContext buildContext(QueryType queryType) { + return UnifiedQueryContext.builder() + .language(queryType) + .catalog(DEFAULT_CATALOG, testSchema) + .build(); + } + @After public void tearDown() throws Exception { if (context != null) { @@ -128,4 +142,37 @@ public boolean rolledUpColumnValidInsideAgg( return false; } } + + /** Fluent helper for asserting query plan results. */ + protected QueryAssert givenQuery(String query) { + return new QueryAssert(planner.plan(query)); + } + + /** Fluent assertion on a query's logical plan. */ + protected static class QueryAssert { + private final RelNode plan; + + QueryAssert(RelNode plan) { + this.plan = plan; + } + + /** Assert the logical plan matches the expected tree string. */ + public QueryAssert assertPlan(String expected) { + assertEquals( + expected.stripTrailing(), + RelOptUtil.toString(plan).replaceAll("\\r\\n", "\n").stripTrailing()); + return this; + } + + /** Assert the output field names match. */ + public QueryAssert assertFields(String... names) { + assertEquals(List.of(names), plan.getRowType().getFieldNames()); + return this; + } + + /** Access the underlying plan for custom assertions. */ + public RelNode plan() { + return plan; + } + } } diff --git a/benchmarks/src/jmh/java/org/opensearch/sql/api/UnifiedQueryBenchmark.java b/benchmarks/src/jmh/java/org/opensearch/sql/api/UnifiedQueryBenchmark.java index d75a87ea8c3..aeb47e78821 100644 --- a/benchmarks/src/jmh/java/org/opensearch/sql/api/UnifiedQueryBenchmark.java +++ b/benchmarks/src/jmh/java/org/opensearch/sql/api/UnifiedQueryBenchmark.java @@ -6,6 +6,7 @@ package org.opensearch.sql.api; import java.sql.PreparedStatement; +import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.calcite.rel.RelNode; import org.apache.calcite.sql.dialect.SparkSqlDialect; @@ -24,10 +25,12 @@ import org.openjdk.jmh.annotations.Warmup; import org.opensearch.sql.api.compiler.UnifiedQueryCompiler; import org.opensearch.sql.api.transpiler.UnifiedQueryTranspiler; +import org.opensearch.sql.executor.QueryType; /** - * JMH benchmark for measuring the overhead of unified query API components when processing queries. - * This provides baseline metrics and guidance for API consumers during integration. + * JMH benchmark for measuring the overhead of unified query API components when processing PPL and + * SQL queries. The {@code language} and {@code queryPattern} parameters produce a cross-product, + * enabling side-by-side comparison of equivalent queries across both languages. */ @Warmup(iterations = 2, time = 1) @Measurement(iterations = 5, time = 1) @@ -37,25 +40,69 @@ @Fork(value = 1) public class UnifiedQueryBenchmark extends UnifiedQueryTestBase { - /** Common query patterns for benchmarking. */ - @Param({ - "source = catalog.employees", - "source = catalog.employees | where age > 30", - "source = catalog.employees | stats count() by department", - "source = catalog.employees | sort - age", - "source = catalog.employees | where age > 25 | stats avg(age) by department | sort - department" - }) - private String query; + private static final Map PPL_QUERIES = + Map.of( + "scan", "source = catalog.employees", + "filter", "source = catalog.employees | where age > 30", + "aggregate", "source = catalog.employees | stats count() by department", + "sort", "source = catalog.employees | sort - age", + "complex", + """ + source = catalog.employees \ + | where age > 25 \ + | stats avg(age) by department \ + | sort - department\ + """); - /** Transpiler for converting logical plans to SQL strings. */ - private UnifiedQueryTranspiler transpiler; + private static final Map SQL_QUERIES = + Map.of( + "scan", "SELECT * FROM catalog.employees", + "filter", + """ + SELECT * + FROM catalog.employees + WHERE age > 30\ + """, + "aggregate", + """ + SELECT department, count(*) + FROM catalog.employees + GROUP BY department\ + """, + "sort", + """ + SELECT * + FROM catalog.employees + ORDER BY age DESC\ + """, + "complex", + """ + SELECT department, avg(age) + FROM catalog.employees + WHERE age > 25 + GROUP BY department + ORDER BY department\ + """); + + @Param({"PPL", "SQL"}) + private String language; + + @Param({"scan", "filter", "aggregate", "sort", "complex"}) + private String queryPattern; - /** Compiler for converting logical plans to executable statements. */ + private String query; + private UnifiedQueryTranspiler transpiler; private UnifiedQueryCompiler compiler; + @Override + protected QueryType queryType() { + return QueryType.valueOf(language); + } + @Setup(Level.Trial) public void setUpBenchmark() { super.setUp(); + query = (language.equals("PPL") ? PPL_QUERIES : SQL_QUERIES).get(queryPattern); transpiler = UnifiedQueryTranspiler.builder().dialect(SparkSqlDialect.DEFAULT).build(); compiler = new UnifiedQueryCompiler(context); }