Skip to content
This repository was archived by the owner on Aug 2, 2022. It is now read-only.

Commit c43d8af

Browse files
committed
Merge branch 'develop' into feature/sql-new-engine-enabled
2 parents a97fb05 + 43f41bd commit c43d8af

File tree

12 files changed

+184
-39
lines changed

12 files changed

+184
-39
lines changed

core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/DSL.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,10 @@ public FunctionExpression nullif(Expression... expressions) {
526526
return function(BuiltinFunctionName.NULLIF, expressions);
527527
}
528528

529+
public FunctionExpression iffunction(Expression... expressions) {
530+
return function(BuiltinFunctionName.IF, expressions);
531+
}
532+
529533
public static Expression cases(Expression defaultResult,
530534
WhenClause... whenClauses) {
531535
return new CaseClause(Arrays.asList(whenClauses), defaultResult);

core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public enum BuiltinFunctionName {
139139
IS_NULL(FunctionName.of("is null")),
140140
IS_NOT_NULL(FunctionName.of("is not null")),
141141
IFNULL(FunctionName.of("ifnull")),
142+
IF(FunctionName.of("if")),
142143
NULLIF(FunctionName.of("nullif")),
143144
ISNULL(FunctionName.of("isnull")),
144145

core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/UnaryPredicateOperator.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515

1616
package com.amazon.opendistroforelasticsearch.sql.expression.operator.predicate;
1717

18-
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_FALSE;
19-
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_MISSING;
2018
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_NULL;
2119
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_TRUE;
2220

@@ -59,6 +57,7 @@ public static void register(BuiltinFunctionRepository repository) {
5957
repository.register(nullIf());
6058
repository.register(isNull(BuiltinFunctionName.IS_NULL));
6159
repository.register(isNull(BuiltinFunctionName.ISNULL));
60+
repository.register(ifFunction());
6261
}
6362

6463
private static FunctionResolver not() {
@@ -100,21 +99,28 @@ private static FunctionResolver isNotNull() {
10099
Collectors.toList()));
101100
}
102101

103-
private static FunctionResolver ifNull() {
104-
FunctionName functionName = BuiltinFunctionName.IFNULL.getName();
102+
private static FunctionResolver ifFunction() {
103+
FunctionName functionName = BuiltinFunctionName.IF.getName();
105104
List<ExprCoreType> typeList = ExprCoreType.coreTypes();
106105

107106
List<SerializableFunction<FunctionName, org.apache.commons.lang3.tuple.Pair<FunctionSignature,
108107
FunctionBuilder>>> functionsOne = typeList.stream().map(v ->
109-
impl((UnaryPredicateOperator::exprIfNull), v, v, v))
108+
impl((UnaryPredicateOperator::exprIf), v, BOOLEAN, v, v))
110109
.collect(Collectors.toList());
111110

111+
FunctionResolver functionResolver = FunctionDSL.define(functionName, functionsOne);
112+
return functionResolver;
113+
}
114+
115+
private static FunctionResolver ifNull() {
116+
FunctionName functionName = BuiltinFunctionName.IFNULL.getName();
117+
List<ExprCoreType> typeList = ExprCoreType.coreTypes();
118+
112119
List<SerializableFunction<FunctionName, org.apache.commons.lang3.tuple.Pair<FunctionSignature,
113-
FunctionBuilder>>> functionsTwo = typeList.stream().map(v ->
114-
impl((UnaryPredicateOperator::exprIfNull), v, UNKNOWN, v))
120+
FunctionBuilder>>> functionsOne = typeList.stream().map(v ->
121+
impl((UnaryPredicateOperator::exprIfNull), v, v, v))
115122
.collect(Collectors.toList());
116123

117-
functionsOne.addAll(functionsTwo);
118124
FunctionResolver functionResolver = FunctionDSL.define(functionName, functionsOne);
119125
return functionResolver;
120126
}
@@ -149,4 +155,8 @@ public static ExprValue exprNullIf(ExprValue v1, ExprValue v2) {
149155
return v1.equals(v2) ? LITERAL_NULL : v1;
150156
}
151157

158+
public static ExprValue exprIf(ExprValue v1, ExprValue v2, ExprValue v3) {
159+
return !v1.isNull() && !v1.isMissing() && LITERAL_TRUE.equals(v1) ? v2 : v3;
160+
}
161+
152162
}

core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/UnaryPredicateOperatorTest.java

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ private static Stream<Arguments> isNullArguments() {
8080
private static Stream<Arguments> ifNullArguments() {
8181
ArrayList<Expression> exprValueArrayList = new ArrayList<>();
8282
exprValueArrayList.add(DSL.literal(123));
83-
exprValueArrayList.add(DSL.literal("test"));
83+
exprValueArrayList.add(DSL.literal(LITERAL_NULL));
8484
exprValueArrayList.add(DSL.literal(321));
85-
exprValueArrayList.add(DSL.literal(""));
85+
exprValueArrayList.add(DSL.literal(LITERAL_NULL));
8686

8787
return Lists.cartesianProduct(exprValueArrayList, exprValueArrayList).stream()
8888
.map(list -> {
@@ -115,12 +115,30 @@ private static Stream<Arguments> nullIfArguments() {
115115
});
116116
}
117117

118+
private static Stream<Arguments> ifArguments() {
119+
ArrayList<Expression> exprValueArrayList = new ArrayList<>();
120+
exprValueArrayList.add(DSL.literal(LITERAL_TRUE));
121+
exprValueArrayList.add(DSL.literal(LITERAL_FALSE));
122+
exprValueArrayList.add(DSL.literal(LITERAL_NULL));
123+
exprValueArrayList.add(DSL.literal(LITERAL_MISSING));
124+
125+
return Lists.cartesianProduct(exprValueArrayList, exprValueArrayList).stream()
126+
.map(list -> {
127+
Expression e1 = list.get(0);
128+
if (e1.valueOf(valueEnv()).value() == LITERAL_TRUE.value()) {
129+
return Arguments.of(e1, DSL.literal("123"), DSL.literal("321"), DSL.literal("123"));
130+
} else {
131+
return Arguments.of(e1, DSL.literal("123"), DSL.literal("321"), DSL.literal("321"));
132+
}
133+
});
134+
}
135+
118136
private static Stream<Arguments> exprIfNullArguments() {
119137
ArrayList<ExprValue> exprValues = new ArrayList<>();
120138
exprValues.add(LITERAL_NULL);
121139
exprValues.add(LITERAL_MISSING);
122140
exprValues.add(ExprValueUtils.integerValue(123));
123-
exprValues.add(ExprValueUtils.stringValue("test"));
141+
exprValues.add(ExprValueUtils.integerValue(456));
124142

125143
return Lists.cartesianProduct(exprValues, exprValues).stream()
126144
.map(list -> {
@@ -200,18 +218,24 @@ public void test_ifnull_predicate(Expression v1, Expression v2, Expression expec
200218
assertEquals(expected.valueOf(valueEnv()), dsl.ifnull(v1, v2).valueOf(valueEnv()));
201219
}
202220

203-
@ParameterizedTest
204-
@MethodSource("exprIfNullArguments")
205-
public void test_exprIfNull_predicate(ExprValue v1, ExprValue v2, ExprValue expected) {
206-
assertEquals(expected.value(), UnaryPredicateOperator.exprIfNull(v1, v2).value());
207-
}
208-
209221
@ParameterizedTest
210222
@MethodSource("nullIfArguments")
211223
public void test_nullif_predicate(Expression v1, Expression v2, Expression expected) {
212224
assertEquals(expected.valueOf(valueEnv()), dsl.nullif(v1, v2).valueOf(valueEnv()));
213225
}
214226

227+
@ParameterizedTest
228+
@MethodSource("ifArguments")
229+
public void test_if_predicate(Expression v1, Expression v2, Expression v3, Expression expected) {
230+
assertEquals(expected.valueOf(valueEnv()), dsl.iffunction(v1, v2, v3).valueOf(valueEnv()));
231+
}
232+
233+
@ParameterizedTest
234+
@MethodSource("exprIfNullArguments")
235+
public void test_exprIfNull_predicate(ExprValue v1, ExprValue v2, ExprValue expected) {
236+
assertEquals(expected.value(), UnaryPredicateOperator.exprIfNull(v1, v2).value());
237+
}
238+
215239
@ParameterizedTest
216240
@MethodSource("exprNullIfArguments")
217241
public void test_exprNullIf_predicate(ExprValue v1, ExprValue v2, ExprValue expected) {

docs/experiment/ppl/functions/condition.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,39 @@ Example::
145145
| False | Quility | Nanette |
146146
| True | null | Dale |
147147
+----------+------------+-------------+
148+
149+
IF
150+
------
151+
152+
Description
153+
>>>>>>>>>>>
154+
155+
Usage: if(condition, expr1, expr2) return expr1 if condition is true, otherwiser return expr2.
156+
157+
Argument type: all the supported data type, (NOTE : if expr1 and expr2 are different type, you will fail semantic check
158+
159+
Return type: any
160+
161+
Example::
162+
163+
od> source=accounts | eval result = if(true, firstname, lastname) | fields result, firstname, lastname
164+
fetched rows / total rows = 4/4
165+
+----------+-------------+------------+
166+
| result | firstname | lastname |
167+
|----------+-------------+------------|
168+
| Amber | Amber | Duke |
169+
| Hattie | Hattie | Bond |
170+
| Nanette | Nanette | Bates |
171+
| Dale | Dale | Adams |
172+
+----------+-------------+------------+
173+
174+
od> source=accounts | eval result = if(false, firstname, lastname) | fields result, firstname, lastname
175+
fetched rows / total rows = 4/4
176+
+----------+-------------+------------+
177+
| result | firstname | lastname |
178+
|----------+-------------+------------|
179+
| Duke | Amber | Duke |
180+
| Bond | Hattie | Bond |
181+
| Bates | Nanette | Bates |
182+
| Adams | Dale | Adams |
183+
+----------+-------------+------------+

docs/user/dql/functions.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,6 +1982,40 @@ Example::
19821982
| True | False |
19831983
+---------------+---------------+
19841984

1985+
IF
1986+
------
1987+
1988+
Description
1989+
>>>>>>>>>>>
1990+
1991+
Specifications:
1992+
1993+
1. IF(condition, ES_TYPE1, ES_TYPE2) -> ES_TYPE1 or ES_TYPE2
1994+
1995+
Usage: if first parameter is true, return second parameter, otherwise return third one.
1996+
1997+
Argument type: condition as BOOLEAN, second and third can by any type
1998+
1999+
Return type: Any (NOTE : if parameters #2 and #3 has different type, you will fail semantic check"
2000+
2001+
Example::
2002+
2003+
od> SELECT IF(100 > 200, '100', '200')
2004+
fetched rows / total rows = 1/1
2005+
+-------------------------------+
2006+
| IF(100 > 200, '100', '200') |
2007+
|-------------------------------|
2008+
| 200 |
2009+
+-------------------------------+
2010+
2011+
od> SELECT IF(200 > 100, '100', '200')
2012+
fetched rows / total rows = 1/1
2013+
+-------------------------------+
2014+
| IF(200 > 100, '100', '200') |
2015+
|-------------------------------|
2016+
| 100 |
2017+
+-------------------------------+
2018+
19852019
CASE
19862020
----
19872021

integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/SQLFunctionsIT.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -714,12 +714,13 @@ public void right() throws IOException {
714714

715715
@Test
716716
public void ifFuncShouldPassJDBC() {
717+
Assume.assumeTrue(isNewQueryEngineEabled());
717718
JSONObject response = executeJdbcRequest(
718719
"SELECT IF(age > 30, 'True', 'False') AS Ages FROM " + TEST_INDEX_ACCOUNT
719720
+ " WHERE age IS NOT NULL GROUP BY Ages");
720-
assertEquals("Ages", response.query("/schema/0/name"));
721+
assertEquals("IF(age > 30, \'True\', \'False\')", response.query("/schema/0/name"));
721722
assertEquals("Ages", response.query("/schema/0/alias"));
722-
assertEquals("double", response.query("/schema/0/type"));
723+
assertEquals("keyword", response.query("/schema/0/type"));
723724
}
724725

725726
@Test

integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/ConditionalIT.java

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,16 @@ public void ifnullShouldPassJDBC() throws IOException {
6363
@Test
6464
public void ifnullWithNullInputTest() {
6565
JSONObject response = new JSONObject(executeQuery(
66-
"SELECT IFNULL(1/0, firstname) as IFNULL1 ,"
67-
+ " IFNULL(firstname, 1/0) as IFNULL2 ,"
68-
+ " IFNULL(1/0, 1/0) as IFNULL3 "
66+
"SELECT IFNULL(null, firstname) as IFNULL1 ,"
67+
+ " IFNULL(firstname, null) as IFNULL2 ,"
68+
+ " IFNULL(null, null) as IFNULL3 "
6969
+ " FROM " + TEST_INDEX_BANK_WITH_NULL_VALUES
7070
+ " WHERE balance is null limit 2", "jdbc"));
71+
7172
verifySchema(response,
72-
schema("IFNULL(1/0, firstname)", "IFNULL1", "keyword"),
73-
schema("IFNULL(firstname, 1/0)", "IFNULL2", "integer"),
74-
schema("IFNULL(1/0, 1/0)", "IFNULL3", "integer"));
73+
schema("IFNULL(null, firstname)", "IFNULL1", "keyword"),
74+
schema("IFNULL(firstname, null)", "IFNULL2", "keyword"),
75+
schema("IFNULL(null, null)", "IFNULL3", "byte"));
7576
verifyDataRows(response,
7677
rows("Hattie", "Hattie", LITERAL_NULL.value()),
7778
rows( "Elinor", "Elinor", LITERAL_NULL.value())
@@ -81,18 +82,19 @@ public void ifnullWithNullInputTest() {
8182
@Test
8283
public void ifnullWithMissingInputTest() {
8384
JSONObject response = new JSONObject(executeQuery(
84-
"SELECT IFNULL(balance, firstname) as IFNULL1 ,"
85-
+ " IFNULL(firstname, balance) as IFNULL2 ,"
85+
"SELECT IFNULL(balance, 100) as IFNULL1, "
86+
+ " IFNULL(200, balance) as IFNULL2, "
8687
+ " IFNULL(balance, balance) as IFNULL3 "
8788
+ " FROM " + TEST_INDEX_BANK_WITH_NULL_VALUES
88-
+ " WHERE balance is null limit 2", "jdbc"));
89+
+ " WHERE balance is null limit 3", "jdbc"));
8990
verifySchema(response,
90-
schema("IFNULL(balance, firstname)", "IFNULL1", "keyword"),
91-
schema("IFNULL(firstname, balance)", "IFNULL2", "long"),
91+
schema("IFNULL(balance, 100)", "IFNULL1", "long"),
92+
schema("IFNULL(200, balance)", "IFNULL2", "long"),
9293
schema("IFNULL(balance, balance)", "IFNULL3", "long"));
9394
verifyDataRows(response,
94-
rows("Hattie", "Hattie", LITERAL_NULL.value()),
95-
rows( "Elinor", "Elinor", LITERAL_NULL.value())
95+
rows(100, 200, null),
96+
rows(100, 200, null),
97+
rows(100, 200, null)
9698
);
9799
}
98100

@@ -109,8 +111,8 @@ public void nullifShouldPassJDBC() throws IOException {
109111
public void nullifWithNotNullInputTestOne(){
110112
JSONObject response = new JSONObject(executeQuery(
111113
"SELECT NULLIF(firstname, 'Amber JOHnny') as testnullif "
112-
+ "FROM " + TEST_INDEX_BANK_WITH_NULL_VALUES
113-
+ " limit 2 ", "jdbc"));
114+
+ "FROM " + TEST_INDEX_BANK_WITH_NULL_VALUES
115+
+ " limit 2 ", "jdbc"));
114116
verifySchema(response,
115117
schema("NULLIF(firstname, 'Amber JOHnny')", "testnullif", "keyword"));
116118
verifyDataRows(response,
@@ -186,6 +188,37 @@ public void isnullWithMathExpr() throws IOException{
186188
);
187189
}
188190

191+
@Test
192+
public void ifShouldPassJDBC() throws IOException {
193+
JSONObject response = executeJdbcRequest(
194+
"SELECT IF(2 > 0, \'hello\', \'world\') AS name FROM " + TEST_INDEX_ACCOUNT);
195+
assertEquals("IF(2 > 0, \'hello\', \'world\')", response.query("/schema/0/name"));
196+
assertEquals("name", response.query("/schema/0/alias"));
197+
assertEquals("keyword", response.query("/schema/0/type"));
198+
}
199+
200+
@Test
201+
public void ifWithTrueAndFalseCondition() throws IOException {
202+
JSONObject response = new JSONObject(executeQuery(
203+
"SELECT IF(2 < 0, firstname, lastname) as IF0, "
204+
+ " IF(2 > 0, firstname, lastname) as IF1, "
205+
+ " firstname as IF2, "
206+
+ " lastname as IF3 "
207+
+ " FROM " + TEST_INDEX_BANK_WITH_NULL_VALUES
208+
+ " limit 2 ", "jdbc" ));
209+
verifySchema(response,
210+
schema("IF(2 < 0, firstname, lastname)", "IF0", "keyword"),
211+
schema("IF(2 > 0, firstname, lastname)", "IF1", "keyword"),
212+
schema("firstname", "IF2", "text"),
213+
schema("lastname", "IF3", "keyword")
214+
);
215+
verifyDataRows(response,
216+
rows("Duke Willmington", "Amber JOHnny", "Amber JOHnny", "Duke Willmington"),
217+
rows("Bond", "Hattie", "Hattie", "Bond")
218+
);
219+
220+
}
221+
189222
private SearchHits query(String query) throws IOException {
190223
final String rsp = executeQueryWithStringOutput(query);
191224

integ-test/src/test/resources/correctness/expressions/conditionals.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ CASE WHEN 'test' = 'hello world' THEN 'Hello' WHEN 1.0 = 2.0 THEN 'One' ELSE TRI
1313
CASE 1 WHEN 1 THEN 'One' ELSE NULL END AS cases
1414
CASE 1 WHEN 1 THEN 'One' WHEN 2 THEN 'Two' ELSE NULL END AS cases
1515
CASE 2 WHEN 1 THEN NULL WHEN 2 THEN 'Two' ELSE NULL END AS cases
16-
IFNULL(1/0, AvgTicketPrice) from kibana_sample_data_flights
17-
IFNULL(FlightTimeHour, AvgTicketPrice) from kibana_sample_data_flights
18-
IFNULL(AvgTicketPrice, 1/0) from kibana_sample_data_flights
16+
IFNULL(null, AvgTicketPrice) from kibana_sample_data_flights
17+
IFNULL(AvgTicketPrice, 100) from kibana_sample_data_flights
18+
IFNULL(AvgTicketPrice, null) from kibana_sample_data_flights

ppl/src/main/antlr/OpenDistroPPLLexer.g4

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ ISNOTNULL: 'ISNOTNULL';
238238
// FLOWCONTROL FUNCTIONS
239239
IFNULL: 'IFNULL';
240240
NULLIF: 'NULLIF';
241+
IF: 'IF';
242+
241243

242244
// LITERALS AND VALUES
243245
//STRING_LITERAL: DQUOTA_STRING | SQUOTA_STRING | BQUOTA_STRING;

0 commit comments

Comments
 (0)