1 | /* | |
2 | * Copyright OpenSearch Contributors | |
3 | * SPDX-License-Identifier: Apache-2.0 | |
4 | */ | |
5 | ||
6 | ||
7 | package org.opensearch.sql.analysis; | |
8 | ||
9 | import com.google.common.collect.ImmutableList; | |
10 | import java.util.Collections; | |
11 | import java.util.List; | |
12 | import java.util.Map; | |
13 | import java.util.stream.Collectors; | |
14 | import lombok.RequiredArgsConstructor; | |
15 | import org.opensearch.sql.analysis.symbol.Namespace; | |
16 | import org.opensearch.sql.ast.AbstractNodeVisitor; | |
17 | import org.opensearch.sql.ast.expression.Alias; | |
18 | import org.opensearch.sql.ast.expression.AllFields; | |
19 | import org.opensearch.sql.ast.expression.Field; | |
20 | import org.opensearch.sql.ast.expression.QualifiedName; | |
21 | import org.opensearch.sql.ast.expression.UnresolvedExpression; | |
22 | import org.opensearch.sql.data.type.ExprType; | |
23 | import org.opensearch.sql.expression.DSL; | |
24 | import org.opensearch.sql.expression.Expression; | |
25 | import org.opensearch.sql.expression.NamedExpression; | |
26 | import org.opensearch.sql.expression.ReferenceExpression; | |
27 | ||
28 | /** | |
29 | * Analyze the select list in the {@link AnalysisContext} to construct the list of | |
30 | * {@link NamedExpression}. | |
31 | */ | |
32 | @RequiredArgsConstructor | |
33 | public class SelectExpressionAnalyzer | |
34 | extends | |
35 | AbstractNodeVisitor<List<NamedExpression>, AnalysisContext> { | |
36 | private final ExpressionAnalyzer expressionAnalyzer; | |
37 | ||
38 | private ExpressionReferenceOptimizer optimizer; | |
39 | ||
40 | /** | |
41 | * Analyze Select fields. | |
42 | */ | |
43 | public List<NamedExpression> analyze(List<UnresolvedExpression> selectList, | |
44 | AnalysisContext analysisContext, | |
45 | ExpressionReferenceOptimizer optimizer) { | |
46 | this.optimizer = optimizer; | |
47 | ImmutableList.Builder<NamedExpression> builder = new ImmutableList.Builder<>(); | |
48 | for (UnresolvedExpression unresolvedExpression : selectList) { | |
49 | builder.addAll(unresolvedExpression.accept(this, analysisContext)); | |
50 | } | |
51 |
1
1. analyze : replaced return value with Collections.emptyList for org/opensearch/sql/analysis/SelectExpressionAnalyzer::analyze → KILLED |
return builder.build(); |
52 | } | |
53 | ||
54 | @Override | |
55 | public List<NamedExpression> visitField(Field node, AnalysisContext context) { | |
56 |
1
1. visitField : replaced return value with Collections.emptyList for org/opensearch/sql/analysis/SelectExpressionAnalyzer::visitField → KILLED |
return Collections.singletonList(DSL.named(node.accept(expressionAnalyzer, context))); |
57 | } | |
58 | ||
59 | @Override | |
60 | public List<NamedExpression> visitAlias(Alias node, AnalysisContext context) { | |
61 | Expression expr = referenceIfSymbolDefined(node, context); | |
62 |
1
1. visitAlias : replaced return value with Collections.emptyList for org/opensearch/sql/analysis/SelectExpressionAnalyzer::visitAlias → KILLED |
return Collections.singletonList(DSL.named( |
63 | unqualifiedNameIfFieldOnly(node, context), | |
64 | expr, | |
65 | node.getAlias())); | |
66 | } | |
67 | ||
68 | /** | |
69 | * The Alias could be | |
70 | * 1. SELECT name, AVG(age) FROM s BY name -> | |
71 | * Project(Alias("name", expr), Alias("AVG(age)", aggExpr)) | |
72 | * Agg(Alias("AVG(age)", aggExpr)) | |
73 | * 2. SELECT length(name), AVG(age) FROM s BY length(name) | |
74 | * Project(Alias("name", expr), Alias("AVG(age)", aggExpr)) | |
75 | * Agg(Alias("AVG(age)", aggExpr)) | |
76 | * 3. SELECT length(name) as l, AVG(age) FROM s BY l | |
77 | * Project(Alias("name", expr, l), Alias("AVG(age)", aggExpr)) | |
78 | * Agg(Alias("AVG(age)", aggExpr), Alias("length(name)", groupExpr)) | |
79 | */ | |
80 | private Expression referenceIfSymbolDefined(Alias expr, | |
81 | AnalysisContext context) { | |
82 | UnresolvedExpression delegatedExpr = expr.getDelegated(); | |
83 | ||
84 | // Pass named expression because expression like window function loses full name | |
85 | // (OVER clause) and thus depends on name in alias to be replaced correctly | |
86 |
1
1. referenceIfSymbolDefined : replaced return value with null for org/opensearch/sql/analysis/SelectExpressionAnalyzer::referenceIfSymbolDefined → KILLED |
return optimizer.optimize( |
87 | DSL.named( | |
88 | expr.getName(), | |
89 | delegatedExpr.accept(expressionAnalyzer, context), | |
90 | expr.getAlias()), | |
91 | context); | |
92 | } | |
93 | ||
94 | @Override | |
95 | public List<NamedExpression> visitAllFields(AllFields node, | |
96 | AnalysisContext context) { | |
97 | TypeEnvironment environment = context.peek(); | |
98 | Map<String, ExprType> lookupAllFields = environment.lookupAllFields(Namespace.FIELD_NAME); | |
99 |
2
1. lambda$visitAllFields$0 : replaced return value with null for org/opensearch/sql/analysis/SelectExpressionAnalyzer::lambda$visitAllFields$0 → KILLED 2. visitAllFields : replaced return value with Collections.emptyList for org/opensearch/sql/analysis/SelectExpressionAnalyzer::visitAllFields → KILLED |
return lookupAllFields.entrySet().stream().map(entry -> DSL.named(entry.getKey(), |
100 | new ReferenceExpression(entry.getKey(), entry.getValue()))).collect(Collectors.toList()); | |
101 | } | |
102 | ||
103 | /** | |
104 | * Get unqualified name if select item is just a field. For example, suppose an index | |
105 | * named "accounts", return "age" for "SELECT accounts.age". But do nothing for expression | |
106 | * in "SELECT ABS(accounts.age)". | |
107 | * Note that an assumption is made implicitly that original name field in Alias must be | |
108 | * the same as the values in QualifiedName. This is true because AST builder does this. | |
109 | * Otherwise, what unqualified() returns will override Alias's name as NamedExpression's name | |
110 | * even though the QualifiedName doesn't have qualifier. | |
111 | */ | |
112 | private String unqualifiedNameIfFieldOnly(Alias node, AnalysisContext context) { | |
113 | UnresolvedExpression selectItem = node.getDelegated(); | |
114 |
1
1. unqualifiedNameIfFieldOnly : negated conditional → KILLED |
if (selectItem instanceof QualifiedName) { |
115 | QualifierAnalyzer qualifierAnalyzer = new QualifierAnalyzer(context); | |
116 |
1
1. unqualifiedNameIfFieldOnly : replaced return value with "" for org/opensearch/sql/analysis/SelectExpressionAnalyzer::unqualifiedNameIfFieldOnly → KILLED |
return qualifierAnalyzer.unqualified((QualifiedName) selectItem); |
117 | } | |
118 |
1
1. unqualifiedNameIfFieldOnly : replaced return value with "" for org/opensearch/sql/analysis/SelectExpressionAnalyzer::unqualifiedNameIfFieldOnly → KILLED |
return node.getName(); |
119 | } | |
120 | ||
121 | } | |
Mutations | ||
51 |
1.1 |
|
56 |
1.1 |
|
62 |
1.1 |
|
86 |
1.1 |
|
99 |
1.1 2.2 |
|
114 |
1.1 |
|
116 |
1.1 |
|
118 |
1.1 |