Skip to content

Commit f40d870

Browse files
authored
Fix PPL eval command string concatenation with + operator (#4020)
* eval command support Signed-off-by: Kai Huang <ahkcs@amazon.com> * improvment Signed-off-by: Kai Huang <ahkcs@amazon.com> * Refactor Signed-off-by: Kai Huang <ahkcs@amazon.com> * fix CI Signed-off-by: Kai Huang <ahkcs@amazon.com> * fix CI Signed-off-by: Kai Huang <ahkcs@amazon.com> * fix CI Signed-off-by: Kai Huang <ahkcs@amazon.com> * fixes Signed-off-by: Kai Huang <ahkcs@amazon.com> * fix Signed-off-by: Kai Huang <ahkcs@amazon.com> * Add IT Signed-off-by: Kai Huang <ahkcs@amazon.com> * remove redundant tests Signed-off-by: Kai Huang <ahkcs@amazon.com> --------- Signed-off-by: Kai Huang <ahkcs@amazon.com>
1 parent 8368e60 commit f40d870

File tree

2 files changed

+119
-1
lines changed

2 files changed

+119
-1
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,17 @@ void populate() {
689689
registerOperator(AND, SqlStdOperatorTable.AND);
690690
registerOperator(OR, SqlStdOperatorTable.OR);
691691
registerOperator(NOT, SqlStdOperatorTable.NOT);
692-
registerOperator(ADD, SqlStdOperatorTable.PLUS);
692+
693+
// Register ADD (+ symbol) for numeric addition
694+
register(
695+
ADD,
696+
(RexBuilder builder, RexNode... args) -> builder.makeCall(SqlStdOperatorTable.PLUS, args),
697+
new PPLTypeChecker.PPLFamilyTypeChecker(SqlTypeFamily.NUMERIC, SqlTypeFamily.NUMERIC));
698+
699+
// Register ADD (+ symbol) for string concatenation
700+
registerOperator(ADD, SqlStdOperatorTable.CONCAT);
701+
702+
// Register ADDFUNCTION for numeric addition only
693703
registerOperator(ADDFUNCTION, SqlStdOperatorTable.PLUS);
694704
registerOperator(SUBTRACT, SqlStdOperatorTable.MINUS);
695705
registerOperator(SUBTRACTFUNCTION, SqlStdOperatorTable.MINUS);
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.calcite.remote;
7+
8+
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_BANK;
9+
import static org.opensearch.sql.util.MatcherUtils.rows;
10+
import static org.opensearch.sql.util.MatcherUtils.schema;
11+
import static org.opensearch.sql.util.MatcherUtils.verifyDataRows;
12+
import static org.opensearch.sql.util.MatcherUtils.verifySchema;
13+
14+
import java.io.IOException;
15+
import org.json.JSONObject;
16+
import org.junit.jupiter.api.Test;
17+
import org.opensearch.client.Request;
18+
import org.opensearch.sql.ppl.PPLIntegTestCase;
19+
20+
public class CalciteEvalCommandIT extends PPLIntegTestCase {
21+
22+
@Override
23+
public void init() throws Exception {
24+
super.init();
25+
enableCalcite();
26+
27+
loadIndex(Index.BANK);
28+
29+
// Create test data for string concatenation
30+
Request request1 = new Request("PUT", "/test_eval/_doc/1?refresh=true");
31+
request1.setJsonEntity("{\"name\": \"Alice\", \"age\": 25, \"title\": \"Engineer\"}");
32+
client().performRequest(request1);
33+
34+
Request request2 = new Request("PUT", "/test_eval/_doc/2?refresh=true");
35+
request2.setJsonEntity("{\"name\": \"Bob\", \"age\": 30, \"title\": \"Manager\"}");
36+
client().performRequest(request2);
37+
38+
Request request3 = new Request("PUT", "/test_eval/_doc/3?refresh=true");
39+
request3.setJsonEntity("{\"name\": \"Charlie\", \"age\": null, \"title\": \"Analyst\"}");
40+
client().performRequest(request3);
41+
}
42+
43+
@Test
44+
public void testEvalStringConcatenation() throws IOException {
45+
JSONObject result = executeQuery("source=test_eval | eval greeting = 'Hello ' + name");
46+
verifySchema(
47+
result,
48+
schema("name", "string"),
49+
schema("title", "string"),
50+
schema("age", "bigint"),
51+
schema("greeting", "string"));
52+
verifyDataRows(
53+
result,
54+
rows("Alice", "Engineer", 25, "Hello Alice"),
55+
rows("Bob", "Manager", 30, "Hello Bob"),
56+
rows("Charlie", "Analyst", null, "Hello Charlie"));
57+
}
58+
59+
@Test
60+
public void testEvalStringConcatenationWithNullField() throws IOException {
61+
JSONObject result =
62+
executeQuery(
63+
"source=test_eval | eval age_desc = 'Age: ' + CAST(age AS STRING) | fields name, age,"
64+
+ " age_desc");
65+
verifySchema(
66+
result, schema("name", "string"), schema("age", "bigint"), schema("age_desc", "string"));
67+
verifyDataRows(
68+
result,
69+
rows("Alice", 25, "Age: 25"),
70+
rows("Bob", 30, "Age: 30"),
71+
rows("Charlie", null, null));
72+
}
73+
74+
@Test
75+
public void testEvalStringConcatenationWithLiterals() throws IOException {
76+
JSONObject result =
77+
executeQuery(
78+
"source=test_eval | eval full_info = 'Name: ' + name + ', Title: ' + title | fields"
79+
+ " name, title, full_info");
80+
verifySchema(
81+
result, schema("name", "string"), schema("title", "string"), schema("full_info", "string"));
82+
verifyDataRows(
83+
result,
84+
rows("Alice", "Engineer", "Name: Alice, Title: Engineer"),
85+
rows("Bob", "Manager", "Name: Bob, Title: Manager"),
86+
rows("Charlie", "Analyst", "Name: Charlie, Title: Analyst"));
87+
}
88+
89+
@Test
90+
public void testEvalStringConcatenationWithExistingData() throws IOException {
91+
JSONObject result =
92+
executeQuery(
93+
String.format(
94+
"source=%s | eval full_name = firstname + ' ' + lastname | head 3 | fields"
95+
+ " firstname, lastname, full_name",
96+
TEST_INDEX_BANK));
97+
verifySchema(
98+
result,
99+
schema("firstname", "string"),
100+
schema("lastname", "string"),
101+
schema("full_name", "string"));
102+
verifyDataRows(
103+
result,
104+
rows("Amber JOHnny", "Duke Willmington", "Amber JOHnny Duke Willmington"),
105+
rows("Hattie", "Bond", "Hattie Bond"),
106+
rows("Nanette", "Bates", "Nanette Bates"));
107+
}
108+
}

0 commit comments

Comments
 (0)