Skip to content

Commit a6ccb5a

Browse files
author
Srikanth Padakanti
committed
Merge upstream changes to my main
Signed-off-by: Srikanth Padakanti <[email protected]>
2 parents c215243 + dd52196 commit a6ccb5a

File tree

144 files changed

+7589
-2243
lines changed

Some content is hidden

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

144 files changed

+7589
-2243
lines changed

.github/workflows/enforce-labels.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ jobs:
99
steps:
1010
- uses: yogevbd/[email protected]
1111
with:
12-
REQUIRED_LABELS_ANY: "breaking,feature,enhancement,bug,infrastructure,dependencies,documentation,maintenance,skip-changelog,testing,security fix"
13-
REQUIRED_LABELS_ANY_DESCRIPTION: "A release label is required: ['breaking', 'bug', 'dependencies', 'documentation', 'enhancement', 'feature', 'infrastructure', 'maintenance', 'skip-changelog', 'testing', 'security fix']"
12+
REQUIRED_LABELS_ANY: "breaking,feature,enhancement,bugFix,infrastructure,dependencies,documentation,maintenance,skip-changelog,testing,security fix"
13+
REQUIRED_LABELS_ANY_DESCRIPTION: "A release label is required: ['breaking', 'bugFix', 'dependencies', 'documentation', 'enhancement', 'feature', 'infrastructure', 'maintenance', 'skip-changelog', 'testing', 'security fix']"

.github/workflows/integ-tests-with-security.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
strategy:
6363
fail-fast: false
6464
matrix:
65-
os: [ windows-latest, macos-13 ]
65+
os: [ windows-latest, macos-14 ]
6666
java: [21, 24]
6767

6868
runs-on: ${{ matrix.os }}

.github/workflows/sql-test-and-build-workflow.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ jobs:
107107
entry:
108108
- { os: windows-latest, java: 21, os_build_args: -PbuildPlatform=windows }
109109
- { os: windows-latest, java: 24, os_build_args: -PbuildPlatform=windows }
110-
- { os: macos-13, java: 21, os_build_args: '' }
111-
- { os: macos-13, java: 24, os_build_args: '' }
110+
- { os: macos-14, java: 21, os_build_args: '' }
111+
- { os: macos-14, java: 24, os_build_args: '' }
112112
test-type: ['unit', 'integration', 'doc']
113113
exclude:
114114
# Exclude doctest for Windows

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import org.opensearch.sql.ast.tree.Aggregation;
6161
import org.opensearch.sql.ast.tree.Append;
6262
import org.opensearch.sql.ast.tree.AppendCol;
63+
import org.opensearch.sql.ast.tree.AppendPipe;
6364
import org.opensearch.sql.ast.tree.Bin;
6465
import org.opensearch.sql.ast.tree.Chart;
6566
import org.opensearch.sql.ast.tree.CloseCursor;
@@ -97,7 +98,6 @@
9798
import org.opensearch.sql.ast.tree.StreamWindow;
9899
import org.opensearch.sql.ast.tree.SubqueryAlias;
99100
import org.opensearch.sql.ast.tree.TableFunction;
100-
import org.opensearch.sql.ast.tree.Timechart;
101101
import org.opensearch.sql.ast.tree.Trendline;
102102
import org.opensearch.sql.ast.tree.UnresolvedPlan;
103103
import org.opensearch.sql.ast.tree.Values;
@@ -781,11 +781,6 @@ public LogicalPlan visitChart(Chart node, AnalysisContext context) {
781781
throw getOnlyForCalciteException("Chart");
782782
}
783783

784-
@Override
785-
public LogicalPlan visitTimechart(Timechart node, AnalysisContext context) {
786-
throw getOnlyForCalciteException("Timechart");
787-
}
788-
789784
@Override
790785
public LogicalPlan visitWindow(Window node, AnalysisContext context) {
791786
throw getOnlyForCalciteException("Window");
@@ -839,6 +834,11 @@ public LogicalPlan visitAppendCol(AppendCol node, AnalysisContext context) {
839834
throw getOnlyForCalciteException("Appendcol");
840835
}
841836

837+
@Override
838+
public LogicalPlan visitAppendPipe(AppendPipe node, AnalysisContext context) {
839+
throw getOnlyForCalciteException("AppendPipe");
840+
}
841+
842842
@Override
843843
public LogicalPlan visitAppend(Append node, AnalysisContext context) {
844844
throw getOnlyForCalciteException("Append");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ private boolean isCalciteOnlyFunction(FunctionName functionName) {
233233
// Set of functions that are only supported with Calcite engine
234234
Set<String> calciteOnlyFunctions =
235235
ImmutableSet.of(
236-
BuiltinFunctionName.REGEX_MATCH.getName().getFunctionName(),
236+
BuiltinFunctionName.REGEXP_MATCH.getName().getFunctionName(),
237237
BuiltinFunctionName.STRFTIME.getName().getFunctionName());
238238

239239
return calciteOnlyFunctions.stream()

core/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.opensearch.sql.ast.tree.Aggregation;
4949
import org.opensearch.sql.ast.tree.Append;
5050
import org.opensearch.sql.ast.tree.AppendCol;
51+
import org.opensearch.sql.ast.tree.AppendPipe;
5152
import org.opensearch.sql.ast.tree.Bin;
5253
import org.opensearch.sql.ast.tree.Chart;
5354
import org.opensearch.sql.ast.tree.CloseCursor;
@@ -84,7 +85,6 @@
8485
import org.opensearch.sql.ast.tree.StreamWindow;
8586
import org.opensearch.sql.ast.tree.SubqueryAlias;
8687
import org.opensearch.sql.ast.tree.TableFunction;
87-
import org.opensearch.sql.ast.tree.Timechart;
8888
import org.opensearch.sql.ast.tree.Trendline;
8989
import org.opensearch.sql.ast.tree.Values;
9090
import org.opensearch.sql.ast.tree.Window;
@@ -141,6 +141,10 @@ public T visitSearch(Search node, C context) {
141141
return visitChildren(node, context);
142142
}
143143

144+
public T visitAppendPipe(AppendPipe node, C context) {
145+
return visitChildren(node, context);
146+
}
147+
144148
public T visitFilter(Filter node, C context) {
145149
return visitChildren(node, context);
146150
}
@@ -281,10 +285,6 @@ public T visitChart(Chart node, C context) {
281285
return visitChildren(node, context);
282286
}
283287

284-
public T visitTimechart(Timechart node, C context) {
285-
return visitChildren(node, context);
286-
}
287-
288288
public T visitRegex(Regex node, C context) {
289289
return visitChildren(node, context);
290290
}

core/src/main/java/org/opensearch/sql/ast/dsl/AstDSL.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.opensearch.sql.ast.expression.WindowFunction;
5050
import org.opensearch.sql.ast.expression.Xor;
5151
import org.opensearch.sql.ast.tree.Aggregation;
52+
import org.opensearch.sql.ast.tree.AppendPipe;
5253
import org.opensearch.sql.ast.tree.Bin;
5354
import org.opensearch.sql.ast.tree.CountBin;
5455
import org.opensearch.sql.ast.tree.Dedupe;
@@ -569,6 +570,11 @@ public static Trendline trendline(
569570
return new Trendline(sortField, Arrays.asList(computations)).attach(input);
570571
}
571572

573+
public static AppendPipe appendPipe(UnresolvedPlan input, UnresolvedPlan subquery) {
574+
575+
return new AppendPipe(subquery).attach(input);
576+
}
577+
572578
public static Trendline.TrendlineComputation computation(
573579
Integer numDataPoints, Field dataField, String alias, Trendline.TrendlineType type) {
574580
return new Trendline.TrendlineComputation(numDataPoints, dataField, alias, type);
@@ -734,7 +740,7 @@ public static Bin bin(UnresolvedExpression field, Argument... arguments) {
734740
}
735741

736742
/** Get a reference to the implicit timestamp field {@code @timestamp} */
737-
public static Field referImplicitTimestampField() {
743+
public static Field implicitTimestampField() {
738744
return AstDSL.field(OpenSearchConstants.IMPLICIT_FIELD_TIMESTAMP);
739745
}
740746
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.tree;
7+
8+
import com.google.common.collect.ImmutableList;
9+
import java.util.List;
10+
import lombok.EqualsAndHashCode;
11+
import lombok.Getter;
12+
import lombok.Setter;
13+
import lombok.ToString;
14+
import org.opensearch.sql.ast.AbstractNodeVisitor;
15+
16+
@Getter
17+
@Setter
18+
@ToString
19+
@EqualsAndHashCode(callSuper = false)
20+
public class AppendPipe extends UnresolvedPlan {
21+
22+
private UnresolvedPlan subQuery;
23+
24+
private UnresolvedPlan child;
25+
26+
public AppendPipe(UnresolvedPlan subQuery) {
27+
this.subQuery = subQuery;
28+
}
29+
30+
@Override
31+
public AppendPipe attach(UnresolvedPlan child) {
32+
this.child = child;
33+
return this;
34+
}
35+
36+
@Override
37+
public List<UnresolvedPlan> getChild() {
38+
return this.child == null ? ImmutableList.of() : ImmutableList.of(this.child);
39+
}
40+
41+
@Override
42+
public <T, C> T accept(AbstractNodeVisitor<T, C> nodeVisitor, C context) {
43+
return nodeVisitor.visitAppendPipe(this, context);
44+
}
45+
}

core/src/main/java/org/opensearch/sql/ast/tree/Chart.java

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,44 @@
55

66
package org.opensearch.sql.ast.tree;
77

8+
import static org.opensearch.sql.ast.dsl.AstDSL.aggregate;
9+
import static org.opensearch.sql.ast.dsl.AstDSL.doubleLiteral;
10+
import static org.opensearch.sql.ast.dsl.AstDSL.eval;
11+
import static org.opensearch.sql.ast.dsl.AstDSL.function;
12+
import static org.opensearch.sql.ast.dsl.AstDSL.stringLiteral;
13+
import static org.opensearch.sql.ast.expression.IntervalUnit.MILLISECOND;
14+
import static org.opensearch.sql.ast.tree.Chart.PerFunctionRateExprBuilder.timestampadd;
15+
import static org.opensearch.sql.ast.tree.Chart.PerFunctionRateExprBuilder.timestampdiff;
16+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.DIVIDE;
17+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MULTIPLY;
18+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.SUM;
19+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.TIMESTAMPADD;
20+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.TIMESTAMPDIFF;
21+
822
import com.google.common.collect.ImmutableList;
923
import java.util.List;
24+
import java.util.Locale;
25+
import java.util.Map;
26+
import java.util.Optional;
1027
import lombok.AllArgsConstructor;
1128
import lombok.EqualsAndHashCode;
1229
import lombok.Getter;
30+
import lombok.RequiredArgsConstructor;
1331
import lombok.ToString;
1432
import org.opensearch.sql.ast.AbstractNodeVisitor;
1533
import org.opensearch.sql.ast.dsl.AstDSL;
34+
import org.opensearch.sql.ast.expression.AggregateFunction;
35+
import org.opensearch.sql.ast.expression.Alias;
1636
import org.opensearch.sql.ast.expression.Argument;
37+
import org.opensearch.sql.ast.expression.Field;
38+
import org.opensearch.sql.ast.expression.Function;
39+
import org.opensearch.sql.ast.expression.IntervalUnit;
40+
import org.opensearch.sql.ast.expression.Let;
1741
import org.opensearch.sql.ast.expression.Literal;
42+
import org.opensearch.sql.ast.expression.Span;
43+
import org.opensearch.sql.ast.expression.SpanUnit;
1844
import org.opensearch.sql.ast.expression.UnresolvedExpression;
45+
import org.opensearch.sql.calcite.utils.PlanUtils;
1946

2047
/** AST node represent chart command. */
2148
@Getter
@@ -39,8 +66,8 @@ public class Chart extends UnresolvedPlan {
3966

4067
@Override
4168
public UnresolvedPlan attach(UnresolvedPlan child) {
42-
this.child = child;
43-
return this;
69+
// Transform after child attached to avoid unintentionally overriding it
70+
return toBuilder().child(child).build().transformPerFunction();
4471
}
4572

4673
@Override
@@ -52,4 +79,131 @@ public List<UnresolvedPlan> getChild() {
5279
public <T, C> T accept(AbstractNodeVisitor<T, C> nodeVisitor, C context) {
5380
return nodeVisitor.visitChart(this, context);
5481
}
82+
83+
/**
84+
* Transform per function to eval-based post-processing on sum result by chart. Specifically,
85+
* calculate how many seconds are in the time bucket based on the span option dynamically, then
86+
* divide the aggregated sum value by the number of seconds to get the per-second rate.
87+
*
88+
* <p>For example, with span=5m per_second(field): per second rate = sum(field) / 300 seconds
89+
*
90+
* @return eval+chart if per function present, or the original chart otherwise.
91+
*/
92+
private UnresolvedPlan transformPerFunction() {
93+
Optional<PerFunction> perFuncOpt = PerFunction.from(aggregationFunction);
94+
if (perFuncOpt.isEmpty()) {
95+
return this;
96+
}
97+
98+
PerFunction perFunc = perFuncOpt.get();
99+
// For chart, the rowSplit should contain the span information
100+
UnresolvedExpression spanExpr = rowSplit;
101+
if (rowSplit instanceof Alias) {
102+
spanExpr = ((Alias) rowSplit).getDelegated();
103+
}
104+
if (!(spanExpr instanceof Span)) {
105+
return this; // Cannot transform without span information
106+
}
107+
108+
Span span = (Span) spanExpr;
109+
Field spanStartTime = AstDSL.implicitTimestampField();
110+
Function spanEndTime = timestampadd(span.getUnit(), span.getValue(), spanStartTime);
111+
Function spanMillis = timestampdiff(MILLISECOND, spanStartTime, spanEndTime);
112+
final int SECOND_IN_MILLISECOND = 1000;
113+
return eval(
114+
chart(AstDSL.alias(perFunc.aggName, PerFunctionRateExprBuilder.sum(perFunc.aggArg))),
115+
let(perFunc.aggName)
116+
.multiply(perFunc.seconds * SECOND_IN_MILLISECOND)
117+
.dividedBy(spanMillis));
118+
}
119+
120+
private Chart chart(UnresolvedExpression newAggregationFunction) {
121+
return this.toBuilder().aggregationFunction(newAggregationFunction).build();
122+
}
123+
124+
@RequiredArgsConstructor
125+
static class PerFunction {
126+
private static final Map<String, Integer> UNIT_SECONDS =
127+
Map.of(
128+
"per_second", 1,
129+
"per_minute", 60,
130+
"per_hour", 3600,
131+
"per_day", 86400);
132+
private final String aggName;
133+
private final UnresolvedExpression aggArg;
134+
private final int seconds;
135+
136+
static Optional<PerFunction> from(UnresolvedExpression aggExpr) {
137+
if (aggExpr instanceof Alias) {
138+
return from(((Alias) aggExpr).getDelegated());
139+
}
140+
;
141+
if (!(aggExpr instanceof AggregateFunction)) {
142+
return Optional.empty();
143+
}
144+
145+
AggregateFunction aggFunc = (AggregateFunction) aggExpr;
146+
String aggFuncName = aggFunc.getFuncName().toLowerCase(Locale.ROOT);
147+
if (!UNIT_SECONDS.containsKey(aggFuncName)) {
148+
return Optional.empty();
149+
}
150+
151+
String aggName = toAggName(aggFunc);
152+
return Optional.of(
153+
new PerFunction(aggName, aggFunc.getField(), UNIT_SECONDS.get(aggFuncName)));
154+
}
155+
156+
private static String toAggName(AggregateFunction aggFunc) {
157+
String fieldName =
158+
(aggFunc.getField() instanceof Field)
159+
? ((Field) aggFunc.getField()).getField().toString()
160+
: aggFunc.getField().toString();
161+
return String.format(Locale.ROOT, "%s(%s)", aggFunc.getFuncName(), fieldName);
162+
}
163+
}
164+
165+
private PerFunctionRateExprBuilder let(String fieldName) {
166+
return new PerFunctionRateExprBuilder(AstDSL.field(fieldName));
167+
}
168+
169+
/** Fluent builder for creating Let expressions with mathematical operations. */
170+
static class PerFunctionRateExprBuilder {
171+
private final Field field;
172+
private UnresolvedExpression expr;
173+
174+
PerFunctionRateExprBuilder(Field field) {
175+
this.field = field;
176+
this.expr = field;
177+
}
178+
179+
PerFunctionRateExprBuilder multiply(Integer multiplier) {
180+
// Promote to double literal to avoid integer division in downstream
181+
this.expr =
182+
function(
183+
MULTIPLY.getName().getFunctionName(), expr, doubleLiteral(multiplier.doubleValue()));
184+
return this;
185+
}
186+
187+
Let dividedBy(UnresolvedExpression divisor) {
188+
return AstDSL.let(field, function(DIVIDE.getName().getFunctionName(), expr, divisor));
189+
}
190+
191+
static UnresolvedExpression sum(UnresolvedExpression field) {
192+
return aggregate(SUM.getName().getFunctionName(), field);
193+
}
194+
195+
static Function timestampadd(
196+
SpanUnit unit, UnresolvedExpression value, UnresolvedExpression timestampField) {
197+
UnresolvedExpression intervalUnit =
198+
stringLiteral(PlanUtils.spanUnitToIntervalUnit(unit).toString());
199+
return function(
200+
TIMESTAMPADD.getName().getFunctionName(), intervalUnit, value, timestampField);
201+
}
202+
203+
static Function timestampdiff(
204+
IntervalUnit unit, UnresolvedExpression start, UnresolvedExpression end) {
205+
return function(
206+
TIMESTAMPDIFF.getName().getFunctionName(), stringLiteral(unit.toString()), start, end);
207+
}
208+
}
55209
}

0 commit comments

Comments
 (0)