Skip to content

Commit 86ace08

Browse files
Add implementation of now, sysdate, localtime and similar functions (#92)
Signed-off-by: Yury Fridlyand <[email protected]>
1 parent 29f1c52 commit 86ace08

File tree

61 files changed

+2445
-333
lines changed

Some content is hidden

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

61 files changed

+2445
-333
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
buildscript {
88
ext {
99
opensearch_version = System.getProperty("opensearch.version", "2.2.0-SNAPSHOT")
10+
spring_version = "5.3.22"
11+
jackson_version = "2.13.3"
1012
isSnapshot = "true" == System.getProperty("build.snapshot", "true")
1113
buildVersionQualifier = System.getProperty("build.version_qualifier", "")
1214
version_tokens = opensearch_version.tokenize('-')

common/src/main/java/org/opensearch/sql/common/utils/LogUtils.java renamed to common/src/main/java/org/opensearch/sql/common/utils/QueryContext.java

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,29 @@
66

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

9+
import java.time.LocalDateTime;
910
import java.util.Map;
11+
import java.util.Optional;
1012
import java.util.UUID;
1113
import org.apache.logging.log4j.ThreadContext;
1214

1315
/**
14-
* Utility class for generating/accessing the request id from logging context.
16+
* 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
1519
*/
16-
public class LogUtils {
20+
public class QueryContext {
1721

1822
/**
1923
* The key of the request id in the context map.
2024
*/
2125
private static final String REQUEST_ID_KEY = "request_id";
2226

27+
/**
28+
* Timestamp when SQL plugin started to process current request.
29+
*/
30+
private static final String REQUEST_PROCESSING_STARTED = "request_processing_started";
31+
2332
/**
2433
* Generates a random UUID and adds to the {@link ThreadContext} as the request id.
2534
* <p>
@@ -29,17 +38,38 @@ public class LogUtils {
2938
* call this method twice on the same thread within the lifetime of the request.
3039
* </p>
3140
*/
32-
public static void addRequestId() {
33-
ThreadContext.put(REQUEST_ID_KEY, UUID.randomUUID().toString());
41+
public static String addRequestId() {
42+
var id = UUID.randomUUID().toString();
43+
ThreadContext.put(REQUEST_ID_KEY, id);
44+
return id;
3445
}
3546

3647
/**
3748
* Get RequestID.
3849
* @return the current request id from {@link ThreadContext}.
3950
*/
4051
public static String getRequestId() {
41-
final String requestId = ThreadContext.get(REQUEST_ID_KEY);
42-
return requestId;
52+
var id = ThreadContext.get(REQUEST_ID_KEY);
53+
if (null == id) {
54+
id = addRequestId();
55+
}
56+
return id;
57+
}
58+
59+
public static void recordProcessingStarted() {
60+
ThreadContext.put(REQUEST_PROCESSING_STARTED, LocalDateTime.now().toString());
61+
}
62+
63+
/**
64+
* Get recorded previously time indicating when processing started for the current query.
65+
* @return A LocalDateTime object
66+
*/
67+
public static LocalDateTime getProcessingStartedTime() {
68+
if (ThreadContext.containsKey(REQUEST_PROCESSING_STARTED)) {
69+
return LocalDateTime.parse(ThreadContext.get(REQUEST_PROCESSING_STARTED));
70+
}
71+
// This shouldn't happen outside of unit tests
72+
return LocalDateTime.now();
4373
}
4474

4575
/**
@@ -57,7 +87,7 @@ public static Runnable withCurrentContext(final Runnable task) {
5787
};
5888
}
5989

60-
private LogUtils() {
90+
private QueryContext() {
6191
throw new AssertionError(
6292
getClass().getCanonicalName() + " is a utility class and must not be initialized");
6393
}

core/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@ repositories {
4040

4141
dependencies {
4242
api group: 'com.google.guava', name: 'guava', version: '31.0.1-jre'
43-
api group: 'org.springframework', name: 'spring-context', version: '5.3.22'
44-
api group: 'org.springframework', name: 'spring-beans', version: '5.3.22'
43+
api group: 'org.springframework', name: 'spring-context', version: "${spring_version}"
44+
api group: 'org.springframework', name: 'spring-beans', version: "${spring_version}"
4545
api group: 'org.apache.commons', name: 'commons-lang3', version: '3.10'
4646
api group: 'com.facebook.presto', name: 'presto-matching', version: '0.240'
4747
api group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'
4848
api project(':common')
4949

5050
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
5151
testImplementation group: 'org.hamcrest', name: 'hamcrest-library', version: '2.1'
52-
testImplementation group: 'org.springframework', name: 'spring-test', version: '5.3.22'
52+
testImplementation group: 'org.springframework', name: 'spring-test', version: "${spring_version}"
5353
testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.12.4'
5454
testImplementation group: 'org.mockito', name: 'mockito-junit-jupiter', version: '3.12.4'
5555
}

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

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

685+
public FunctionExpression now(Expression... args) {
686+
return compile(BuiltinFunctionName.NOW, args);
687+
}
688+
689+
public FunctionExpression current_timestamp(Expression... args) {
690+
return compile(BuiltinFunctionName.CURRENT_TIMESTAMP, args);
691+
}
692+
693+
public FunctionExpression localtimestamp(Expression... args) {
694+
return compile(BuiltinFunctionName.LOCALTIMESTAMP, args);
695+
}
696+
697+
public FunctionExpression localtime(Expression... args) {
698+
return compile(BuiltinFunctionName.LOCALTIME, args);
699+
}
700+
701+
public FunctionExpression sysdate(Expression... args) {
702+
return compile(BuiltinFunctionName.SYSDATE, args);
703+
}
704+
705+
public FunctionExpression curtime(Expression... args) {
706+
return compile(BuiltinFunctionName.CURTIME, args);
707+
}
708+
709+
public FunctionExpression current_time(Expression... args) {
710+
return compile(BuiltinFunctionName.CURRENT_TIME, args);
711+
}
712+
713+
public FunctionExpression curdate(Expression... args) {
714+
return compile(BuiltinFunctionName.CURDATE, args);
715+
}
716+
717+
public FunctionExpression current_date(Expression... args) {
718+
return compile(BuiltinFunctionName.CURRENT_DATE, args);
719+
}
720+
685721
private FunctionExpression compile(BuiltinFunctionName bfn, Expression... args) {
686722
return (FunctionExpression) repository.compile(bfn.getName(), Arrays.asList(args.clone()));
687723
}

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

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,17 @@
1818
import static org.opensearch.sql.expression.function.FunctionDSL.impl;
1919
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling;
2020

21+
import java.math.BigDecimal;
22+
import java.math.RoundingMode;
2123
import java.time.LocalDate;
24+
import java.time.LocalDateTime;
2225
import java.time.format.TextStyle;
2326
import java.util.Locale;
2427
import java.util.concurrent.TimeUnit;
28+
import java.util.function.Supplier;
29+
import javax.annotation.Nullable;
2530
import lombok.experimental.UtilityClass;
31+
import org.opensearch.sql.common.utils.QueryContext;
2632
import org.opensearch.sql.data.model.ExprDateValue;
2733
import org.opensearch.sql.data.model.ExprDatetimeValue;
2834
import org.opensearch.sql.data.model.ExprIntegerValue;
@@ -78,6 +84,89 @@ public void register(BuiltinFunctionRepository repository) {
7884
repository.register(to_days());
7985
repository.register(week());
8086
repository.register(year());
87+
88+
repository.register(now());
89+
repository.register(current_timestamp());
90+
repository.register(localtimestamp());
91+
repository.register(localtime());
92+
repository.register(sysdate());
93+
repository.register(curtime());
94+
repository.register(current_time());
95+
repository.register(curdate());
96+
repository.register(current_date());
97+
}
98+
99+
/**
100+
* NOW() returns a constant time that indicates the time at which the statement began to execute.
101+
*/
102+
private LocalDateTime now(@Nullable Integer fsp) {
103+
return formatLocalDateTime(QueryContext::getProcessingStartedTime, fsp);
104+
}
105+
106+
private FunctionResolver now(FunctionName functionName) {
107+
return define(functionName,
108+
impl(() -> new ExprDatetimeValue(now((Integer)null)), DATETIME),
109+
impl((v) -> new ExprDatetimeValue(now(v.integerValue())), DATETIME, INTEGER)
110+
);
111+
}
112+
113+
private FunctionResolver now() {
114+
return now(BuiltinFunctionName.NOW.getName());
115+
}
116+
117+
private FunctionResolver current_timestamp() {
118+
return now(BuiltinFunctionName.CURRENT_TIMESTAMP.getName());
119+
}
120+
121+
private FunctionResolver localtimestamp() {
122+
return now(BuiltinFunctionName.LOCALTIMESTAMP.getName());
123+
}
124+
125+
private FunctionResolver localtime() {
126+
return now(BuiltinFunctionName.LOCALTIME.getName());
127+
}
128+
129+
/**
130+
* SYSDATE() returns the time at which it executes.
131+
*/
132+
private LocalDateTime sysDate(@Nullable Integer fsp) {
133+
return formatLocalDateTime(LocalDateTime::now, fsp);
134+
}
135+
136+
private FunctionResolver sysdate() {
137+
return define(BuiltinFunctionName.SYSDATE.getName(),
138+
impl(() -> new ExprDatetimeValue(sysDate(null)), DATETIME),
139+
impl((v) -> new ExprDatetimeValue(sysDate(v.integerValue())), DATETIME, INTEGER)
140+
);
141+
}
142+
143+
private FunctionResolver curtime(FunctionName functionName) {
144+
return define(functionName,
145+
impl(() -> new ExprTimeValue(sysDate(null).toLocalTime()), TIME),
146+
impl((v) -> new ExprTimeValue(sysDate(v.integerValue()).toLocalTime()), TIME, INTEGER)
147+
);
148+
}
149+
150+
private FunctionResolver curtime() {
151+
return curtime(BuiltinFunctionName.CURTIME.getName());
152+
}
153+
154+
private FunctionResolver current_time() {
155+
return curtime(BuiltinFunctionName.CURRENT_TIME.getName());
156+
}
157+
158+
private FunctionResolver curdate(FunctionName functionName) {
159+
return define(functionName,
160+
impl(() -> new ExprDateValue(sysDate(null).toLocalDate()), DATE)
161+
);
162+
}
163+
164+
private FunctionResolver curdate() {
165+
return curdate(BuiltinFunctionName.CURDATE.getName());
166+
}
167+
168+
private FunctionResolver current_date() {
169+
return curdate(BuiltinFunctionName.CURRENT_DATE.getName());
81170
}
82171

83172
/**
@@ -108,6 +197,10 @@ private FunctionResolver adddate() {
108197
return add_date(BuiltinFunctionName.ADDDATE.getName());
109198
}
110199

200+
private FunctionResolver date_add() {
201+
return add_date(BuiltinFunctionName.DATE_ADD.getName());
202+
}
203+
111204
/**
112205
* Extracts the date part of a date and time value.
113206
* Also to construct a date type. The supported signatures:
@@ -121,10 +214,6 @@ private FunctionResolver date() {
121214
impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, TIMESTAMP));
122215
}
123216

124-
private FunctionResolver date_add() {
125-
return add_date(BuiltinFunctionName.DATE_ADD.getName());
126-
}
127-
128217
/**
129218
* Specify a start date and subtract a temporal amount to the date.
130219
* The return type depends on the date type and the interval unit. Detailed supported signatures:
@@ -679,4 +768,26 @@ private ExprValue exprYear(ExprValue date) {
679768
return new ExprIntegerValue(date.dateValue().getYear());
680769
}
681770

771+
/**
772+
* Prepare LocalDateTime value.
773+
* @param supplier A function which returns LocalDateTime to format.
774+
* @param fsp argument is given to specify a fractional seconds precision from 0 to 6,
775+
* the return value includes a fractional seconds part of that many digits.
776+
* @return LocalDateTime object.
777+
*/
778+
private LocalDateTime formatLocalDateTime(Supplier<LocalDateTime> supplier,
779+
@Nullable Integer fsp) {
780+
var res = supplier.get();
781+
if (fsp == null) {
782+
return res;
783+
}
784+
var defaultPrecision = 9; // There are 10^9 nanoseconds in one second
785+
if (fsp < 0 || fsp > 6) { // Check that the argument is in the allowed range [0, 6]
786+
throw new IllegalArgumentException(
787+
String.format("Invalid `fsp` value: %d, allowed 0 to 6", fsp));
788+
}
789+
var nano = new BigDecimal(res.getNano())
790+
.setScale(fsp - defaultPrecision, RoundingMode.DOWN).intValue();
791+
return res.withNano(nano);
792+
}
682793
}

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
@@ -82,7 +82,16 @@ public enum BuiltinFunctionName {
8282
TO_DAYS(FunctionName.of("to_days")),
8383
WEEK(FunctionName.of("week")),
8484
YEAR(FunctionName.of("year")),
85-
85+
// `now`-like functions
86+
NOW(FunctionName.of("now")),
87+
CURDATE(FunctionName.of("curdate")),
88+
CURRENT_DATE(FunctionName.of("current_date")),
89+
CURTIME(FunctionName.of("curtime")),
90+
CURRENT_TIME(FunctionName.of("current_time")),
91+
LOCALTIME(FunctionName.of("localtime")),
92+
CURRENT_TIMESTAMP(FunctionName.of("current_timestamp")),
93+
LOCALTIMESTAMP(FunctionName.of("localtimestamp")),
94+
SYSDATE(FunctionName.of("sysdate")),
8695
/**
8796
* Text Functions.
8897
*/

core/src/main/java/org/opensearch/sql/planner/logical/LogicalPlanDSL.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import org.opensearch.sql.expression.Expression;
1919
import org.opensearch.sql.expression.LiteralExpression;
2020
import org.opensearch.sql.expression.NamedExpression;
21-
import org.opensearch.sql.expression.ParseExpression;
2221
import org.opensearch.sql.expression.ReferenceExpression;
2322
import org.opensearch.sql.expression.aggregation.NamedAggregator;
2423
import org.opensearch.sql.expression.window.WindowDefinition;

0 commit comments

Comments
 (0)