From e7e2ecb2f519c50132def0a2db14e1355865cb4b Mon Sep 17 00:00:00 2001 From: Peng Huo Date: Mon, 11 May 2020 16:56:57 -0700 Subject: [PATCH] Feature/analyzer (#464) * update * update * add doc * change the package name from schema to symbol * fix build issue --- .gitignore | 1 + core/build.gradle | 7 ++ .../sql/analysis/AnalysisContext.java | 61 +++++++++++ .../sql/analysis/Analyzer.java | 58 ++++++++++ .../sql/analysis/ExpressionAnalyzer.java | 69 ++++++++++++ .../sql/analysis/TypeEnvironment.java | 85 +++++++++++++++ .../sql/analysis/symbol/Namespace.java | 32 ++++++ .../sql/analysis/symbol/Symbol.java | 29 +++++ .../sql/analysis/symbol/SymbolTable.java | 84 +++++++++++++++ .../sql/ast/expression/QualifiedName.java | 2 +- .../sql/exception/SemanticCheckException.java | 25 +++++ .../sql/expression/FunctionExpression.java | 2 + .../sql/expression/LiteralExpression.java | 7 ++ .../sql/expression/ReferenceExpression.java | 7 ++ .../sql/planner/AbstractPlanNodeVisitor.java | 41 +++++++ .../sql/planner/PlanNode.java | 42 ++++++++ .../sql/planner/logical/LogicalFilter.java | 46 ++++++++ .../sql/planner/logical/LogicalPlan.java | 24 +++++ .../sql/planner/logical/LogicalPlanDSL.java | 33 ++++++ .../sql/planner/logical/LogicalRelation.java | 44 ++++++++ .../sql/storage/StorageEngine.java | 27 +++++ .../sql/storage/Table.java | 31 ++++++ .../sql/analysis/AnalysisContextTest.java | 48 +++++++++ .../sql/analysis/AnalyzerTest.java | 51 +++++++++ .../sql/analysis/AnalyzerTestBase.java | 73 +++++++++++++ .../sql/analysis/ExpressionAnalyzerTest.java | 63 +++++++++++ .../sql/analysis/TypeEnvironmentTest.java | 101 ++++++++++++++++++ .../sql/analysis/symbol/SymbolTableTest.java | 86 +++++++++++++++ .../sql/config/TestConfig.java | 96 +++++++++++++++++ .../sql/expression/ExpressionTestBase.java | 43 ++------ .../expression/ReferenceExpressionTest.java | 2 + .../arthmetic/ArithmeticFunctionTest.java | 2 + .../BinaryPredicateFunctionTest.java | 2 + .../predicate/UnaryPredicateFunctionTest.java | 2 + .../planner/AbstractPlanNodeVisitorTest.java | 73 +++++++++++++ .../planner/logical/LogicalRelationTest.java | 29 +++++ 36 files changed, 1392 insertions(+), 36 deletions(-) create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalysisContext.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/Analyzer.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/ExpressionAnalyzer.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/TypeEnvironment.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/Namespace.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/Symbol.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/SymbolTable.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/exception/SemanticCheckException.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/AbstractPlanNodeVisitor.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/PlanNode.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalFilter.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalPlan.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalPlanDSL.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalRelation.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/storage/StorageEngine.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/storage/Table.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalysisContextTest.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalyzerTest.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalyzerTestBase.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/ExpressionAnalyzerTest.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/TypeEnvironmentTest.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/SymbolTableTest.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/config/TestConfig.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/planner/AbstractPlanNodeVisitorTest.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalRelationTest.java diff --git a/.gitignore b/.gitignore index 89dc919cff..9c5cbd0951 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ build/ # git mergetool artifact *.orig gen +*.tokens diff --git a/core/build.gradle b/core/build.gradle index 5e72011dc2..7c848dc9c2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -16,6 +16,7 @@ dependencies { compile group: 'org.springframework', name: 'spring-beans', version: '5.2.5.RELEASE' testImplementation('org.junit.jupiter:junit-jupiter:5.6.2') + testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '2.1' testCompile group: 'org.springframework', name: 'spring-test', version: '5.2.5.RELEASE' testCompile group: 'org.mockito', name: 'mockito-core', version: '3.3.3' testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '3.3.3' @@ -35,6 +36,12 @@ jacocoTestReport { reports { html.enabled true } + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, + exclude: ['**/ast/**']) + })) + } } test.finalizedBy(project.tasks.jacocoTestReport) jacocoTestCoverageVerification { diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalysisContext.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalysisContext.java new file mode 100644 index 0000000000..f9d51b32c8 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalysisContext.java @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import java.util.Objects; + +/** + * The context used for Analyzer. + */ +public class AnalysisContext { + /** Environment stack for symbol scope management */ + private TypeEnvironment environment; + + public AnalysisContext() { + this.environment = new TypeEnvironment(null); + } + + public AnalysisContext(TypeEnvironment environment) { + this.environment = environment; + } + + /** + * Push a new environment + */ + public void push() { + environment = new TypeEnvironment(environment); + } + + /** + * Return current environment + * @return current environment + */ + public TypeEnvironment peek() { + return environment; + } + + /** + * Pop up current environment from environment chain + * @return current environment (before pop) + */ + public TypeEnvironment pop() { + Objects.requireNonNull(environment, "Fail to pop context due to no environment present"); + + TypeEnvironment curEnv = environment; + environment = curEnv.getParent(); + return curEnv; + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/Analyzer.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/Analyzer.java new file mode 100644 index 0000000000..ff9211e357 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/Analyzer.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import com.amazon.opendistroforelasticsearch.sql.ast.AbstractNodeVisitor; +import com.amazon.opendistroforelasticsearch.sql.ast.tree.Filter; +import com.amazon.opendistroforelasticsearch.sql.ast.tree.Relation; +import com.amazon.opendistroforelasticsearch.sql.ast.tree.UnresolvedPlan; +import com.amazon.opendistroforelasticsearch.sql.expression.DSL; +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalFilter; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalPlan; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalRelation; +import com.amazon.opendistroforelasticsearch.sql.storage.StorageEngine; +import com.amazon.opendistroforelasticsearch.sql.storage.Table; +import lombok.RequiredArgsConstructor; + +/** + * Analyze the {@link UnresolvedPlan} in the {@link AnalysisContext} to construct the {@link LogicalPlan} + */ +@RequiredArgsConstructor +public class Analyzer extends AbstractNodeVisitor { + private final ExpressionAnalyzer expressionAnalyzer; + private final StorageEngine storageEngine; + + public LogicalPlan analyze(UnresolvedPlan unresolved, AnalysisContext context) { + return unresolved.accept(this, context); + } + + @Override + public LogicalPlan visitRelation(Relation node, AnalysisContext context) { + context.push(); + TypeEnvironment curEnv = context.peek(); + Table table = storageEngine.getTable(node.getTableName()); + table.getFieldTypes().forEach((k, v) -> curEnv.define(DSL.ref(k), v)); + return new LogicalRelation(node.getTableName()); + } + + @Override + public LogicalPlan visitFilter(Filter node, AnalysisContext context) { + LogicalPlan child = node.getChild().get(0).accept(this, context); + Expression condition = expressionAnalyzer.analyze(node.getCondition(), context); + return new LogicalFilter(condition, child); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/ExpressionAnalyzer.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/ExpressionAnalyzer.java new file mode 100644 index 0000000000..7d369bde94 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/ExpressionAnalyzer.java @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import com.amazon.opendistroforelasticsearch.sql.ast.AbstractNodeVisitor; +import com.amazon.opendistroforelasticsearch.sql.ast.expression.And; +import com.amazon.opendistroforelasticsearch.sql.ast.expression.EqualTo; +import com.amazon.opendistroforelasticsearch.sql.ast.expression.Literal; +import com.amazon.opendistroforelasticsearch.sql.ast.expression.UnresolvedAttribute; +import com.amazon.opendistroforelasticsearch.sql.ast.expression.UnresolvedExpression; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; +import com.amazon.opendistroforelasticsearch.sql.expression.DSL; +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import com.amazon.opendistroforelasticsearch.sql.expression.ReferenceExpression; +import lombok.RequiredArgsConstructor; + +/** + * Analyze the {@link UnresolvedExpression} in the {@link AnalysisContext} to construct the {@link Expression} + */ +@RequiredArgsConstructor +public class ExpressionAnalyzer extends AbstractNodeVisitor { + private final DSL dsl; + + public Expression analyze(UnresolvedExpression unresolved, AnalysisContext context) { + return unresolved.accept(this, context); + } + + @Override + public Expression visitUnresolvedAttribute(UnresolvedAttribute node, AnalysisContext context) { + TypeEnvironment typeEnv = context.peek(); + ReferenceExpression ref = DSL.ref(node.getAttr()); + typeEnv.resolve(ref); + return ref; + } + + @Override + public Expression visitEqualTo(EqualTo node, AnalysisContext context) { + Expression left = node.getLeft().accept(this, context); + Expression right = node.getRight().accept(this, context); + + return dsl.equal(context.peek(), left, right); + } + + @Override + public Expression visitLiteral(Literal node, AnalysisContext context) { + return DSL.literal(ExprValueUtils.fromObjectValue(node.getValue())); + } + + @Override + public Expression visitAnd(And node, AnalysisContext context) { + Expression left = node.getLeft().accept(this, context); + Expression right = node.getRight().accept(this, context); + + return dsl.and(context.peek(), left, right); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/TypeEnvironment.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/TypeEnvironment.java new file mode 100644 index 0000000000..cb05efb1a0 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/TypeEnvironment.java @@ -0,0 +1,85 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import com.amazon.opendistroforelasticsearch.sql.analysis.symbol.Namespace; +import com.amazon.opendistroforelasticsearch.sql.analysis.symbol.Symbol; +import com.amazon.opendistroforelasticsearch.sql.analysis.symbol.SymbolTable; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; +import com.amazon.opendistroforelasticsearch.sql.exception.SemanticCheckException; +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import com.amazon.opendistroforelasticsearch.sql.expression.ReferenceExpression; +import com.amazon.opendistroforelasticsearch.sql.expression.env.Environment; +import lombok.Getter; + +import java.util.Optional; + +/** + * The definition of Type Environment. + */ +public class TypeEnvironment implements Environment { + @Getter + private final TypeEnvironment parent; + private final SymbolTable symbolTable; + + public TypeEnvironment(TypeEnvironment parent) { + this.parent = parent; + this.symbolTable = new SymbolTable(); + } + + public TypeEnvironment(TypeEnvironment parent, SymbolTable symbolTable) { + this.parent = parent; + this.symbolTable = symbolTable; + } + + /** + * Resolve the {@link Expression} from environment. + * + * @param var expression + * @return resolved {@link ExprType} + */ + @Override + public ExprType resolve(Expression var) { + if (var instanceof ReferenceExpression) { + ReferenceExpression ref = (ReferenceExpression) var; + for (TypeEnvironment cur = this; cur != null; cur = cur.parent) { + Optional typeOptional = cur.symbolTable.lookup(new Symbol(Namespace.FIELD_NAME, + ref.getAttr())); + if (typeOptional.isPresent()) { + return typeOptional.get(); + } + } + } + throw new SemanticCheckException(String.format("can't resolve expression %s in type env", var)); + } + + /** + * Define symbol with the type + * + * @param var symbol to define + * @param type type + */ + public void define(Expression var, ExprType type) { + if (var instanceof ReferenceExpression) { + ReferenceExpression ref = (ReferenceExpression) var; + symbolTable.store(new Symbol(Namespace.FIELD_NAME, ref.getAttr()), type); + } else { + throw new IllegalArgumentException(String.format("only support define reference, unexpected expression %s" + , var)); + } + } + +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/Namespace.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/Namespace.java new file mode 100644 index 0000000000..79fd719f31 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/Namespace.java @@ -0,0 +1,32 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis.symbol; + +/** + * Namespace of symbol to avoid naming conflict + */ +public enum Namespace { + + FIELD_NAME("Field"), + FUNCTION_NAME("Function"); + + private final String name; + + Namespace(String name) { + this.name = name; + } + +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/Symbol.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/Symbol.java new file mode 100644 index 0000000000..e5a734b505 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/Symbol.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis.symbol; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * Symbol in the scope + */ +@Getter +@RequiredArgsConstructor +public class Symbol { + private final Namespace namespace; + private final String name; +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/SymbolTable.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/SymbolTable.java new file mode 100644 index 0000000000..ea3daa18af --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/SymbolTable.java @@ -0,0 +1,84 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis.symbol; + +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; + +import java.util.EnumMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.TreeMap; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptyNavigableMap; + +/** + * Symbol table for symbol definition and resolution. + */ +public class SymbolTable { + + /** Two-dimension hash table to manage symbols with type in different namespace */ + private Map> tableByNamespace = new EnumMap<>(Namespace.class); + + /** + * Store symbol with the type. Create new map for namespace for the first time. + * @param symbol symbol to define + * @param type symbol type + */ + public void store(Symbol symbol, ExprType type) { + tableByNamespace.computeIfAbsent( + symbol.getNamespace(), + ns -> new TreeMap<>() + ).put(symbol.getName(), type); + } + + /** + * Look up symbol in the namespace map. + * @param symbol symbol to look up + * @return symbol type which is optional + */ + public Optional lookup(Symbol symbol) { + Map table = tableByNamespace.get(symbol.getNamespace()); + ExprType type = null; + if (table != null) { + type = table.get(symbol.getName()); + } + return Optional.ofNullable(type); + } + + /** + * Look up symbols by a prefix. + * @param prefix a symbol prefix + * @return symbols starting with the prefix + */ + public Map lookupByPrefix(Symbol prefix) { + NavigableMap table = tableByNamespace.get(prefix.getNamespace()); + if (table != null) { + return table.subMap(prefix.getName(), prefix.getName() + Character.MAX_VALUE); + } + return emptyMap(); + } + + /** + * Check if namespace map in empty (none definition) + * @param namespace a namespace + * @return true for empty + */ + public boolean isEmpty(Namespace namespace) { + return tableByNamespace.getOrDefault(namespace, emptyNavigableMap()).isEmpty(); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/ast/expression/QualifiedName.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/ast/expression/QualifiedName.java index 47b62d9b41..cf4d656f84 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/ast/expression/QualifiedName.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/ast/expression/QualifiedName.java @@ -25,7 +25,7 @@ import java.util.stream.StreamSupport; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.ToString; + import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/exception/SemanticCheckException.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/exception/SemanticCheckException.java new file mode 100644 index 0000000000..bd21ff4465 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/exception/SemanticCheckException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.exception; + +/** + * Semantic Check Exception. + */ +public class SemanticCheckException extends QueryEngineException { + public SemanticCheckException(String message) { + super(message); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/FunctionExpression.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/FunctionExpression.java index dec69182c8..e4f0b37efd 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/FunctionExpression.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/FunctionExpression.java @@ -16,6 +16,7 @@ package com.amazon.opendistroforelasticsearch.sql.expression; import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionName; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -24,6 +25,7 @@ /** * Function Expression */ +@EqualsAndHashCode @RequiredArgsConstructor public abstract class FunctionExpression implements Expression { @Getter diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/LiteralExpression.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/LiteralExpression.java index 20a1d39676..e6ae042e7a 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/LiteralExpression.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/LiteralExpression.java @@ -18,11 +18,13 @@ import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.expression.env.Environment; +import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; /** * Literal Expression */ +@EqualsAndHashCode @RequiredArgsConstructor public class LiteralExpression implements Expression { private final ExprValue exprValue; @@ -36,4 +38,9 @@ public ExprValue valueOf(Environment env) { public ExprType type(Environment env) { return exprValue.type(); } + + @Override + public String toString() { + return exprValue.toString(); + } } diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/ReferenceExpression.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/ReferenceExpression.java index 2853a3e9f5..03eb384b0b 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/ReferenceExpression.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/ReferenceExpression.java @@ -18,9 +18,11 @@ import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.expression.env.Environment; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; +@EqualsAndHashCode @RequiredArgsConstructor public class ReferenceExpression implements Expression { @Getter @@ -35,4 +37,9 @@ public ExprValue valueOf(Environment env) { public ExprType type(Environment env) { return env.resolve(this); } + + @Override + public String toString() { + return attr; + } } diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/AbstractPlanNodeVisitor.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/AbstractPlanNodeVisitor.java new file mode 100644 index 0000000000..623149193e --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/AbstractPlanNodeVisitor.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.planner; + +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalFilter; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalPlan; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalRelation; + +/** + * Abstract {@link PlanNode} Visitor. + * + * @param return object type. + * @param context type. + */ +public abstract class AbstractPlanNodeVisitor { + + protected R visitNode(LogicalPlan plan, C context) { + return null; + } + + public R visitRelation(LogicalRelation plan, C context) { + return visitNode(plan, context); + } + + public R visitFilter(LogicalFilter plan, C context) { + return visitNode(plan, context); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/PlanNode.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/PlanNode.java new file mode 100644 index 0000000000..80ccf9b285 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/PlanNode.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.planner; + +import java.util.List; + +/** + * The definition of Plan Node + */ +public interface PlanNode { + + /** + * Return the child nodes. + * + * @return child nodes. + */ + List getChild(); + + /** + * Accept the visitor. + * + * @param visitor visitor. + * @param context visitor context. + * @param returned object type. + * @param context type. + * @return returned object. + */ + R accept(AbstractPlanNodeVisitor visitor, C context); +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalFilter.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalFilter.java new file mode 100644 index 0000000000..3d807b9ce9 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalFilter.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.planner.logical; + +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import com.amazon.opendistroforelasticsearch.sql.planner.AbstractPlanNodeVisitor; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +import java.util.Arrays; +import java.util.List; + +/** + * Logical Filter represent the filter relation. + */ +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor +public class LogicalFilter extends LogicalPlan { + private final Expression condition; + private final LogicalPlan child; + + @Override + public List getChild() { + return Arrays.asList(child); + } + + @Override + public R accept(AbstractPlanNodeVisitor visitor, C context) { + return visitor.visitFilter(this, context); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalPlan.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalPlan.java new file mode 100644 index 0000000000..2047ff8e03 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalPlan.java @@ -0,0 +1,24 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.planner.logical; + +import com.amazon.opendistroforelasticsearch.sql.planner.PlanNode; + +/** + * The abstract base class for all the Logical Plan node. + */ +public abstract class LogicalPlan implements PlanNode { +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalPlanDSL.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalPlanDSL.java new file mode 100644 index 0000000000..ea8c8135ce --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalPlanDSL.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.planner.logical; + +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import lombok.experimental.UtilityClass; + +/** + * Logical Plan DSL. + */ +@UtilityClass +public class LogicalPlanDSL { + public static LogicalPlan filter(Expression expression, LogicalPlan input) { + return new LogicalFilter(expression, input); + } + + public static LogicalPlan relation(String tableName) { + return new LogicalRelation(tableName); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalRelation.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalRelation.java new file mode 100644 index 0000000000..8351c3d858 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalRelation.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.planner.logical; + +import com.amazon.opendistroforelasticsearch.sql.planner.AbstractPlanNodeVisitor; +import com.google.common.collect.ImmutableList; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +import java.util.List; + +/** + * Logical Relation represent the data source. + */ +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor +public class LogicalRelation extends LogicalPlan { + private final String relationName; + + @Override + public List getChild() { + return ImmutableList.of(); + } + + @Override + public R accept(AbstractPlanNodeVisitor visitor, C context) { + return visitor.visitRelation(this, context); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/storage/StorageEngine.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/storage/StorageEngine.java new file mode 100644 index 0000000000..68da18d536 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/storage/StorageEngine.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.storage; + +/** + * Storage engine for different storage to provide data access API implementation + */ +public interface StorageEngine { + + /** + * Get {@link Table} from storage engine. + */ + Table getTable(String name); +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/storage/Table.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/storage/Table.java new file mode 100644 index 0000000000..bb6a3da924 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/storage/Table.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.storage; + +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; + +import java.util.Map; + +/** + * Table + */ +public interface Table { + /** + * Get the {@link ExprType} for each field in the table. + */ + Map getFieldTypes(); + +} diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalysisContextTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalysisContextTest.java new file mode 100644 index 0000000000..22dd81e2b7 --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalysisContextTest.java @@ -0,0 +1,48 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class AnalysisContextTest { + + private final AnalysisContext context = new AnalysisContext(); + + @Test + public void rootEnvironmentShouldBeThereInitially() { + assertNotNull(context.peek()); + } + + @Test + public void pushAndPopEnvironmentShouldPass() { + context.push(); + context.pop(); + } + + @Test + public void popRootEnvironmentShouldPass() { + context.pop(); + } + + @Test + public void popEmptyEnvironmentStackShouldFail() { + context.pop(); + NullPointerException exception = assertThrows(NullPointerException.class, () -> context.pop()); + assertEquals("Fail to pop context due to no environment present", exception.getMessage()); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalyzerTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalyzerTest.java new file mode 100644 index 0000000000..db0a9557bb --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalyzerTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL; +import com.amazon.opendistroforelasticsearch.sql.ast.tree.UnresolvedPlan; +import com.amazon.opendistroforelasticsearch.sql.expression.DSL; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalPlan; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalPlanDSL; +import org.junit.jupiter.api.Test; + +import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.integerValue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AnalyzerTest extends AnalyzerTestBase { + + @Test + public void filter_relation() { + assertAnalyzeEqual( + LogicalPlanDSL.filter( + dsl.equal(typeEnv, DSL.ref("integer_value"), DSL.literal(integerValue(1))), + LogicalPlanDSL.relation("schema") + ), + AstDSL.filter( + AstDSL.relation("schema"), + AstDSL.equalTo(AstDSL.unresolvedAttr("integer_value"), AstDSL.intLiteral(1)) + ) + ); + } + + protected void assertAnalyzeEqual(LogicalPlan expected, UnresolvedPlan unresolvedPlan) { + assertEquals(expected, analyze(unresolvedPlan)); + } + + protected LogicalPlan analyze(UnresolvedPlan unresolvedPlan) { + return analyzer.analyze(unresolvedPlan, analysisContext); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalyzerTestBase.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalyzerTestBase.java new file mode 100644 index 0000000000..643b6a18d5 --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/AnalyzerTestBase.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import com.amazon.opendistroforelasticsearch.sql.analysis.symbol.SymbolTable; +import com.amazon.opendistroforelasticsearch.sql.config.TestConfig; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; +import com.amazon.opendistroforelasticsearch.sql.expression.DSL; +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import com.amazon.opendistroforelasticsearch.sql.expression.config.ExpressionConfig; +import com.amazon.opendistroforelasticsearch.sql.expression.env.Environment; +import com.amazon.opendistroforelasticsearch.sql.storage.StorageEngine; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + + +@Configuration +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {ExpressionConfig.class, AnalyzerTestBase.class, TestConfig.class}) +public class AnalyzerTestBase { + + @Autowired + protected DSL dsl; + + @Autowired + protected AnalysisContext analysisContext; + + @Autowired + protected ExpressionAnalyzer expressionAnalyzer; + + @Autowired + protected Analyzer analyzer; + + @Autowired + protected Environment typeEnv; + + @Bean + protected Analyzer analyzer(ExpressionAnalyzer expressionAnalyzer, StorageEngine engine) { + return new Analyzer(expressionAnalyzer, engine); + } + + @Bean + protected TypeEnvironment typeEnvironment(SymbolTable symbolTable) { + return new TypeEnvironment(null, symbolTable); + } + + @Bean + protected AnalysisContext analysisContext(TypeEnvironment typeEnvironment) { + return new AnalysisContext(typeEnvironment); + } + + @Bean + protected ExpressionAnalyzer expressionAnalyzer(DSL dsl) { + return new ExpressionAnalyzer(dsl); + } +} diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/ExpressionAnalyzerTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/ExpressionAnalyzerTest.java new file mode 100644 index 0000000000..19866f3671 --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/ExpressionAnalyzerTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL; +import com.amazon.opendistroforelasticsearch.sql.ast.expression.UnresolvedExpression; +import com.amazon.opendistroforelasticsearch.sql.exception.SemanticCheckException; +import com.amazon.opendistroforelasticsearch.sql.expression.DSL; +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import org.junit.jupiter.api.Test; + +import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_TRUE; +import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.integerValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + + +class ExpressionAnalyzerTest extends AnalyzerTestBase { + + @Test + public void equal() { + assertAnalyzeEqual( + dsl.equal(typeEnv, DSL.ref("integer_value"), DSL.literal(integerValue(1))), + AstDSL.equalTo(AstDSL.unresolvedAttr("integer_value"), AstDSL.intLiteral(1)) + ); + } + + @Test + public void and() { + assertAnalyzeEqual( + dsl.and(typeEnv, DSL.ref("boolean_value"), DSL.literal(LITERAL_TRUE)), + AstDSL.and(AstDSL.unresolvedAttr("boolean_value"), AstDSL.booleanLiteral(true)) + ); + } + + @Test + public void undefined_var_semantic_check_failed() { + SemanticCheckException exception = assertThrows(SemanticCheckException.class, + () -> analyze(AstDSL.and(AstDSL.unresolvedAttr("undefined_field"), AstDSL.booleanLiteral(true)))); + assertEquals("can't resolve expression undefined_field in type env", exception.getMessage()); + } + + protected Expression analyze(UnresolvedExpression unresolvedExpression) { + return expressionAnalyzer.analyze(unresolvedExpression, analysisContext); + } + + protected void assertAnalyzeEqual(Expression expected, UnresolvedExpression unresolvedExpression) { + assertEquals(expected, analyze(unresolvedExpression)); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/TypeEnvironmentTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/TypeEnvironmentTest.java new file mode 100644 index 0000000000..d9555c3ca6 --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/TypeEnvironmentTest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis; + +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; +import com.amazon.opendistroforelasticsearch.sql.exception.SemanticCheckException; +import com.amazon.opendistroforelasticsearch.sql.expression.DSL; +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class TypeEnvironmentTest { + + /** + * Use context class for push/pop + */ + private AnalysisContext context = new AnalysisContext(); + + @Test + public void defineFieldSymbolInDifferentEnvironmentsShouldBeAbleToResolve() { + // Root environment + Expression age = DSL.ref("s.age"); + environment().define(age, ExprType.INTEGER); + assertEquals(ExprType.INTEGER, environment().resolve(age)); + + // New environment 1 + context.push(); + Expression city = DSL.ref("s.city"); + environment().define(city, ExprType.STRING); + assertEquals(ExprType.INTEGER, environment().resolve(age)); + assertEquals(ExprType.STRING, environment().resolve(city)); + + // New environment 2 + context.push(); + Expression manager = DSL.ref("s.manager"); + environment().define(manager, ExprType.STRUCT); + assertEquals(ExprType.INTEGER, environment().resolve(age)); + assertEquals(ExprType.STRING, environment().resolve(city)); + assertEquals(ExprType.STRUCT, environment().resolve(manager)); + } + + @Test + public void defineFieldSymbolInDifferentEnvironmentsShouldNotAbleToResolveOncePopped() { + // Root environment + Expression age = DSL.ref("s.age"); + environment().define(age, ExprType.INTEGER); + + // New environment + context.push(); + Expression city = DSL.ref("s.city"); + environment().define(city, ExprType.STRING); + Expression manager = DSL.ref("s.manager"); + environment().define(manager, ExprType.STRUCT); + assertEquals(ExprType.INTEGER, environment().resolve(age)); + assertEquals(ExprType.STRING, environment().resolve(city)); + assertEquals(ExprType.STRUCT, environment().resolve(manager)); + + context.pop(); + assertEquals(ExprType.INTEGER, environment().resolve(age)); + SemanticCheckException exception = assertThrows(SemanticCheckException.class, () -> environment().resolve(city)); + assertEquals("can't resolve expression s.city in type env", exception.getMessage()); + exception = assertThrows(SemanticCheckException.class, () -> environment().resolve(manager)); + assertEquals("can't resolve expression s.manager in type env", exception.getMessage()); + } + + @Test + public void resolveLiteralInEnvFailed() { + SemanticCheckException exception = assertThrows(SemanticCheckException.class, + () -> environment().resolve(DSL.literal(ExprValueUtils.integerValue(1)))); + assertEquals("can't resolve expression 1 in type env", exception.getMessage()); + } + + + @Test + public void defineLiteralInEnvIsIllegal() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> environment().define(DSL.literal(ExprValueUtils.integerValue(1)), ExprType.INTEGER)); + assertEquals("only support define reference, unexpected expression 1", exception.getMessage()); + } + + private TypeEnvironment environment() { + return context.peek(); + } + +} diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/SymbolTableTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/SymbolTableTest.java new file mode 100644 index 0000000000..a44e443d1c --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/analysis/symbol/SymbolTableTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.analysis.symbol; + +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.Optional; + +import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprType.BOOLEAN; +import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprType.INTEGER; +import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprType.STRING; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.anEmptyMap; +import static org.hamcrest.Matchers.hasEntry; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +public class SymbolTableTest { + + private final SymbolTable symbolTable = new SymbolTable(); + + @Test + public void defineFieldSymbolShouldBeAbleToResolve() { + defineSymbolShouldBeAbleToResolve(new Symbol(Namespace.FIELD_NAME, "age"), INTEGER); + } + + + @Test + public void defineFieldSymbolShouldBeAbleToResolveByPrefix() { + symbolTable.store(new Symbol(Namespace.FIELD_NAME, "s.projects.active"), BOOLEAN); + symbolTable.store(new Symbol(Namespace.FIELD_NAME, "s.address"), STRING); + symbolTable.store(new Symbol(Namespace.FIELD_NAME, "s.manager.name"), STRING); + + Map typeByName = symbolTable.lookupByPrefix(new Symbol(Namespace.FIELD_NAME, "s.projects")); + + assertThat( + typeByName, + allOf( + aMapWithSize(1), + hasEntry("s.projects.active", BOOLEAN) + ) + ); + } + + @Test + public void failedToResolveSymbolNoNamespaceMatched() { + symbolTable.store(new Symbol(Namespace.FUNCTION_NAME, "customFunction"), BOOLEAN); + assertFalse(symbolTable.lookup(new Symbol(Namespace.FIELD_NAME, "s.projects")).isPresent()); + + assertThat(symbolTable.lookupByPrefix(new Symbol(Namespace.FIELD_NAME, "s.projects")), anEmptyMap()); + } + + @Test + public void isEmpty() { + symbolTable.store(new Symbol(Namespace.FUNCTION_NAME, "customFunction"), BOOLEAN); + assertTrue(symbolTable.isEmpty(Namespace.FIELD_NAME)); + } + + private void defineSymbolShouldBeAbleToResolve(Symbol symbol, ExprType expectedType) { + symbolTable.store(symbol, expectedType); + + Optional actualType = symbolTable.lookup(symbol); + assertTrue(actualType.isPresent()); + assertEquals(expectedType, actualType.get()); + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/config/TestConfig.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/config/TestConfig.java new file mode 100644 index 0000000000..6de45def33 --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/config/TestConfig.java @@ -0,0 +1,96 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.config; + +import com.amazon.opendistroforelasticsearch.sql.analysis.symbol.Namespace; +import com.amazon.opendistroforelasticsearch.sql.analysis.symbol.Symbol; +import com.amazon.opendistroforelasticsearch.sql.analysis.symbol.SymbolTable; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; +import com.amazon.opendistroforelasticsearch.sql.exception.ExpressionEvaluationException; +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import com.amazon.opendistroforelasticsearch.sql.expression.ReferenceExpression; +import com.amazon.opendistroforelasticsearch.sql.expression.env.Environment; +import com.amazon.opendistroforelasticsearch.sql.storage.StorageEngine; +import com.amazon.opendistroforelasticsearch.sql.storage.Table; +import com.google.common.collect.ImmutableMap; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; + +/** + * Configuration will be used for UT. + */ +@Configuration +public class TestConfig { + public static final String INT_TYPE_NULL_VALUE_FIELD = "int_null_value"; + public static final String INT_TYPE_MISSING_VALUE_FIELD = "int_missing_value"; + public static final String BOOL_TYPE_NULL_VALUE_FIELD = "null_value_boolean"; + public static final String BOOL_TYPE_MISSING_VALUE_FIELD = "missing_value_boolean"; + + private static Map typeMapping = new ImmutableMap.Builder() + .put("integer_value", ExprType.INTEGER) + .put(INT_TYPE_NULL_VALUE_FIELD, ExprType.INTEGER) + .put(INT_TYPE_MISSING_VALUE_FIELD, ExprType.INTEGER) + .put("long_value", ExprType.LONG) + .put("float_value", ExprType.FLOAT) + .put("double_value", ExprType.DOUBLE) + .put("boolean_value", ExprType.BOOLEAN) + .put(BOOL_TYPE_NULL_VALUE_FIELD, ExprType.BOOLEAN) + .put(BOOL_TYPE_MISSING_VALUE_FIELD, ExprType.BOOLEAN) + .put("string_value", ExprType.STRING) + .put("struct_value", ExprType.STRUCT) + .put("array_value", ExprType.ARRAY) + .build(); + + @Bean + protected StorageEngine storageEngine() { + return new StorageEngine() { + @Override + public Table getTable(String name) { + return new Table() { + @Override + public Map getFieldTypes() { + return typeMapping; + } + }; + } + }; + } + + + @Bean + protected SymbolTable symbolTable() { + SymbolTable symbolTable = new SymbolTable(); + typeMapping.entrySet() + .forEach( + entry -> symbolTable.store(new Symbol(Namespace.FIELD_NAME, entry.getKey()), entry.getValue())); + return symbolTable; + } + + @Bean + protected Environment typeEnv() { + return var -> { + if (var instanceof ReferenceExpression) { + ReferenceExpression refExpr = (ReferenceExpression) var; + if (typeMapping.containsKey(refExpr.getAttr())) { + return typeMapping.get(refExpr.getAttr()); + } + } + throw new ExpressionEvaluationException("type resolved failed"); + }; + } +} diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ExpressionTestBase.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ExpressionTestBase.java index e289d305b3..e40303f514 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ExpressionTestBase.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ExpressionTestBase.java @@ -15,9 +15,9 @@ package com.amazon.opendistroforelasticsearch.sql.expression; +import com.amazon.opendistroforelasticsearch.sql.config.TestConfig; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; -import com.amazon.opendistroforelasticsearch.sql.exception.ExpressionEvaluationException; import com.amazon.opendistroforelasticsearch.sql.expression.config.ExpressionConfig; import com.amazon.opendistroforelasticsearch.sql.expression.env.Environment; import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionName; @@ -33,6 +33,8 @@ import java.util.List; import java.util.function.BiFunction; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_NULL_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_NULL_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.booleanValue; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.collectionValue; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.doubleValue; @@ -46,16 +48,14 @@ @Configuration @ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = {ExpressionConfig.class, ExpressionTestBase.class}) +@ContextConfiguration(classes = {ExpressionConfig.class, ExpressionTestBase.class, TestConfig.class}) public class ExpressionTestBase { - public static final String INT_TYPE_NULL_VALUE_FIELD = "int_null_value"; - public static final String INT_TYPE_MISSING_VALUE_FIELD = "int_missing_value"; - public static final String BOOL_TYPE_NULL_VALUE_FIELD = "null_value_boolean"; - public static final String BOOL_TYPE_MISSING_VALUE_FIELD = "missing_value_boolean"; - @Autowired protected DSL dsl; + @Autowired + protected Environment typeEnv; + @Bean protected Environment valueEnv() { return var -> { @@ -88,34 +88,7 @@ protected Environment valueEnv() { @Bean protected Environment typeEnv() { - return var -> { - if (var instanceof ReferenceExpression) { - switch (((ReferenceExpression) var).getAttr()) { - case "integer_value": - case INT_TYPE_NULL_VALUE_FIELD: - case INT_TYPE_MISSING_VALUE_FIELD: - return ExprType.INTEGER; - case "long_value": - return ExprType.LONG; - case "float_value": - return ExprType.FLOAT; - case "double_value": - return ExprType.DOUBLE; - case "boolean_value": - return ExprType.BOOLEAN; - case "string_value": - return ExprType.STRING; - case "struct_value": - return ExprType.STRUCT; - case "array_value": - return ExprType.ARRAY; - case BOOL_TYPE_NULL_VALUE_FIELD: - case BOOL_TYPE_MISSING_VALUE_FIELD: - return ExprType.BOOLEAN; - } - } - throw new ExpressionEvaluationException("type resolved failed"); - }; + return typeEnv; } protected BiFunction, diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ReferenceExpressionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ReferenceExpressionTest.java index 849e4bda07..f1bd4a9099 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ReferenceExpressionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ReferenceExpressionTest.java @@ -23,6 +23,8 @@ import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_MISSING_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_NULL_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_MISSING; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_NULL; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.booleanValue; diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/arthmetic/ArithmeticFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/arthmetic/ArithmeticFunctionTest.java index 3dffdcf29d..863086d205 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/arthmetic/ArithmeticFunctionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/arthmetic/ArithmeticFunctionTest.java @@ -38,6 +38,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_MISSING_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_NULL_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_MISSING; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_NULL; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.integerValue; diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/predicate/BinaryPredicateFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/predicate/BinaryPredicateFunctionTest.java index 1f8912e08a..b300511991 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/predicate/BinaryPredicateFunctionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/predicate/BinaryPredicateFunctionTest.java @@ -33,6 +33,8 @@ import java.util.List; import java.util.stream.Stream; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_MISSING_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_NULL_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_FALSE; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_MISSING; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_NULL; diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/predicate/UnaryPredicateFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/predicate/UnaryPredicateFunctionTest.java index 3f7d6018f3..bfebc16d89 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/predicate/UnaryPredicateFunctionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/scalar/predicate/UnaryPredicateFunctionTest.java @@ -24,6 +24,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_MISSING_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_NULL_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_MISSING; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_NULL; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.booleanValue; diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/planner/AbstractPlanNodeVisitorTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/planner/AbstractPlanNodeVisitorTest.java new file mode 100644 index 0000000000..01f9c1ba04 --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/planner/AbstractPlanNodeVisitorTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.planner; + +import com.amazon.opendistroforelasticsearch.sql.expression.Expression; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalFilter; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalPlan; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalPlanDSL; +import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalRelation; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * Temporary added for UT coverage, Will be removed. + */ +@ExtendWith(MockitoExtension.class) +class AbstractPlanNodeVisitorTest { + @Mock + Expression expression; + + @Test + public void logicalPlanShouldTraversable() { + LogicalPlan logicalPlan = LogicalPlanDSL.filter( + expression, + LogicalPlanDSL.relation("schema") + ); + + Integer result = logicalPlan.accept(new NodesCount(), null); + assertEquals(2, result); + } + + @Test + public void testAbstractPlanNodeVisitorShouldReturnNull() { + LogicalPlan relation = LogicalPlanDSL.relation("schema"); + LogicalPlan filter = LogicalPlanDSL.filter(expression,relation); + + assertNull(relation.accept(new AbstractPlanNodeVisitor() {}, null)); + assertNull(filter.accept(new AbstractPlanNodeVisitor() {}, null)); + } + + private static class NodesCount extends AbstractPlanNodeVisitor { + @Override + public Integer visitRelation(LogicalRelation plan, Object context) { + return 1; + } + + @Override + public Integer visitFilter(LogicalFilter plan, Object context) { + return 1 + plan.getChild().stream() + .map(child -> child.accept(this, context)).collect(Collectors.summingInt(Integer::intValue)); + } + } +} \ No newline at end of file diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalRelationTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalRelationTest.java new file mode 100644 index 0000000000..b860b45007 --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/planner/logical/LogicalRelationTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.sql.planner.logical; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class LogicalRelationTest { + + @Test + public void logicalRelationHasNoInput() { + LogicalPlan relation = LogicalPlanDSL.relation("index"); + assertEquals(0, relation.getChild().size()); + } +} \ No newline at end of file