From e5c29fd9cd6f9557cfcc3806a823f8546a076ee3 Mon Sep 17 00:00:00 2001 From: Finn Carroll Date: Tue, 19 May 2026 16:06:47 -0700 Subject: [PATCH] Fix SQL query routing to analytics engine for composite-dataformat indices The SQL path in RestUnifiedQueryAction.isAnalyticsIndex() was failing to route queries to the analytics engine because: 1. extractIndexName() used context.getParser() which returns SqlV2QueryParser (producing UnresolvedPlan), but the SQL branch expected a Calcite SqlNode. The V2 parser also treats 'score' as a reserved keyword, causing parse failures on common column names. 2. SqlTableNameExtractor did not handle SqlOrderBy, which Calcite wraps around SELECT...LIMIT queries. 3. Calcite's default parser uppercases unquoted identifiers, but index names in OpenSearch are lowercase. Fix: - Use SqlParser.create(query).parseQuery() (Calcite parser) for the SQL branch, which correctly returns SqlNode and handles standard SQL without reserved-word conflicts. - Add SqlOrderBy handling in SqlTableNameExtractor to unwrap LIMIT queries. - Lowercase the extracted table name before cluster state lookup. Signed-off-by: Finn Carroll --- .../plugin/rest/RestUnifiedQueryAction.java | 13 ++++++++--- .../rest/RestUnifiedQueryActionTest.java | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/plugin/src/main/java/org/opensearch/sql/plugin/rest/RestUnifiedQueryAction.java b/plugin/src/main/java/org/opensearch/sql/plugin/rest/RestUnifiedQueryAction.java index 95ff51b0b9..8d07252c40 100644 --- a/plugin/src/main/java/org/opensearch/sql/plugin/rest/RestUnifiedQueryAction.java +++ b/plugin/src/main/java/org/opensearch/sql/plugin/rest/RestUnifiedQueryAction.java @@ -10,6 +10,7 @@ import static org.opensearch.sql.opensearch.executor.OpenSearchQueryManager.SQL_WORKER_THREAD_POOL_NAME; import static org.opensearch.sql.protocol.response.format.JsonResponseFormatter.Style.PRETTY; +import java.util.Locale; import java.util.Map; import java.util.Optional; import org.apache.calcite.rel.RelNode; @@ -17,7 +18,9 @@ import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlJoin; import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOrderBy; import org.apache.calcite.sql.SqlSelect; +import org.apache.calcite.sql.parser.SqlParser; import org.apache.calcite.sql.util.SqlBasicVisitor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -214,13 +217,14 @@ private void forwardClusterSetting( * node. Uses the context's parser which supports both PPL and SQL. */ private static Optional extractIndexName( - String query, QueryType queryType, UnifiedQueryContext context) { + String query, QueryType queryType, UnifiedQueryContext context) throws Exception { if (queryType == QueryType.PPL) { UnresolvedPlan unresolvedPlan = (UnresolvedPlan) context.getParser().parse(query); return Optional.ofNullable(unresolvedPlan.accept(new IndexNameExtractor(), null)); } - SqlNode sqlNode = (SqlNode) context.getParser().parse(query); - return Optional.ofNullable(extractTableNameFromSqlNode(sqlNode)); + SqlNode sqlNode = SqlParser.create(query).parseQuery(); + String tableName = extractTableNameFromSqlNode(sqlNode); + return Optional.ofNullable(tableName != null ? tableName.toLowerCase(Locale.ROOT) : null); } /** AST visitor that extracts the source index name from a Relation node (PPL path). */ @@ -235,6 +239,9 @@ public String visitRelation(Relation node, Void context) { private static class SqlTableNameExtractor extends SqlBasicVisitor { @Override public String visit(SqlCall call) { + if (call instanceof SqlOrderBy orderBy) { + return orderBy.query.accept(this); + } if (call instanceof SqlSelect select) { return select.getFrom().accept(this); } diff --git a/plugin/src/test/java/org/opensearch/sql/plugin/rest/RestUnifiedQueryActionTest.java b/plugin/src/test/java/org/opensearch/sql/plugin/rest/RestUnifiedQueryActionTest.java index c801266036..cb5df14505 100644 --- a/plugin/src/test/java/org/opensearch/sql/plugin/rest/RestUnifiedQueryActionTest.java +++ b/plugin/src/test/java/org/opensearch/sql/plugin/rest/RestUnifiedQueryActionTest.java @@ -65,6 +65,29 @@ public void pluggableDataformatIndexRoutesToAnalytics() { action.isAnalyticsIndex("source = opensearch.parquet_logs | fields ts", QueryType.PPL)); } + @Test + public void sqlQueryRoutesToAnalyticsForCompositeIndex() { + registerIndex( + "parquet_logs", + Settings.builder() + .put(IndexSettings.PLUGGABLE_DATAFORMAT_ENABLED_SETTING.getKey(), true) + .put(IndexSettings.PLUGGABLE_DATAFORMAT_VALUE_SETTING.getKey(), "composite") + .build()); + + assertTrue(action.isAnalyticsIndex("SELECT ts, msg FROM parquet_logs", QueryType.SQL)); + assertTrue(action.isAnalyticsIndex("SELECT ts FROM parquet_logs LIMIT 10", QueryType.SQL)); + assertTrue( + action.isAnalyticsIndex( + "SELECT score, name FROM parquet_logs WHERE score > 90", QueryType.SQL)); + } + + @Test + public void sqlQueryRoutesToLuceneForNonCompositeIndex() { + registerIndex("plain_logs", Settings.EMPTY); + + assertFalse(action.isAnalyticsIndex("SELECT ts FROM plain_logs", QueryType.SQL)); + } + @Test public void pluggableEnabledButLuceneFormatRoutesToLucene() { registerIndex(