Skip to content

Commit e42bcd4

Browse files
Resolve merge conflicts and fix tests.
Signed-off-by: Yury-Fridlyand <[email protected]>
1 parent 9a1a17c commit e42bcd4

File tree

156 files changed

+6756
-831
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

156 files changed

+6756
-831
lines changed

common/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ dependencies {
3636
api group: 'com.google.guava', name: 'guava', version: '31.0.1-jre'
3737
api group: 'org.apache.logging.log4j', name: 'log4j-core', version:'2.17.1'
3838
api group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
39-
api 'com.amazonaws:aws-encryption-sdk-java:2.4.0'
4039

4140
testImplementation group: 'junit', name: 'junit', version: '4.13.2'
4241
testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.9.1'

config/checkstyle/google_checks.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@
3939
<property name="max" value="100"/>
4040
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
4141
</module>
42-
42+
<module name="SuppressWarningsFilter" />
4343
<module name="TreeWalker">
44+
<module name="SuppressWarningsHolder" />
4445
<module name="OuterTypeFilename"/>
4546
<module name="IllegalTokenText">
4647
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>

core/src/main/java/org/opensearch/sql/analysis/Analyzer.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.opensearch.sql.ast.tree.Values;
6464
import org.opensearch.sql.data.model.ExprMissingValue;
6565
import org.opensearch.sql.data.type.ExprCoreType;
66+
import org.opensearch.sql.data.type.ExprType;
6667
import org.opensearch.sql.datasource.DataSourceService;
6768
import org.opensearch.sql.exception.SemanticCheckException;
6869
import org.opensearch.sql.expression.DSL;
@@ -152,6 +153,9 @@ public LogicalPlan visitRelation(Relation node, AnalysisContext context) {
152153
dataSourceSchemaIdentifierNameResolver.getIdentifierName());
153154
}
154155
table.getFieldTypes().forEach((k, v) -> curEnv.define(new Symbol(Namespace.FIELD_NAME, k), v));
156+
table.getReservedFieldTypes().forEach(
157+
(k, v) -> curEnv.addReservedWord(new Symbol(Namespace.FIELD_NAME, k), v)
158+
);
155159

156160
// Put index name or its alias in index namespace on type environment so qualifier
157161
// can be removed when analyzing qualified name. The value (expr type) here doesn't matter.
@@ -195,6 +199,9 @@ public LogicalPlan visitTableFunction(TableFunction node, AnalysisContext contex
195199
TypeEnvironment curEnv = context.peek();
196200
Table table = tableFunctionImplementation.applyArguments();
197201
table.getFieldTypes().forEach((k, v) -> curEnv.define(new Symbol(Namespace.FIELD_NAME, k), v));
202+
table.getReservedFieldTypes().forEach(
203+
(k, v) -> curEnv.addReservedWord(new Symbol(Namespace.FIELD_NAME, k), v)
204+
);
198205
curEnv.define(new Symbol(Namespace.INDEX_NAME,
199206
dataSourceSchemaIdentifierNameResolver.getIdentifierName()), STRUCT);
200207
return new LogicalRelation(dataSourceSchemaIdentifierNameResolver.getIdentifierName(),
@@ -361,6 +368,14 @@ public LogicalPlan visitProject(Project node, AnalysisContext context) {
361368
List<NamedExpression> namedExpressions =
362369
selectExpressionAnalyzer.analyze(node.getProjectList(), context,
363370
new ExpressionReferenceOptimizer(expressionAnalyzer.getRepository(), child));
371+
372+
for (UnresolvedExpression expr : node.getProjectList()) {
373+
NestedAnalyzer nestedAnalyzer = new NestedAnalyzer(
374+
namedExpressions, expressionAnalyzer, child
375+
);
376+
child = nestedAnalyzer.analyze(expr, context);
377+
}
378+
364379
// new context
365380
context.push();
366381
TypeEnvironment newEnv = context.peek();

core/src/main/java/org/opensearch/sql/analysis/ExpressionAnalyzer.java

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
import static org.opensearch.sql.ast.dsl.AstDSL.and;
1010
import static org.opensearch.sql.ast.dsl.AstDSL.compare;
11-
import static org.opensearch.sql.expression.function.BuiltinFunctionName.GTE;
12-
import static org.opensearch.sql.expression.function.BuiltinFunctionName.LTE;
1311

1412
import com.google.common.collect.ImmutableList;
1513
import com.google.common.collect.ImmutableMap;
@@ -31,6 +29,7 @@
3129
import org.opensearch.sql.ast.expression.Case;
3230
import org.opensearch.sql.ast.expression.Cast;
3331
import org.opensearch.sql.ast.expression.Compare;
32+
import org.opensearch.sql.ast.expression.DataType;
3433
import org.opensearch.sql.ast.expression.EqualTo;
3534
import org.opensearch.sql.ast.expression.Field;
3635
import org.opensearch.sql.ast.expression.Function;
@@ -42,6 +41,7 @@
4241
import org.opensearch.sql.ast.expression.Or;
4342
import org.opensearch.sql.ast.expression.QualifiedName;
4443
import org.opensearch.sql.ast.expression.RelevanceFieldList;
44+
import org.opensearch.sql.ast.expression.ScoreFunction;
4545
import org.opensearch.sql.ast.expression.Span;
4646
import org.opensearch.sql.ast.expression.UnresolvedArgument;
4747
import org.opensearch.sql.ast.expression.UnresolvedAttribute;
@@ -51,6 +51,7 @@
5151
import org.opensearch.sql.ast.expression.Xor;
5252
import org.opensearch.sql.common.antlr.SyntaxCheckException;
5353
import org.opensearch.sql.data.model.ExprValueUtils;
54+
import org.opensearch.sql.data.type.ExprCoreType;
5455
import org.opensearch.sql.data.type.ExprType;
5556
import org.opensearch.sql.exception.SemanticCheckException;
5657
import org.opensearch.sql.expression.DSL;
@@ -67,6 +68,7 @@
6768
import org.opensearch.sql.expression.function.BuiltinFunctionName;
6869
import org.opensearch.sql.expression.function.BuiltinFunctionRepository;
6970
import org.opensearch.sql.expression.function.FunctionName;
71+
import org.opensearch.sql.expression.function.OpenSearchFunctions;
7072
import org.opensearch.sql.expression.parse.ParseExpression;
7173
import org.opensearch.sql.expression.span.SpanExpression;
7274
import org.opensearch.sql.expression.window.aggregation.AggregateWindowFunction;
@@ -207,6 +209,65 @@ public Expression visitHighlightFunction(HighlightFunction node, AnalysisContext
207209
return new HighlightExpression(expr);
208210
}
209211

212+
/**
213+
* visitScoreFunction removes the score function from the AST and replaces it with the child
214+
* relevance function node. If the optional boost variable is provided, the boost argument
215+
* of the relevance function is combined.
216+
*
217+
* @param node score function node
218+
* @param context analysis context for the query
219+
* @return resolved relevance function
220+
*/
221+
public Expression visitScoreFunction(ScoreFunction node, AnalysisContext context) {
222+
Literal boostArg = node.getRelevanceFieldWeight();
223+
if (!boostArg.getType().equals(DataType.DOUBLE)) {
224+
throw new SemanticCheckException(String.format("Expected boost type '%s' but got '%s'",
225+
DataType.DOUBLE.name(), boostArg.getType().name()));
226+
}
227+
Double thisBoostValue = ((Double) boostArg.getValue());
228+
229+
// update the existing unresolved expression to add a boost argument if it doesn't exist
230+
// OR multiply the existing boost argument
231+
Function relevanceQueryUnresolvedExpr = (Function) node.getRelevanceQuery();
232+
List<UnresolvedExpression> relevanceFuncArgs = relevanceQueryUnresolvedExpr.getFuncArgs();
233+
234+
boolean doesFunctionContainBoostArgument = false;
235+
List<UnresolvedExpression> updatedFuncArgs = new ArrayList<>();
236+
for (UnresolvedExpression expr : relevanceFuncArgs) {
237+
String argumentName = ((UnresolvedArgument) expr).getArgName();
238+
if (argumentName.equalsIgnoreCase("boost")) {
239+
doesFunctionContainBoostArgument = true;
240+
Literal boostArgLiteral = (Literal) ((UnresolvedArgument) expr).getValue();
241+
Double boostValue =
242+
Double.parseDouble((String) boostArgLiteral.getValue()) * thisBoostValue;
243+
UnresolvedArgument newBoostArg = new UnresolvedArgument(
244+
argumentName,
245+
new Literal(boostValue.toString(), DataType.STRING)
246+
);
247+
updatedFuncArgs.add(newBoostArg);
248+
} else {
249+
updatedFuncArgs.add(expr);
250+
}
251+
}
252+
253+
// since nothing was found, add an argument
254+
if (!doesFunctionContainBoostArgument) {
255+
UnresolvedArgument newBoostArg = new UnresolvedArgument(
256+
"boost", new Literal(Double.toString(thisBoostValue), DataType.STRING));
257+
updatedFuncArgs.add(newBoostArg);
258+
}
259+
260+
// create a new function expression with boost argument and resolve it
261+
Function updatedRelevanceQueryUnresolvedExpr = new Function(
262+
relevanceQueryUnresolvedExpr.getFuncName(),
263+
updatedFuncArgs);
264+
OpenSearchFunctions.OpenSearchFunction relevanceQueryExpr =
265+
(OpenSearchFunctions.OpenSearchFunction) updatedRelevanceQueryUnresolvedExpr
266+
.accept(this, context);
267+
relevanceQueryExpr.setScoreTracked(true);
268+
return relevanceQueryExpr;
269+
}
270+
210271
@Override
211272
public Expression visitIn(In node, AnalysisContext context) {
212273
return visitIn(node.getField(), node.getValueList(), context);
@@ -297,6 +358,23 @@ public Expression visitAllFields(AllFields node, AnalysisContext context) {
297358
@Override
298359
public Expression visitQualifiedName(QualifiedName node, AnalysisContext context) {
299360
QualifierAnalyzer qualifierAnalyzer = new QualifierAnalyzer(context);
361+
362+
// check for reserved words in the identifier
363+
for (String part : node.getParts()) {
364+
for (TypeEnvironment typeEnv = context.peek();
365+
typeEnv != null;
366+
typeEnv = typeEnv.getParent()) {
367+
Optional<ExprType> exprType = typeEnv.getReservedSymbolTable().lookup(
368+
new Symbol(Namespace.FIELD_NAME, part));
369+
if (exprType.isPresent()) {
370+
return visitMetadata(
371+
qualifierAnalyzer.unqualified(node),
372+
(ExprCoreType) exprType.get(),
373+
context
374+
);
375+
}
376+
}
377+
}
300378
return visitIdentifier(qualifierAnalyzer.unqualified(node), context);
301379
}
302380

@@ -313,6 +391,19 @@ public Expression visitUnresolvedArgument(UnresolvedArgument node, AnalysisConte
313391
return new NamedArgumentExpression(node.getArgName(), node.getValue().accept(this, context));
314392
}
315393

394+
/**
395+
* If QualifiedName is actually a reserved metadata field, return the expr type associated
396+
* with the metadata field.
397+
* @param ident metadata field name
398+
* @param context analysis context
399+
* @return DSL reference
400+
*/
401+
private Expression visitMetadata(String ident,
402+
ExprCoreType exprCoreType,
403+
AnalysisContext context) {
404+
return DSL.ref(ident, exprCoreType);
405+
}
406+
316407
private Expression visitIdentifier(String ident, AnalysisContext context) {
317408
// ParseExpression will always override ReferenceExpression when ident conflicts
318409
for (NamedExpression expr : context.getNamedParseExpressions()) {
@@ -325,17 +416,6 @@ private Expression visitIdentifier(String ident, AnalysisContext context) {
325416
ReferenceExpression ref = DSL.ref(ident,
326417
typeEnv.resolve(new Symbol(Namespace.FIELD_NAME, ident)));
327418

328-
// Fall back to old engine too if type is not supported semantically
329-
if (isTypeNotSupported(ref.type())) {
330-
throw new SyntaxCheckException(String.format(
331-
"Identifier [%s] of type [%s] is not supported yet", ident, ref.type()));
332-
}
333419
return ref;
334420
}
335-
336-
// Array type is not supporte yet.
337-
private boolean isTypeNotSupported(ExprType type) {
338-
return "array".equalsIgnoreCase(type.typeName());
339-
}
340-
341421
}

core/src/main/java/org/opensearch/sql/analysis/ExpressionReferenceOptimizer.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.opensearch.sql.expression.conditional.cases.CaseClause;
2020
import org.opensearch.sql.expression.conditional.cases.WhenClause;
2121
import org.opensearch.sql.expression.function.BuiltinFunctionRepository;
22+
import org.opensearch.sql.expression.function.OpenSearchFunctions;
2223
import org.opensearch.sql.planner.logical.LogicalAggregation;
2324
import org.opensearch.sql.planner.logical.LogicalPlan;
2425
import org.opensearch.sql.planner.logical.LogicalPlanNodeVisitor;
@@ -70,8 +71,17 @@ public Expression visitFunction(FunctionExpression node, AnalysisContext context
7071
final List<Expression> args =
7172
node.getArguments().stream().map(expr -> expr.accept(this, context))
7273
.collect(Collectors.toList());
73-
return (Expression) repository.compile(context.getFunctionProperties(),
74-
node.getFunctionName(), args);
74+
Expression optimizedFunctionExpression = (Expression) repository.compile(
75+
context.getFunctionProperties(),
76+
node.getFunctionName(),
77+
args
78+
);
79+
// Propagate scoreTracked for OpenSearch functions
80+
if (optimizedFunctionExpression instanceof OpenSearchFunctions.OpenSearchFunction) {
81+
((OpenSearchFunctions.OpenSearchFunction) optimizedFunctionExpression).setScoreTracked(
82+
((OpenSearchFunctions.OpenSearchFunction)node).isScoreTracked());
83+
}
84+
return optimizedFunctionExpression;
7585
}
7686
}
7787

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.analysis;
7+
8+
import static org.opensearch.sql.data.type.ExprCoreType.STRING;
9+
10+
import java.util.ArrayList;
11+
import java.util.Arrays;
12+
import java.util.List;
13+
import java.util.Map;
14+
import lombok.RequiredArgsConstructor;
15+
import org.opensearch.sql.ast.AbstractNodeVisitor;
16+
import org.opensearch.sql.ast.expression.Alias;
17+
import org.opensearch.sql.ast.expression.Function;
18+
import org.opensearch.sql.ast.expression.QualifiedName;
19+
import org.opensearch.sql.ast.expression.UnresolvedExpression;
20+
import org.opensearch.sql.expression.NamedExpression;
21+
import org.opensearch.sql.expression.ReferenceExpression;
22+
import org.opensearch.sql.expression.function.BuiltinFunctionName;
23+
import org.opensearch.sql.planner.logical.LogicalNested;
24+
import org.opensearch.sql.planner.logical.LogicalPlan;
25+
26+
/**
27+
* Analyze the Nested Function in the {@link AnalysisContext} to construct the {@link
28+
* LogicalPlan}.
29+
*/
30+
@RequiredArgsConstructor
31+
public class NestedAnalyzer extends AbstractNodeVisitor<LogicalPlan, AnalysisContext> {
32+
private final List<NamedExpression> namedExpressions;
33+
private final ExpressionAnalyzer expressionAnalyzer;
34+
private final LogicalPlan child;
35+
36+
public LogicalPlan analyze(UnresolvedExpression projectItem, AnalysisContext context) {
37+
LogicalPlan nested = projectItem.accept(this, context);
38+
return (nested == null) ? child : nested;
39+
}
40+
41+
@Override
42+
public LogicalPlan visitAlias(Alias node, AnalysisContext context) {
43+
return node.getDelegated().accept(this, context);
44+
}
45+
46+
@Override
47+
public LogicalPlan visitFunction(Function node, AnalysisContext context) {
48+
if (node.getFuncName().equalsIgnoreCase(BuiltinFunctionName.NESTED.name())) {
49+
50+
List<UnresolvedExpression> expressions = node.getFuncArgs();
51+
validateArgs(expressions);
52+
ReferenceExpression nestedField =
53+
(ReferenceExpression)expressionAnalyzer.analyze(expressions.get(0), context);
54+
Map<String, ReferenceExpression> args;
55+
if (expressions.size() == 2) {
56+
args = Map.of(
57+
"field", nestedField,
58+
"path", (ReferenceExpression)expressionAnalyzer.analyze(expressions.get(1), context)
59+
);
60+
} else {
61+
args = Map.of(
62+
"field", (ReferenceExpression)expressionAnalyzer.analyze(expressions.get(0), context),
63+
"path", generatePath(nestedField.toString())
64+
);
65+
}
66+
if (child instanceof LogicalNested) {
67+
((LogicalNested)child).addFields(args);
68+
return child;
69+
} else {
70+
return new LogicalNested(child, new ArrayList<>(Arrays.asList(args)), namedExpressions);
71+
}
72+
}
73+
return null;
74+
}
75+
76+
/**
77+
* Validate each parameter used in nested function in SELECT clause. Any supplied parameter
78+
* for a nested function in a SELECT statement must be a valid qualified name, and the field
79+
* parameter must be nested at least one level.
80+
* @param args : Arguments in nested function.
81+
*/
82+
private void validateArgs(List<UnresolvedExpression> args) {
83+
if (args.size() < 1 || args.size() > 2) {
84+
throw new IllegalArgumentException(
85+
"on nested object only allowed 2 parameters (field,path) or 1 parameter (field)"
86+
);
87+
}
88+
89+
for (int i = 0; i < args.size(); i++) {
90+
if (!(args.get(i) instanceof QualifiedName)) {
91+
throw new IllegalArgumentException(
92+
String.format("Illegal nested field name: %s", args.get(i).toString())
93+
);
94+
}
95+
if (i == 0 && ((QualifiedName)args.get(i)).getParts().size() < 2) {
96+
throw new IllegalArgumentException(
97+
String.format("Illegal nested field name: %s", args.get(i).toString())
98+
);
99+
}
100+
}
101+
}
102+
103+
/**
104+
* Generate nested path dynamically. Assumes at least one level of nesting in supplied string.
105+
* @param field : Nested field to generate path of.
106+
* @return : Path of field derived from last level of nesting.
107+
*/
108+
private ReferenceExpression generatePath(String field) {
109+
return new ReferenceExpression(field.substring(0, field.lastIndexOf(".")), STRING);
110+
}
111+
}

0 commit comments

Comments
 (0)