Skip to content

Commit 7f5dcf8

Browse files
committed
Merged opensearch-project#754 with dev-add-UTC-DateTime.
Signed-off-by: MitchellGale-BitQuill <[email protected]>
2 parents 3e3930c + 425a998 commit 7f5dcf8

File tree

26 files changed

+1559
-35
lines changed

26 files changed

+1559
-35
lines changed

common/src/main/java/org/opensearch/sql/common/utils/QueryContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66

77
package org.opensearch.sql.common.utils;
88

9+
import java.time.LocalDateTime;
910
import java.util.Map;
1011
import java.util.Optional;
1112
import java.util.UUID;
1213
import org.apache.logging.log4j.ThreadContext;
1314

1415
/**
1516
* Utility class for recording and accessing context for the query being executed.
17+
* Implementation Details: context variables is being persisted statically in the thread context
18+
* @see: @ThreadContext
1619
*/
1720
public class QueryContext {
1821

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
package org.opensearch.sql.analysis;
88

99
import java.util.ArrayList;
10+
import java.util.HashMap;
1011
import java.util.List;
12+
import java.util.Map;
1113
import java.util.Objects;
1214
import lombok.Getter;
15+
import org.opensearch.sql.expression.Expression;
1316
import org.opensearch.sql.expression.NamedExpression;
1417

1518
/**
@@ -23,13 +26,26 @@ public class AnalysisContext {
2326
@Getter
2427
private final List<NamedExpression> namedParseExpressions;
2528

29+
/**
30+
* Storage for values of functions which return a constant value.
31+
* We are storing the values there to use it in sequential calls to those functions.
32+
* For example, `now` function should the same value during processing a query.
33+
*/
34+
@Getter
35+
private final Map<String, Expression> constantFunctionValues;
36+
2637
public AnalysisContext() {
2738
this(new TypeEnvironment(null));
2839
}
2940

41+
/**
42+
* Class CTOR.
43+
* @param environment Env to set to a new instance.
44+
*/
3045
public AnalysisContext(TypeEnvironment environment) {
3146
this.environment = environment;
3247
this.namedParseExpressions = new ArrayList<>();
48+
this.constantFunctionValues = new HashMap<>();
3349
}
3450

3551
/**

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.opensearch.sql.ast.expression.Case;
2525
import org.opensearch.sql.ast.expression.Cast;
2626
import org.opensearch.sql.ast.expression.Compare;
27+
import org.opensearch.sql.ast.expression.ConstantFunction;
2728
import org.opensearch.sql.ast.expression.EqualTo;
2829
import org.opensearch.sql.ast.expression.Field;
2930
import org.opensearch.sql.ast.expression.Function;
@@ -169,6 +170,19 @@ public Expression visitRelevanceFieldList(RelevanceFieldList node, AnalysisConte
169170
ImmutableMap.copyOf(node.getFieldList())));
170171
}
171172

173+
@Override
174+
public Expression visitConstantFunction(ConstantFunction node, AnalysisContext context) {
175+
var valueName = node.getFuncName();
176+
if (context.getConstantFunctionValues().containsKey(valueName)) {
177+
return context.getConstantFunctionValues().get(valueName);
178+
}
179+
180+
var value = visitFunction(node, context);
181+
value = DSL.literal(value.valueOf(null));
182+
context.getConstantFunctionValues().put(valueName, value);
183+
return value;
184+
}
185+
172186
@Override
173187
public Expression visitFunction(Function node, AnalysisContext context) {
174188
FunctionName functionName = FunctionName.of(node.getFuncName());

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.opensearch.sql.ast.expression.Case;
1616
import org.opensearch.sql.ast.expression.Cast;
1717
import org.opensearch.sql.ast.expression.Compare;
18+
import org.opensearch.sql.ast.expression.ConstantFunction;
1819
import org.opensearch.sql.ast.expression.EqualTo;
1920
import org.opensearch.sql.ast.expression.Field;
2021
import org.opensearch.sql.ast.expression.Function;
@@ -116,6 +117,10 @@ public T visitRelevanceFieldList(RelevanceFieldList node, C context) {
116117
return visitChildren(node, context);
117118
}
118119

120+
public T visitConstantFunction(ConstantFunction node, C context) {
121+
return visitChildren(node, context);
122+
}
123+
119124
public T visitUnresolvedAttribute(UnresolvedAttribute node, C context) {
120125
return visitChildren(node, context);
121126
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.opensearch.sql.ast.expression.Case;
2020
import org.opensearch.sql.ast.expression.Cast;
2121
import org.opensearch.sql.ast.expression.Compare;
22+
import org.opensearch.sql.ast.expression.ConstantFunction;
2223
import org.opensearch.sql.ast.expression.DataType;
2324
import org.opensearch.sql.ast.expression.EqualTo;
2425
import org.opensearch.sql.ast.expression.Field;
@@ -234,6 +235,10 @@ public static Function function(String funcName, UnresolvedExpression... funcArg
234235
return new Function(funcName, Arrays.asList(funcArgs));
235236
}
236237

238+
public static Function constantFunction(String funcName, UnresolvedExpression... funcArgs) {
239+
return new ConstantFunction(funcName, Arrays.asList(funcArgs));
240+
}
241+
237242
/**
238243
* CASE
239244
* WHEN search_condition THEN result_expr
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
7+
package org.opensearch.sql.ast.expression;
8+
9+
import java.util.List;
10+
import lombok.EqualsAndHashCode;
11+
import org.opensearch.sql.ast.AbstractNodeVisitor;
12+
13+
/**
14+
* Expression node that holds a function which should be replaced by its constant[1] value.
15+
* [1] Constant at execution time.
16+
*/
17+
@EqualsAndHashCode(callSuper = false)
18+
public class ConstantFunction extends Function {
19+
20+
public ConstantFunction(String funcName, List<UnresolvedExpression> funcArgs) {
21+
super(funcName, funcArgs);
22+
}
23+
24+
@Override
25+
public <R, C> R accept(AbstractNodeVisitor<R, C> nodeVisitor, C context) {
26+
return nodeVisitor.visitConstantFunction(this, context);
27+
}
28+
}

core/src/main/java/org/opensearch/sql/expression/DSL.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,42 @@ public FunctionExpression match_bool_prefix(Expression... args) {
694694
return compile(BuiltinFunctionName.MATCH_BOOL_PREFIX, args);
695695
}
696696

697+
public FunctionExpression now(Expression... args) {
698+
return compile(BuiltinFunctionName.NOW, args);
699+
}
700+
701+
public FunctionExpression current_timestamp(Expression... args) {
702+
return compile(BuiltinFunctionName.CURRENT_TIMESTAMP, args);
703+
}
704+
705+
public FunctionExpression localtimestamp(Expression... args) {
706+
return compile(BuiltinFunctionName.LOCALTIMESTAMP, args);
707+
}
708+
709+
public FunctionExpression localtime(Expression... args) {
710+
return compile(BuiltinFunctionName.LOCALTIME, args);
711+
}
712+
713+
public FunctionExpression sysdate(Expression... args) {
714+
return compile(BuiltinFunctionName.SYSDATE, args);
715+
}
716+
717+
public FunctionExpression curtime(Expression... args) {
718+
return compile(BuiltinFunctionName.CURTIME, args);
719+
}
720+
721+
public FunctionExpression current_time(Expression... args) {
722+
return compile(BuiltinFunctionName.CURRENT_TIME, args);
723+
}
724+
725+
public FunctionExpression curdate(Expression... args) {
726+
return compile(BuiltinFunctionName.CURDATE, args);
727+
}
728+
729+
public FunctionExpression current_date(Expression... args) {
730+
return compile(BuiltinFunctionName.CURRENT_DATE, args);
731+
}
732+
697733
private FunctionExpression compile(BuiltinFunctionName bfn, Expression... args) {
698734
return (FunctionExpression) repository.compile(bfn.getName(), Arrays.asList(args.clone()));
699735
}

core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919
import static org.opensearch.sql.expression.function.FunctionDSL.impl;
2020
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling;
2121

22+
import java.math.BigDecimal;
23+
import java.math.RoundingMode;
2224
import java.time.LocalDate;
25+
import java.time.LocalDateTime;
2326
import java.time.LocalTime;
2427
import java.time.format.DateTimeFormatter;
2528
import java.time.format.TextStyle;
2629
import java.util.Locale;
2730
import java.util.concurrent.TimeUnit;
31+
import javax.annotation.Nullable;
2832
import lombok.experimental.UtilityClass;
2933
import org.opensearch.sql.data.model.ExprDateValue;
3034
import org.opensearch.sql.data.model.ExprDatetimeValue;
@@ -88,6 +92,84 @@ public void register(BuiltinFunctionRepository repository) {
8892
repository.register(to_days());
8993
repository.register(week());
9094
repository.register(year());
95+
96+
repository.register(now());
97+
repository.register(current_timestamp());
98+
repository.register(localtimestamp());
99+
repository.register(localtime());
100+
repository.register(sysdate());
101+
repository.register(curtime());
102+
repository.register(current_time());
103+
repository.register(curdate());
104+
repository.register(current_date());
105+
}
106+
107+
/**
108+
* NOW() returns a constant time that indicates the time at which the statement began to execute.
109+
* `fsp` argument support is removed until refactoring to avoid bug where `now()`, `now(x)` and
110+
* `now(y) return different values.
111+
*/
112+
private FunctionResolver now(FunctionName functionName) {
113+
return define(functionName,
114+
impl(() -> new ExprDatetimeValue(formatNow(null)), DATETIME)
115+
);
116+
}
117+
118+
private FunctionResolver now() {
119+
return now(BuiltinFunctionName.NOW.getName());
120+
}
121+
122+
private FunctionResolver current_timestamp() {
123+
return now(BuiltinFunctionName.CURRENT_TIMESTAMP.getName());
124+
}
125+
126+
private FunctionResolver localtimestamp() {
127+
return now(BuiltinFunctionName.LOCALTIMESTAMP.getName());
128+
}
129+
130+
private FunctionResolver localtime() {
131+
return now(BuiltinFunctionName.LOCALTIME.getName());
132+
}
133+
134+
/**
135+
* SYSDATE() returns the time at which it executes.
136+
*/
137+
private FunctionResolver sysdate() {
138+
return define(BuiltinFunctionName.SYSDATE.getName(),
139+
impl(() -> new ExprDatetimeValue(formatNow(null)), DATETIME),
140+
impl((v) -> new ExprDatetimeValue(formatNow(v.integerValue())), DATETIME, INTEGER)
141+
);
142+
}
143+
144+
/**
145+
* Synonym for @see `now`.
146+
*/
147+
private FunctionResolver curtime(FunctionName functionName) {
148+
return define(functionName,
149+
impl(() -> new ExprTimeValue(formatNow(null).toLocalTime()), TIME)
150+
);
151+
}
152+
153+
private FunctionResolver curtime() {
154+
return curtime(BuiltinFunctionName.CURTIME.getName());
155+
}
156+
157+
private FunctionResolver current_time() {
158+
return curtime(BuiltinFunctionName.CURRENT_TIME.getName());
159+
}
160+
161+
private FunctionResolver curdate(FunctionName functionName) {
162+
return define(functionName,
163+
impl(() -> new ExprDateValue(formatNow(null).toLocalDate()), DATE)
164+
);
165+
}
166+
167+
private FunctionResolver curdate() {
168+
return curdate(BuiltinFunctionName.CURDATE.getName());
169+
}
170+
171+
private FunctionResolver current_date() {
172+
return curdate(BuiltinFunctionName.CURRENT_DATE.getName());
91173
}
92174

93175
/**
@@ -796,4 +878,24 @@ private ExprValue exprYear(ExprValue date) {
796878
return new ExprIntegerValue(date.dateValue().getYear());
797879
}
798880

881+
/**
882+
* Prepare LocalDateTime value. Truncate fractional second part according to the argument.
883+
* @param fsp argument is given to specify a fractional seconds precision from 0 to 6,
884+
* the return value includes a fractional seconds part of that many digits.
885+
* @return LocalDateTime object.
886+
*/
887+
private LocalDateTime formatNow(@Nullable Integer fsp) {
888+
var res = LocalDateTime.now();
889+
if (fsp == null) {
890+
fsp = 0;
891+
}
892+
var defaultPrecision = 9; // There are 10^9 nanoseconds in one second
893+
if (fsp < 0 || fsp > 6) { // Check that the argument is in the allowed range [0, 6]
894+
throw new IllegalArgumentException(
895+
String.format("Invalid `fsp` value: %d, allowed 0 to 6", fsp));
896+
}
897+
var nano = new BigDecimal(res.getNano())
898+
.setScale(fsp - defaultPrecision, RoundingMode.DOWN).intValue();
899+
return res.withNano(nano);
900+
}
799901
}

core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,16 @@ public enum BuiltinFunctionName {
8787
UTC_TIMESTAMP(FunctionName.of("utc_timestamp")),
8888
WEEK(FunctionName.of("week")),
8989
YEAR(FunctionName.of("year")),
90-
90+
// `now`-like functions
91+
NOW(FunctionName.of("now")),
92+
CURDATE(FunctionName.of("curdate")),
93+
CURRENT_DATE(FunctionName.of("current_date")),
94+
CURTIME(FunctionName.of("curtime")),
95+
CURRENT_TIME(FunctionName.of("current_time")),
96+
LOCALTIME(FunctionName.of("localtime")),
97+
CURRENT_TIMESTAMP(FunctionName.of("current_timestamp")),
98+
LOCALTIMESTAMP(FunctionName.of("localtimestamp")),
99+
SYSDATE(FunctionName.of("sysdate")),
91100
/**
92101
* Text Functions.
93102
*/

0 commit comments

Comments
 (0)