diff --git a/core/src/main/java/org/opensearch/sql/datasource/model/EmptyDataSourceService.java b/core/src/main/java/org/opensearch/sql/datasource/model/EmptyDataSourceService.java new file mode 100644 index 0000000000..80509c87c1 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/datasource/model/EmptyDataSourceService.java @@ -0,0 +1,89 @@ +package org.opensearch.sql.datasource.model; + +import com.google.common.collect.ImmutableMap; +import org.opensearch.sql.data.type.ExprType; +import org.opensearch.sql.datasource.DataSourceService; +import org.opensearch.sql.planner.logical.LogicalPlan; +import org.opensearch.sql.planner.physical.PhysicalPlan; +import org.opensearch.sql.storage.StorageEngine; +import org.opensearch.sql.storage.Table; + +import java.util.Map; +import java.util.Set; + +import static org.opensearch.sql.analysis.DataSourceSchemaIdentifierNameResolver.DEFAULT_DATASOURCE_NAME; +import static org.opensearch.sql.data.type.ExprCoreType.STRING; + +public class EmptyDataSourceService { + private static DataSourceService emptyDataSourceService = new DataSourceService() { + @Override + public DataSource getDataSource(String dataSourceName) { + return new DataSource(DEFAULT_DATASOURCE_NAME, DataSourceType.OPENSEARCH, storageEngine()); + } + + @Override + public Set getDataSourceMetadata(boolean isDefaultDataSourceRequired) { + return Set.of(); + } + + @Override + public DataSourceMetadata getDataSourceMetadata(String name) { + return null; + } + + @Override + public void createDataSource(DataSourceMetadata metadata) { + + } + + @Override + public void updateDataSource(DataSourceMetadata dataSourceMetadata) { + + } + + @Override + public void deleteDataSource(String dataSourceName) { + + } + + @Override + public Boolean dataSourceExists(String dataSourceName) { + return null; + } + }; + + private static StorageEngine storageEngine() { + Table table = + new Table() { + @Override + public boolean exists() { + return true; + } + + @Override + public void create(Map schema) { + throw new UnsupportedOperationException("Create table is not supported"); + } + + @Override + public Map getFieldTypes() { + return null; + } + + @Override + public PhysicalPlan implement(LogicalPlan plan) { + throw new UnsupportedOperationException(); + } + + public Map getReservedFieldTypes() { + return ImmutableMap.of("_test", STRING); + } + }; + return (dataSourceSchemaName, tableName) -> table; + } + + + public static DataSourceService getEmptyDataSourceService() { + return emptyDataSourceService; + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/DSL.java b/core/src/main/java/org/opensearch/sql/expression/DSL.java index 2131f7248e..ab537392f7 100644 --- a/core/src/main/java/org/opensearch/sql/expression/DSL.java +++ b/core/src/main/java/org/opensearch/sql/expression/DSL.java @@ -25,6 +25,7 @@ import org.opensearch.sql.expression.parse.RegexExpression; import org.opensearch.sql.expression.span.SpanExpression; import org.opensearch.sql.expression.window.ranking.RankingWindowFunction; +import static org.opensearch.sql.datasource.model.EmptyDataSourceService.getEmptyDataSourceService; public class DSL { @@ -615,6 +616,10 @@ public static FunctionExpression xor(Expression... expressions) { return compile(FunctionProperties.None, BuiltinFunctionName.XOR, expressions); } + public static FunctionExpression nested(Expression... expressions) { + return compile(FunctionProperties.None, BuiltinFunctionName.NESTED, expressions); + } + public static FunctionExpression not(Expression... expressions) { return compile(FunctionProperties.None, BuiltinFunctionName.NOT, expressions); } @@ -823,6 +828,10 @@ public static FunctionExpression typeof(Expression value) { return compile(FunctionProperties.None, BuiltinFunctionName.TYPEOF, value); } + public static FunctionExpression match_phrase_prefix(Expression... args) { + return compile(FunctionProperties.None, BuiltinFunctionName.MATCH_PHRASE_PREFIX, args); + } + public static FunctionExpression now(FunctionProperties functionProperties, Expression... args) { return compile(functionProperties, BuiltinFunctionName.NOW, args); } @@ -905,7 +914,7 @@ public static FunctionExpression utc_timestamp( private static T compile( FunctionProperties functionProperties, BuiltinFunctionName bfn, Expression... args) { return (T) - BuiltinFunctionRepository.getInstance(null) + BuiltinFunctionRepository.getInstance(getEmptyDataSourceService()) .compile(functionProperties, bfn.getName(), Arrays.asList(args)); } } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionRepository.java b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionRepository.java index 6d65172734..f9bcd81094 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionRepository.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionRepository.java @@ -67,8 +67,8 @@ public class BuiltinFunctionRepository { * @return singleton instance */ public static synchronized BuiltinFunctionRepository getInstance(DataSourceService dataSourceService) { - // Create hash code for tests with dataSourceService as null - int dataSourceServiceHash = dataSourceService == null ? 0 : dataSourceService.hashCode(); + // TODO: get hashCode of dataSourceMetadata + int dataSourceServiceHash = dataSourceService.hashCode(); // Creates new Repository for every dataSourceService if (!instance.containsKey(dataSourceServiceHash)) { @@ -86,15 +86,15 @@ public static synchronized BuiltinFunctionRepository getInstance(DataSourceServi TextFunction.register(repository); TypeCastOperator.register(repository); SystemFunctions.register(repository); - - if (dataSourceService != null) { - for (DataSourceMetadata metadata : dataSourceService.getDataSourceMetadata(true)) { - dataSourceService - .getDataSource(metadata.getName()) - .getStorageEngine() - .getFunctions() - .forEach(repository::register); - } + // Temporary as part of https://github.com/opensearch-project/sql/issues/811 + // TODO: remove this resolver when Analyzers are moved to opensearch module + repository.register(new NestedFunctionResolver()); + + for (DataSourceMetadata metadata : dataSourceService.getDataSourceMetadata(true)) { + dataSourceService + .getDataSource(metadata.getName()) + .getStorageEngine().getFunctions(). + forEach(function -> repository.register(function)); } instance.put(dataSourceServiceHash, repository); } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/NestedFunctionResolver.java b/core/src/main/java/org/opensearch/sql/expression/function/NestedFunctionResolver.java new file mode 100644 index 0000000000..01903b80a5 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/NestedFunctionResolver.java @@ -0,0 +1,37 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function; + +import org.apache.commons.lang3.tuple.Pair; +import org.opensearch.sql.data.model.ExprValue; +import org.opensearch.sql.data.type.ExprType; +import org.opensearch.sql.expression.Expression; +import org.opensearch.sql.expression.FunctionExpression; +import org.opensearch.sql.expression.env.Environment; + +public class NestedFunctionResolver implements FunctionResolver{ + @Override + public Pair resolve(FunctionSignature unresolvedSignature) { + return Pair.of(unresolvedSignature, + (functionProperties, arguments) -> + new FunctionExpression(BuiltinFunctionName.NESTED.getName(), arguments) { + @Override + public ExprValue valueOf(Environment valueEnv) { + return valueEnv.resolve(getArguments().get(0)); + } + + @Override + public ExprType type() { + return getArguments().get(0).type(); + } + }); + } + + @Override + public FunctionName getFunctionName() { + return BuiltinFunctionName.NESTED.getName(); + } +} diff --git a/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTest.java b/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTest.java index 927b1876b2..617ed6b9ba 100644 --- a/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTest.java +++ b/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTest.java @@ -12,7 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opensearch.sql.analysis.DataSourceSchemaIdentifierNameResolver.DEFAULT_DATASOURCE_NAME; -import static org.opensearch.sql.analysis.NestedAnalyzer.isNestedFunction; import static org.opensearch.sql.ast.dsl.AstDSL.aggregate; import static org.opensearch.sql.ast.dsl.AstDSL.alias; import static org.opensearch.sql.ast.dsl.AstDSL.argument; @@ -41,6 +40,7 @@ import static org.opensearch.sql.data.type.ExprCoreType.LONG; import static org.opensearch.sql.data.type.ExprCoreType.STRING; import static org.opensearch.sql.data.type.ExprCoreType.TIMESTAMP; +import static org.opensearch.sql.datasource.model.EmptyDataSourceService.getEmptyDataSourceService; import static org.opensearch.sql.expression.DSL.literal; import static org.opensearch.sql.utils.MLCommonsConstants.ACTION; import static org.opensearch.sql.utils.MLCommonsConstants.ALGO; @@ -66,7 +66,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -90,9 +89,6 @@ import org.opensearch.sql.ast.tree.UnresolvedPlan; import org.opensearch.sql.common.antlr.SyntaxCheckException; import org.opensearch.sql.datasource.DataSourceService; -import org.opensearch.sql.datasource.model.DataSource; -import org.opensearch.sql.datasource.model.DataSourceMetadata; -import org.opensearch.sql.datasource.model.DataSourceType; import org.opensearch.sql.exception.ExpressionEvaluationException; import org.opensearch.sql.exception.SemanticCheckException; import org.opensearch.sql.expression.DSL; @@ -281,6 +277,33 @@ public void filter_relation_with_multiple_tables() { AstDSL.equalTo(AstDSL.field("integer_value"), AstDSL.intLiteral(1)))); } + @Test + public void analyze_filter_visit_score_function() { + UnresolvedPlan unresolvedPlan = + AstDSL.filter( + AstDSL.relation("schema"), + new ScoreFunction( + AstDSL.function( + "match_phrase_prefix", + AstDSL.unresolvedArg("field", stringLiteral("field_value1")), + AstDSL.unresolvedArg("query", stringLiteral("search query")), + AstDSL.unresolvedArg("boost", stringLiteral("3"))), + AstDSL.doubleLiteral(1.0))); + assertAnalyzeEqual( + LogicalPlanDSL.filter( + LogicalPlanDSL.relation("schema", table), + DSL.match_phrase_prefix( + DSL.namedArgument("field", "field_value1"), + DSL.namedArgument("query", "search query"), + DSL.namedArgument("boost", "3.0"))), + unresolvedPlan); + + LogicalPlan logicalPlan = analyze(unresolvedPlan); + OpenSearchFunction relevanceQuery = + (OpenSearchFunction) ((LogicalFilter) logicalPlan).getCondition(); + assertEquals(true, relevanceQuery.isScoreTracked()); + } + @Test public void analyze_filter_visit_score_function_with_unsupported_boost_SemanticCheckException() { UnresolvedPlan unresolvedPlan = @@ -465,6 +488,284 @@ public void filter_with_nested_all_tuple_fields_throws_exception() { AstDSL.alias("nested(message.*)", nestedAllTupleFields("message"))))); } + @Test + public void project_nested_field_star_arg() { + List> nestedArgs = + List.of( + Map.of( + "field", new ReferenceExpression("message.info", STRING), + "path", new ReferenceExpression("message", STRING))); + + List projectList = + List.of( + new NamedExpression( + "nested(message.info)", DSL.nested(DSL.ref("message.info", STRING)))); + + assertAnalyzeEqual( + LogicalPlanDSL.project( + LogicalPlanDSL.nested( + LogicalPlanDSL.relation("schema", table), nestedArgs, projectList), + DSL.named("nested(message.info)", DSL.nested(DSL.ref("message.info", STRING)))), + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias("nested(message.*)", nestedAllTupleFields("message")))); + } + + @Test + public void project_nested_field_star_arg_with_another_nested_function() { + List> nestedArgs = + List.of( + Map.of( + "field", new ReferenceExpression("message.info", STRING), + "path", new ReferenceExpression("message", STRING)), + Map.of( + "field", new ReferenceExpression("comment.data", STRING), + "path", new ReferenceExpression("comment", STRING))); + + List projectList = + List.of( + new NamedExpression( + "nested(message.info)", DSL.nested(DSL.ref("message.info", STRING))), + new NamedExpression( + "nested(comment.data)", DSL.nested(DSL.ref("comment.data", STRING)))); + + assertAnalyzeEqual( + LogicalPlanDSL.project( + LogicalPlanDSL.nested( + LogicalPlanDSL.relation("schema", table), nestedArgs, projectList), + DSL.named("nested(message.info)", DSL.nested(DSL.ref("message.info", STRING))), + DSL.named("nested(comment.data)", DSL.nested(DSL.ref("comment.data", STRING)))), + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias("nested(message.*)", nestedAllTupleFields("message")), + AstDSL.alias("nested(comment.*)", nestedAllTupleFields("comment")))); + } + + @Test + public void project_nested_field_star_arg_with_another_field() { + List> nestedArgs = + List.of( + Map.of( + "field", new ReferenceExpression("message.info", STRING), + "path", new ReferenceExpression("message", STRING))); + + List projectList = + List.of( + new NamedExpression( + "nested(message.info)", DSL.nested(DSL.ref("message.info", STRING))), + new NamedExpression("comment.data", DSL.ref("comment.data", STRING))); + + assertAnalyzeEqual( + LogicalPlanDSL.project( + LogicalPlanDSL.nested( + LogicalPlanDSL.relation("schema", table), nestedArgs, projectList), + DSL.named("nested(message.info)", DSL.nested(DSL.ref("message.info", STRING))), + DSL.named("comment.data", DSL.ref("comment.data", STRING))), + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias("nested(message.*)", nestedAllTupleFields("message")), + AstDSL.alias("comment.data", field("comment.data")))); + } + + @Test + public void project_nested_field_star_arg_with_highlight() { + List> nestedArgs = + List.of( + Map.of( + "field", new ReferenceExpression("message.info", STRING), + "path", new ReferenceExpression("message", STRING))); + + List projectList = + List.of( + new NamedExpression( + "nested(message.info)", DSL.nested(DSL.ref("message.info", STRING))), + DSL.named("highlight(fieldA)", new HighlightExpression(DSL.literal("fieldA")))); + + Map highlightArgs = new HashMap<>(); + + assertAnalyzeEqual( + LogicalPlanDSL.project( + LogicalPlanDSL.nested( + LogicalPlanDSL.highlight( + LogicalPlanDSL.relation("schema", table), DSL.literal("fieldA"), highlightArgs), + nestedArgs, + projectList), + DSL.named("nested(message.info)", DSL.nested(DSL.ref("message.info", STRING))), + DSL.named("highlight(fieldA)", new HighlightExpression(DSL.literal("fieldA")))), + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias("nested(message.*)", nestedAllTupleFields("message")), + AstDSL.alias( + "highlight(fieldA)", + new HighlightFunction(AstDSL.stringLiteral("fieldA"), highlightArgs)))); + } + + @Test + public void project_nested_field_and_path_args() { + List> nestedArgs = + List.of( + Map.of( + "field", new ReferenceExpression("message.info", STRING), + "path", new ReferenceExpression("message", STRING))); + + List projectList = + List.of( + new NamedExpression( + "nested(message.info)", + DSL.nested(DSL.ref("message.info", STRING), DSL.ref("message", STRING)), + null)); + + assertAnalyzeEqual( + LogicalPlanDSL.project( + LogicalPlanDSL.nested( + LogicalPlanDSL.relation("schema", table), nestedArgs, projectList), + DSL.named( + "nested(message.info)", + DSL.nested(DSL.ref("message.info", STRING), DSL.ref("message", STRING)))), + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias( + "nested(message.info)", + function("nested", qualifiedName("message", "info"), qualifiedName("message")), + null))); + } + + @Test + public void project_nested_deep_field_arg() { + List> nestedArgs = + List.of( + Map.of( + "field", new ReferenceExpression("message.info.id", STRING), + "path", new ReferenceExpression("message.info", STRING))); + + List projectList = + List.of( + new NamedExpression( + "nested(message.info.id)", DSL.nested(DSL.ref("message.info.id", STRING)), null)); + + assertAnalyzeEqual( + LogicalPlanDSL.project( + LogicalPlanDSL.nested( + LogicalPlanDSL.relation("schema", table), nestedArgs, projectList), + DSL.named("nested(message.info.id)", DSL.nested(DSL.ref("message.info.id", STRING)))), + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias( + "nested(message.info.id)", + function("nested", qualifiedName("message", "info", "id")), + null))); + } + + @Test + public void project_multiple_nested() { + List> nestedArgs = + List.of( + Map.of( + "field", new ReferenceExpression("message.info", STRING), + "path", new ReferenceExpression("message", STRING)), + Map.of( + "field", new ReferenceExpression("comment.data", STRING), + "path", new ReferenceExpression("comment", STRING))); + + List projectList = + List.of( + new NamedExpression( + "nested(message.info)", DSL.nested(DSL.ref("message.info", STRING)), null), + new NamedExpression( + "nested(comment.data)", DSL.nested(DSL.ref("comment.data", STRING)), null)); + + assertAnalyzeEqual( + LogicalPlanDSL.project( + LogicalPlanDSL.nested( + LogicalPlanDSL.relation("schema", table), nestedArgs, projectList), + DSL.named("nested(message.info)", DSL.nested(DSL.ref("message.info", STRING))), + DSL.named("nested(comment.data)", DSL.nested(DSL.ref("comment.data", STRING)))), + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias( + "nested(message.info)", function("nested", qualifiedName("message", "info")), null), + AstDSL.alias( + "nested(comment.data)", + function("nested", qualifiedName("comment", "data")), + null))); + } + + @Test + public void project_nested_invalid_field_throws_exception() { + var exception = + assertThrows( + IllegalArgumentException.class, + () -> + analyze( + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias( + "message", function("nested", qualifiedName("message")), null)))); + assertEquals(exception.getMessage(), "Illegal nested field name: message"); + } + + @Test + public void project_nested_invalid_arg_type_throws_exception() { + var exception = + assertThrows( + IllegalArgumentException.class, + () -> + analyze( + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias( + "message", function("nested", stringLiteral("message")), null)))); + assertEquals(exception.getMessage(), "Illegal nested field name: message"); + } + + @Test + public void project_nested_no_args_throws_exception() { + var exception = + assertThrows( + IllegalArgumentException.class, + () -> + analyze( + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias("message", function("nested"), null)))); + assertEquals( + exception.getMessage(), + "on nested object only allowed 2 parameters (field,path) or 1 parameter (field)"); + } + + @Test + public void project_nested_too_many_args_throws_exception() { + var exception = + assertThrows( + IllegalArgumentException.class, + () -> + analyze( + AstDSL.projectWithArg( + AstDSL.relation("schema"), + AstDSL.defaultFieldsArgs(), + AstDSL.alias( + "message", + function( + "nested", + stringLiteral("message.info"), + stringLiteral("message"), + stringLiteral("message")), + null)))); + assertEquals( + exception.getMessage(), + "on nested object only allowed 2 parameters (field,path) or 1 parameter (field)"); + } + @Test public void project_highlight() { Map args = new HashMap<>(); @@ -699,6 +1000,33 @@ public void select_all_from_subquery() { AstDSL.allFields())); } + /** + * Ensure function falls back to legacy engine when used in GROUP BY clause. TODO Remove + * this test when support is added. + */ + @Test + public void nested_group_by_clause_throws_syntax_exception() { + SyntaxCheckException exception = + assertThrows( + SyntaxCheckException.class, + () -> + analyze( + AstDSL.project( + AstDSL.agg( + AstDSL.relation("schema"), + emptyList(), + emptyList(), + ImmutableList.of( + alias( + "nested(message.info)", + function("nested", qualifiedName("message", "info")))), + emptyList())))); + assertEquals( + "Falling back to legacy engine. Nested function is not supported in WHERE," + + " GROUP BY, and HAVING clauses.", + exception.getMessage()); + } + /** SELECT name, AVG(age) FROM test GROUP BY name. */ @Test public void sql_group_by_field() { @@ -1120,43 +1448,7 @@ public void table_function() { @Test public void table_function_with_datasource_with_no_functions() { - DataSourceService dataSourceService = new DataSourceService() { - @Override - public DataSource getDataSource(String dataSourceName) { - return new DataSource(DEFAULT_DATASOURCE_NAME, DataSourceType.OPENSEARCH, storageEngine()); - } - - @Override - public Set getDataSourceMetadata(boolean isDefaultDataSourceRequired) { - return Set.of(); - } - - @Override - public DataSourceMetadata getDataSourceMetadata(String name) { - return null; - } - - @Override - public void createDataSource(DataSourceMetadata metadata) { - - } - - @Override - public void updateDataSource(DataSourceMetadata dataSourceMetadata) { - - } - - @Override - public void deleteDataSource(String dataSourceName) { - - } - - @Override - public Boolean dataSourceExists(String dataSourceName) { - return null; - } - }; - + DataSourceService dataSourceService = getEmptyDataSourceService(); Analyzer analyzer = new Analyzer(super.expressionAnalyzer, dataSourceService, BuiltinFunctionRepository.getInstance(dataSourceService)); ExpressionEvaluationException exception = assertThrows( diff --git a/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTestBase.java b/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTestBase.java index 4b3ac02acc..b21de1f734 100644 --- a/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTestBase.java +++ b/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTestBase.java @@ -26,6 +26,7 @@ import org.opensearch.sql.analysis.symbol.SymbolTable; import org.opensearch.sql.ast.tree.UnresolvedPlan; import org.opensearch.sql.config.TestConfig; +import org.opensearch.sql.data.model.ExprValue; import org.opensearch.sql.data.type.ExprType; import org.opensearch.sql.datasource.DataSourceService; import org.opensearch.sql.datasource.model.DataSource; @@ -33,8 +34,10 @@ import org.opensearch.sql.datasource.model.DataSourceType; import org.opensearch.sql.exception.ExpressionEvaluationException; import org.opensearch.sql.expression.Expression; +import org.opensearch.sql.expression.FunctionExpression; import org.opensearch.sql.expression.ReferenceExpression; import org.opensearch.sql.expression.env.Environment; +import org.opensearch.sql.expression.function.BuiltinFunctionName; import org.opensearch.sql.expression.function.BuiltinFunctionRepository; import org.opensearch.sql.expression.function.FunctionBuilder; import org.opensearch.sql.expression.function.FunctionName; @@ -53,7 +56,41 @@ protected Map typeMapping() { } protected StorageEngine storageEngine() { - return (dataSourceSchemaName, tableName) -> table; + return new StorageEngine() { + @Override + public Collection getFunctions() { + return Collections.singletonList( + new FunctionResolver() { + @Override + public Pair resolve( + FunctionSignature unresolvedSignature) { + return Pair.of(unresolvedSignature, + (functionProperties, arguments) -> + new FunctionExpression(BuiltinFunctionName.NESTED.getName(), arguments) { + @Override + public ExprValue valueOf(Environment valueEnv) { + return valueEnv.resolve(getArguments().get(0)); + } + + @Override + public ExprType type() { + return getArguments().get(0).type(); + } + }); + } + + @Override + public FunctionName getFunctionName() { + return BuiltinFunctionName.NESTED.getName(); + } + }); + } + + @Override + public Table getTable(DataSourceSchemaName dataSourceSchemaName, String tableName) { + return table; + } + }; } protected StorageEngine prometheusStorageEngine() { diff --git a/core/src/test/java/org/opensearch/sql/analysis/ExpressionReferenceOptimizerTest.java b/core/src/test/java/org/opensearch/sql/analysis/ExpressionReferenceOptimizerTest.java index b6d622922a..7e68bafd27 100644 --- a/core/src/test/java/org/opensearch/sql/analysis/ExpressionReferenceOptimizerTest.java +++ b/core/src/test/java/org/opensearch/sql/analysis/ExpressionReferenceOptimizerTest.java @@ -10,6 +10,7 @@ import static org.opensearch.sql.data.type.ExprCoreType.DOUBLE; import static org.opensearch.sql.data.type.ExprCoreType.INTEGER; import static org.opensearch.sql.data.type.ExprCoreType.STRING; +import static org.opensearch.sql.datasource.model.EmptyDataSourceService.getEmptyDataSourceService; import com.google.common.collect.ImmutableList; import org.junit.jupiter.api.Test; @@ -132,7 +133,7 @@ Expression optimize(Expression expression) { } Expression optimize(Expression expression, LogicalPlan logicalPlan) { - BuiltinFunctionRepository functionRepository = BuiltinFunctionRepository.getInstance(null); + BuiltinFunctionRepository functionRepository = BuiltinFunctionRepository.getInstance(getEmptyDataSourceService()); final ExpressionReferenceOptimizer optimizer = new ExpressionReferenceOptimizer(functionRepository, logicalPlan); return optimizer.optimize(DSL.named(expression), new AnalysisContext()); diff --git a/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java b/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java index 301331d6e7..ecc665f653 100644 --- a/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java +++ b/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java @@ -6,6 +6,7 @@ package org.opensearch.sql.expression.datetime; import static org.opensearch.sql.data.model.ExprValueUtils.fromObjectValue; +import static org.opensearch.sql.datasource.model.EmptyDataSourceService.getEmptyDataSourceService; import java.time.Instant; import java.time.LocalDate; @@ -28,7 +29,7 @@ public class DateTimeTestBase extends ExpressionTestBase { protected final BuiltinFunctionRepository functionRepository = - BuiltinFunctionRepository.getInstance(null); + BuiltinFunctionRepository.getInstance(getEmptyDataSourceService()); protected ExprValue eval(Expression expression) { return expression.valueOf(); diff --git a/integ-test/src/test/java/org/opensearch/sql/ppl/StandaloneIT.java b/integ-test/src/test/java/org/opensearch/sql/ppl/StandaloneIT.java index 3ea84a882b..8eb6b1ed43 100644 --- a/integ-test/src/test/java/org/opensearch/sql/ppl/StandaloneIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/ppl/StandaloneIT.java @@ -7,6 +7,7 @@ package org.opensearch.sql.ppl; import static org.opensearch.sql.datasource.model.DataSourceMetadata.defaultOpenSearchDataSourceMetadata; +import static org.opensearch.sql.datasource.model.EmptyDataSourceService.getEmptyDataSourceService; import static org.opensearch.sql.protocol.response.format.JsonResponseFormatter.Style.PRETTY; import com.google.common.collect.ImmutableMap; @@ -181,7 +182,7 @@ public class StandaloneModule extends AbstractModule { private final DataSourceService dataSourceService; private final BuiltinFunctionRepository functionRepository = - BuiltinFunctionRepository.getInstance(null); + BuiltinFunctionRepository.getInstance(getEmptyDataSourceService()); @Override protected void configure() {} diff --git a/integ-test/src/test/java/org/opensearch/sql/util/StandaloneModule.java b/integ-test/src/test/java/org/opensearch/sql/util/StandaloneModule.java index 69074ae657..b8c96ab081 100644 --- a/integ-test/src/test/java/org/opensearch/sql/util/StandaloneModule.java +++ b/integ-test/src/test/java/org/opensearch/sql/util/StandaloneModule.java @@ -36,6 +36,8 @@ import org.opensearch.sql.sql.antlr.SQLSyntaxParser; import org.opensearch.sql.storage.StorageEngine; +import static org.opensearch.sql.datasource.model.EmptyDataSourceService.getEmptyDataSourceService; + /** * A utility class which registers SQL engine singletons as `OpenSearchPluginModule` does. * It is needed to get access to those instances in test and validate their behavior. @@ -50,7 +52,7 @@ public class StandaloneModule extends AbstractModule { private final DataSourceService dataSourceService; private final BuiltinFunctionRepository functionRepository = - BuiltinFunctionRepository.getInstance(null); + BuiltinFunctionRepository.getInstance(getEmptyDataSourceService()); @Override protected void configure() {