1 | /* | |
2 | * Copyright OpenSearch Contributors | |
3 | * SPDX-License-Identifier: Apache-2.0 | |
4 | */ | |
5 | ||
6 | ||
7 | package org.opensearch.sql.analysis; | |
8 | ||
9 | import java.util.HashMap; | |
10 | import java.util.List; | |
11 | import java.util.Map; | |
12 | import java.util.stream.Collectors; | |
13 | import org.opensearch.sql.expression.Expression; | |
14 | import org.opensearch.sql.expression.ExpressionNodeVisitor; | |
15 | import org.opensearch.sql.expression.FunctionExpression; | |
16 | import org.opensearch.sql.expression.NamedExpression; | |
17 | import org.opensearch.sql.expression.ReferenceExpression; | |
18 | import org.opensearch.sql.expression.aggregation.Aggregator; | |
19 | import org.opensearch.sql.expression.conditional.cases.CaseClause; | |
20 | import org.opensearch.sql.expression.conditional.cases.WhenClause; | |
21 | import org.opensearch.sql.expression.function.BuiltinFunctionRepository; | |
22 | import org.opensearch.sql.planner.logical.LogicalAggregation; | |
23 | import org.opensearch.sql.planner.logical.LogicalPlan; | |
24 | import org.opensearch.sql.planner.logical.LogicalPlanNodeVisitor; | |
25 | import org.opensearch.sql.planner.logical.LogicalWindow; | |
26 | ||
27 | /** | |
28 | * The optimizer used to replace the expression referred in the SelectClause | |
29 | * e.g. The query SELECT abs(name), sum(age)-avg(age) FROM test GROUP BY abs(name). | |
30 | * will be translated the AST | |
31 | * Project[abs(age), sub(sum(age), avg(age)) | |
32 | * Agg(agg=[sum(age), avg(age)], group=[abs(age)]] | |
33 | * Relation | |
34 | * The sum(age) and avg(age) in the Project could be replace by the analyzed reference, the | |
35 | * LogicalPlan should be | |
36 | * LogicalProject[Ref("abs(age)"), sub(Ref("sum(age)"), Ref("avg(age)")) | |
37 | * LogicalAgg(agg=[sum(age), avg(age)], group=[abs(age)]] | |
38 | * LogicalRelation | |
39 | */ | |
40 | public class ExpressionReferenceOptimizer | |
41 | extends ExpressionNodeVisitor<Expression, AnalysisContext> { | |
42 | private final BuiltinFunctionRepository repository; | |
43 | ||
44 | /** | |
45 | * The map of expression and it's reference. | |
46 | * For example, The NamedAggregator should produce the map of Aggregator to Ref(name) | |
47 | */ | |
48 | private final Map<Expression, Expression> expressionMap = new HashMap<>(); | |
49 | ||
50 | public ExpressionReferenceOptimizer( | |
51 | BuiltinFunctionRepository repository, LogicalPlan logicalPlan) { | |
52 | this.repository = repository; | |
53 | logicalPlan.accept(new ExpressionMapBuilder(), null); | |
54 | } | |
55 | ||
56 | public Expression optimize(Expression analyzed, AnalysisContext context) { | |
57 |
1
1. optimize : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::optimize → KILLED |
return analyzed.accept(this, context); |
58 | } | |
59 | ||
60 | @Override | |
61 | public Expression visitNode(Expression node, AnalysisContext context) { | |
62 |
1
1. visitNode : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitNode → KILLED |
return node; |
63 | } | |
64 | ||
65 | @Override | |
66 | public Expression visitFunction(FunctionExpression node, AnalysisContext context) { | |
67 |
1
1. visitFunction : negated conditional → KILLED |
if (expressionMap.containsKey(node)) { |
68 |
1
1. visitFunction : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitFunction → KILLED |
return expressionMap.get(node); |
69 | } else { | |
70 | final List<Expression> args = | |
71 |
1
1. lambda$visitFunction$0 : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::lambda$visitFunction$0 → KILLED |
node.getArguments().stream().map(expr -> expr.accept(this, context)) |
72 | .collect(Collectors.toList()); | |
73 |
1
1. visitFunction : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitFunction → KILLED |
return (Expression) repository.compile(node.getFunctionName(), args); |
74 | } | |
75 | } | |
76 | ||
77 | @Override | |
78 | public Expression visitAggregator(Aggregator<?> node, AnalysisContext context) { | |
79 |
1
1. visitAggregator : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitAggregator → KILLED |
return expressionMap.getOrDefault(node, node); |
80 | } | |
81 | ||
82 | @Override | |
83 | public Expression visitNamed(NamedExpression node, AnalysisContext context) { | |
84 |
1
1. visitNamed : negated conditional → KILLED |
if (expressionMap.containsKey(node)) { |
85 |
1
1. visitNamed : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitNamed → KILLED |
return expressionMap.get(node); |
86 | } | |
87 |
1
1. visitNamed : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitNamed → KILLED |
return node.getDelegated().accept(this, context); |
88 | } | |
89 | ||
90 | /** | |
91 | * Implement this because Case/When is not registered in function repository. | |
92 | */ | |
93 | @Override | |
94 | public Expression visitCase(CaseClause node, AnalysisContext context) { | |
95 |
1
1. visitCase : negated conditional → KILLED |
if (expressionMap.containsKey(node)) { |
96 |
1
1. visitCase : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitCase → KILLED |
return expressionMap.get(node); |
97 | } | |
98 | ||
99 | List<WhenClause> whenClauses = node.getWhenClauses() | |
100 | .stream() | |
101 |
1
1. lambda$visitCase$1 : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::lambda$visitCase$1 → KILLED |
.map(expr -> (WhenClause) expr.accept(this, context)) |
102 | .collect(Collectors.toList()); | |
103 | Expression defaultResult = null; | |
104 |
1
1. visitCase : negated conditional → KILLED |
if (node.getDefaultResult() != null) { |
105 | defaultResult = node.getDefaultResult().accept(this, context); | |
106 | } | |
107 |
1
1. visitCase : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitCase → KILLED |
return new CaseClause(whenClauses, defaultResult); |
108 | } | |
109 | ||
110 | @Override | |
111 | public Expression visitWhen(WhenClause node, AnalysisContext context) { | |
112 |
1
1. visitWhen : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer::visitWhen → KILLED |
return new WhenClause( |
113 | node.getCondition().accept(this, context), | |
114 | node.getResult().accept(this, context)); | |
115 | } | |
116 | ||
117 | ||
118 | /** | |
119 | * Expression Map Builder. | |
120 | */ | |
121 | class ExpressionMapBuilder extends LogicalPlanNodeVisitor<Void, Void> { | |
122 | ||
123 | @Override | |
124 | public Void visitNode(LogicalPlan plan, Void context) { | |
125 |
1
1. visitNode : removed call to java/util/List::forEach → KILLED |
plan.getChild().forEach(child -> child.accept(this, context)); |
126 | return null; | |
127 | } | |
128 | ||
129 | @Override | |
130 | public Void visitAggregation(LogicalAggregation plan, Void context) { | |
131 | // Create the mapping for all the aggregator. | |
132 |
1
1. visitAggregation : removed call to java/util/List::forEach → KILLED |
plan.getAggregatorList().forEach(namedAggregator -> expressionMap |
133 | .put(namedAggregator.getDelegated(), | |
134 | new ReferenceExpression(namedAggregator.getName(), namedAggregator.type()))); | |
135 | // Create the mapping for all the group by. | |
136 |
1
1. visitAggregation : removed call to java/util/List::forEach → KILLED |
plan.getGroupByList().forEach(groupBy -> expressionMap |
137 | .put(groupBy.getDelegated(), | |
138 | new ReferenceExpression(groupBy.getNameOrAlias(), groupBy.type()))); | |
139 | return null; | |
140 | } | |
141 | ||
142 | @Override | |
143 | public Void visitWindow(LogicalWindow plan, Void context) { | |
144 | Expression windowFunc = plan.getWindowFunction(); | |
145 | expressionMap.put(windowFunc, | |
146 | new ReferenceExpression(((NamedExpression) windowFunc).getName(), windowFunc.type())); | |
147 |
1
1. visitWindow : replaced return value with null for org/opensearch/sql/analysis/ExpressionReferenceOptimizer$ExpressionMapBuilder::visitWindow → SURVIVED |
return visitNode(plan, context); |
148 | } | |
149 | } | |
150 | } | |
Mutations | ||
57 |
1.1 |
|
62 |
1.1 |
|
67 |
1.1 |
|
68 |
1.1 |
|
71 |
1.1 |
|
73 |
1.1 |
|
79 |
1.1 |
|
84 |
1.1 |
|
85 |
1.1 |
|
87 |
1.1 |
|
95 |
1.1 |
|
96 |
1.1 |
|
101 |
1.1 |
|
104 |
1.1 |
|
107 |
1.1 |
|
112 |
1.1 |
|
125 |
1.1 |
|
132 |
1.1 |
|
136 |
1.1 |
|
147 |
1.1 |