Skip to content

Commit cfea348

Browse files
authored
SQL: Fix deserialisation issue of TimeProcessor (#40776)
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 ba9ea6a commit cfea348

File tree

7 files changed

+80
-62
lines changed

7 files changed

+80
-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: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,16 @@ 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+
// Run the time tests always in UTC
44+
// TODO: https://github.com/elastic/elasticsearch/issues/40779
45+
if ("time".equals(groupName)) {
46+
try (Connection csv = csvConnection(testCase); Connection es = esJdbc(connectionProperties())) {
47+
executeAndAssert(csv, es);
48+
}
49+
} else {
50+
try (Connection csv = csvConnection(testCase); Connection es = esJdbc()) {
51+
executeAndAssert(csv, es);
52+
}
4953
}
5054
}
5155

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

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: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
//
22
// TIME
33
//
4+
// All tests are run with UTC timezone
5+
// TODO: https://github.com/elastic/elasticsearch/issues/40779
46

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-
//;
7+
timeExtractTimeParts
8+
SELECT
9+
SECOND(CAST(birth_date AS TIME)) d,
10+
MINUTE(CAST(birth_date AS TIME)) m,
11+
HOUR(CAST(birth_date AS TIME)) h
12+
FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
13+
14+
d:i | m:i | h:i
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+
0 |0 |0
24+
;
2425

2526
timeAsFilter
2627
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 +60,14 @@ null |100445
5960
0 |904605
6061
;
6162

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-
//;
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+
;
7171

7272
timeAsHavingFilterNoMatch
7373
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)