Skip to content

Commit e8ab7ca

Browse files
committed
SQL: Fix deserialization issue of TimeProcessor
TimeProcessor didn't implement `getWriteableName()` so the one from the parent was used which returned the `NAME` of the parent. This caused `TimeProcessor` objects to be deserialised into DateTimeProcessor. Moreover, added a restriction to run the TIME related integration tests only in UTC timezone. Fixes: #40717
1 parent 23719bb commit e8ab7ca

File tree

7 files changed

+77
-62
lines changed

7 files changed

+77
-62
lines changed

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import static org.elasticsearch.xpack.sql.jdbc.EsType.DATETIME;
4040
import static org.elasticsearch.xpack.sql.jdbc.EsType.TIME;
4141
import static org.elasticsearch.xpack.sql.jdbc.JdbcDateUtils.asDateTimeField;
42+
import static org.elasticsearch.xpack.sql.jdbc.JdbcDateUtils.timeAsTime;
4243

4344
/**
4445
* Conversion utilities for conversion of JDBC types to Java type and back
@@ -220,7 +221,7 @@ static Object convert(Object v, EsType columnType, String typeString) throws SQL
220221
case DATE:
221222
return asDateTimeField(v, JdbcDateUtils::asDate, Date::new);
222223
case TIME:
223-
return asDateTimeField(v, JdbcDateUtils::asTime, Time::new);
224+
return timeAsTime(v.toString());
224225
case DATETIME:
225226
return asDateTimeField(v, JdbcDateUtils::asTimestamp, Timestamp::new);
226227
case INTERVAL_YEAR:

x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvSpecTestCase.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ public CsvSpecTestCase(String fileName, String groupName, String testName, Integ
4040

4141
@Override
4242
protected final void doTest() throws Throwable {
43-
try (Connection csv = csvConnection(testCase); Connection es = esJdbc()) {
44-
45-
// pass the testName as table for debugging purposes (in case the underlying reader is missing)
46-
ResultSet expected = executeCsvQuery(csv, testName);
47-
ResultSet elasticResults = executeJdbcQuery(es, testCase.query);
48-
assertResults(expected, elasticResults);
43+
if ("time".equals(groupName)) { // Run the time tests always in UTC
44+
try (Connection csv = csvConnection(testCase); Connection es = esJdbc(connectionProperties())) {
45+
executeAndAssert(csv, es);
46+
}
47+
} else {
48+
try (Connection csv = csvConnection(testCase); Connection es = esJdbc()) {
49+
executeAndAssert(csv, es);
50+
}
4951
}
5052
}
5153

@@ -54,4 +56,11 @@ protected void assertResults(ResultSet expected, ResultSet elastic) throws SQLEx
5456
Logger log = logEsResultSet() ? logger : null;
5557
JdbcAssert.assertResultSets(expected, elastic, log, false, true);
5658
}
59+
60+
private void executeAndAssert(Connection csv, Connection es) throws SQLException {
61+
// pass the testName as table for debugging purposes (in case the underlying reader is missing)
62+
ResultSet expected = executeCsvQuery(csv, testName);
63+
ResultSet elasticResults = executeJdbcQuery(es, testCase.query);
64+
assertResults(expected, elasticResults);
65+
}
5766
}

x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcIntegrationTestCase.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Properties;
2828
import java.util.Set;
2929

30+
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_TIMEZONE;
3031
import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.assertNoSearchContexts;
3132

3233
public abstract class JdbcIntegrationTestCase extends ESRestTestCase {
@@ -137,7 +138,7 @@ protected String clusterName() {
137138
*/
138139
protected Properties connectionProperties() {
139140
Properties connectionProperties = new Properties();
140-
connectionProperties.put("timezone", randomKnownTimeZone());
141+
connectionProperties.put(JDBC_TIMEZONE, randomKnownTimeZone());
141142
// in the tests, don't be lenient towards multi values
142143
connectionProperties.put("field.multi.value.leniency", "false");
143144
return connectionProperties;

x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/SpecBaseIntegrationTestCase.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.Properties;
3333

3434
import static java.util.Collections.emptyList;
35+
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_TIMEZONE;
3536

3637
/**
3738
* Tests that compare the Elasticsearch JDBC client to some other JDBC client
@@ -116,7 +117,7 @@ protected int fetchSize() {
116117
@Override
117118
protected Properties connectionProperties() {
118119
Properties connectionProperties = new Properties();
119-
connectionProperties.setProperty("timezone", "UTC");
120+
connectionProperties.setProperty(JDBC_TIMEZONE, "UTC");
120121
return connectionProperties;
121122
}
122123

@@ -217,4 +218,4 @@ public interface Parser {
217218
public static InputStream readFromJarUrl(URL source) throws IOException {
218219
return source.openStream();
219220
}
220-
}
221+
}

x-pack/plugin/sql/qa/src/main/resources/time.csv-spec

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
//
22
// TIME
33
//
4+
// All tests are run with UTC timezone
45

5-
// AwaitsFix: https://github.com/elastic/elasticsearch/issues/40717
6-
//timeExtractTimeParts
7-
//SELECT
8-
//SECOND(CAST(birth_date AS TIME)) d,
9-
//MINUTE(CAST(birth_date AS TIME)) m,
10-
//HOUR(CAST(birth_date AS TIME)) h
11-
//FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
12-
//
13-
// d:i | m:i | h:i
14-
//0 |0 |0
15-
//0 |0 |0
16-
//0 |0 |0
17-
//0 |0 |0
18-
//0 |0 |0
19-
//0 |0 |0
20-
//0 |0 |0
21-
//0 |0 |0
22-
//0 |0 |0
23-
//;
6+
timeExtractTimeParts
7+
SELECT
8+
SECOND(CAST(birth_date AS TIME)) d,
9+
MINUTE(CAST(birth_date AS TIME)) m,
10+
HOUR(CAST(birth_date AS TIME)) h
11+
FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
12+
13+
d:i | m:i | h:i
14+
0 |0 |0
15+
0 |0 |0
16+
0 |0 |0
17+
0 |0 |0
18+
0 |0 |0
19+
0 |0 |0
20+
0 |0 |0
21+
0 |0 |0
22+
0 |0 |0
23+
;
2424

2525
timeAsFilter
2626
SELECT birth_date, last_name FROM "test_emp" WHERE birth_date::TIME = CAST('00:00:00' AS TIME) ORDER BY emp_no LIMIT 5;
@@ -59,15 +59,14 @@ null |100445
5959
0 |904605
6060
;
6161

62-
// AwaitsFix: https://github.com/elastic/elasticsearch/issues/40717
63-
//timeAsHavingFilter
64-
//SELECT MINUTE_OF_HOUR(MAX(birth_date)::TIME + INTERVAL 10 MINUTES) as minute, gender FROM test_emp GROUP BY gender HAVING CAST(MAX(birth_date) AS TIME) = CAST('00:00:00.000' AS TIME) ORDER BY gender;
65-
//
66-
//minute:i | gender:s
67-
//10 | null
68-
//10 | F
69-
//10 | M
70-
//;
62+
timeAsHavingFilter
63+
SELECT MINUTE_OF_HOUR(MAX(birth_date)::TIME + INTERVAL 10 MINUTES) as minute, gender FROM test_emp GROUP BY gender HAVING CAST(MAX(birth_date) AS TIME) = CAST('00:00:00.000' AS TIME) ORDER BY gender;
64+
65+
minute:i | gender:s
66+
10 | null
67+
10 | F
68+
10 | M
69+
;
7170

7271
timeAsHavingFilterNoMatch
7372
SELECT MINUTE_OF_HOUR(MAX(birth_date)::TIME) as minute, gender FROM test_emp GROUP BY gender HAVING CAST(MAX(birth_date) AS TIME) > CAST('00:00:00.000' AS TIME);

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/TimeProcessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
public class TimeProcessor extends DateTimeProcessor {
1818

19-
2019
public static final String NAME = "time";
2120

2221
public TimeProcessor(DateTimeExtractor extractor, ZoneId zoneId) {
@@ -27,6 +26,11 @@ public TimeProcessor(StreamInput in) throws IOException {
2726
super(in);
2827
}
2928

29+
@Override
30+
public String getWriteableName() {
31+
return NAME;
32+
}
33+
3034
@Override
3135
public Object process(Object input) {
3236
if (input instanceof OffsetTime) {

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/ProcessorTests.java

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public static void init() throws Exception {
3030
processors = NodeSubclassTests.subclassesOf(Processor.class);
3131
}
3232

33-
3433
public void testProcessorRegistration() throws Exception {
3534
LinkedHashSet<String> registered = Processors.getNamedWriteables().stream()
3635
.map(e -> e.name)
@@ -39,32 +38,33 @@ public void testProcessorRegistration() throws Exception {
3938
// discover available processors
4039
int missing = processors.size() - registered.size();
4140

41+
List<String> notRegistered = new ArrayList<>();
42+
for (Class<? extends Processor> proc : processors) {
43+
String procName = proc.getName();
44+
assertTrue(procName + " does NOT implement NamedWriteable", NamedWriteable.class.isAssignableFrom(proc));
45+
Field name = null;
46+
String value = null;
47+
try {
48+
name = proc.getField("NAME");
49+
} catch (Exception ex) {
50+
fail(procName + " does NOT provide a NAME field\n" + ex);
51+
}
52+
try {
53+
value = name.get(proc).toString();
54+
} catch (Exception ex) {
55+
fail(procName + " does NOT provide a static NAME field\n" + ex);
56+
}
57+
if (!registered.contains(value)) {
58+
notRegistered.add(procName);
59+
}
60+
Class<?> declaringClass = proc.getMethod("getWriteableName").getDeclaringClass();
61+
assertEquals("Processor: " + proc + " doesn't override getWriteableName", proc, declaringClass);
62+
}
4263

4364
if (missing > 0) {
44-
List<String> notRegistered = new ArrayList<>();
45-
for (Class<? extends Processor> proc : processors) {
46-
String procName = proc.getName();
47-
assertTrue(procName + " does NOT implement NamedWriteable", NamedWriteable.class.isAssignableFrom(proc));
48-
Field name = null;
49-
String value = null;
50-
try {
51-
name = proc.getField("NAME");
52-
} catch (Exception ex) {
53-
fail(procName + " does NOT provide a NAME field\n" + ex);
54-
}
55-
try {
56-
value = name.get(proc).toString();
57-
} catch (Exception ex) {
58-
fail(procName + " does NOT provide a static NAME field\n" + ex);
59-
}
60-
if (!registered.contains(value)) {
61-
notRegistered.add(procName);
62-
}
63-
}
64-
6565
fail(missing + " processor(s) not registered : " + notRegistered);
6666
} else {
6767
assertEquals("Detection failed: discovered more registered processors than classes", 0, missing);
6868
}
6969
}
70-
}
70+
}

0 commit comments

Comments
 (0)