diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/FetchSizeTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/FetchSizeTestCase.java index fee5901bc4cb2..ae3e569458a48 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/FetchSizeTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/FetchSizeTestCase.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.xpack.sql.proto.StringUtils; import org.junit.Before; import java.io.IOException; @@ -16,7 +17,12 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Properties; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_TIMEZONE; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.assertNoSearchContexts; /** @@ -106,6 +112,55 @@ public void testIncompleteScroll() throws Exception { assertNoSearchContexts(); } + public void testScrollWithDatetimeAndTimezoneParam() throws IOException, SQLException { + Request request = new Request("PUT", "/test_date_timezone"); + XContentBuilder createIndex = JsonXContent.contentBuilder().startObject(); + createIndex.startObject("mappings"); + { + createIndex.startObject("_doc"); + { + createIndex.startObject("properties"); + { + createIndex.startObject("date").field("type", "date").field("format", "epoch_millis"); + createIndex.endObject(); + } + createIndex.endObject(); + } + createIndex.endObject(); + } + createIndex.endObject().endObject(); + request.setJsonEntity(Strings.toString(createIndex)); + client().performRequest(request); + + request = new Request("PUT", "/test_date_timezone/_doc/_bulk"); + request.addParameter("refresh", "true"); + StringBuilder bulk = new StringBuilder(); + long[] datetimes = new long[] { 1_000, 10_000, 100_000, 1_000_000, 10_000_000 }; + for (long datetime : datetimes) { + bulk.append("{\"index\":{}}\n"); + bulk.append("{\"date\":").append(datetime).append("}\n"); + } + request.setJsonEntity(bulk.toString()); + assertEquals(200, client().performRequest(request).getStatusLine().getStatusCode()); + + ZoneId zoneId = randomZone(); + Properties connectionProperties = connectionProperties(); + connectionProperties.put(JDBC_TIMEZONE, zoneId.toString()); + try (Connection c = esJdbc(connectionProperties); + Statement s = c.createStatement()) { + s.setFetchSize(2); + try (ResultSet rs = + s.executeQuery("SELECT CAST(date AS string) AS date FROM test_date_timezone ORDER BY date")) { + for (int i = 0; i < datetimes.length; i++) { + assertEquals(2, rs.getFetchSize()); + assertTrue("No more entries left at " + i, rs.next()); + assertEquals(StringUtils.toString(ZonedDateTime.ofInstant(Instant.ofEpochMilli(datetimes[i]), zoneId)), + rs.getString(1)); + } + assertFalse(rs.next()); + } + } + } /** * Test for {@code SELECT} that is implemented as an aggregation. diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java index 8de0c8d18543d..414cae6dd053a 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.Streams; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.NotEqualMessageBuilder; @@ -30,6 +31,9 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.sql.JDBCType; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -126,7 +130,76 @@ public void testNextPage() throws IOException { ContentType.APPLICATION_JSON), StringUtils.EMPTY)); } - @AwaitsFix(bugUrl = "https://github.com/elastic/x-pack-elasticsearch/issues/2074") + public void testNextPageWithDatetimeAndTimezoneParam() throws IOException { + Request request = new Request("PUT", "/test_date_timezone"); + XContentBuilder createIndex = JsonXContent.contentBuilder().startObject(); + createIndex.startObject("mappings"); + { + createIndex.startObject("_doc"); + { + createIndex.startObject("properties"); + { + createIndex.startObject("date").field("type", "date").field("format", "epoch_millis"); + createIndex.endObject(); + } + createIndex.endObject(); + } + createIndex.endObject(); + } + createIndex.endObject().endObject(); + request.setJsonEntity(Strings.toString(createIndex)); + client().performRequest(request); + + request = new Request("PUT", "/test_date_timezone/_doc/_bulk"); + request.addParameter("refresh", "true"); + StringBuilder bulk = new StringBuilder(); + long[] datetimes = new long[] { 1_000, 10_000, 100_000, 1_000_000, 10_000_000 }; + for (long datetime : datetimes) { + bulk.append("{\"index\":{}}\n"); + bulk.append("{\"date\":").append(datetime).append("}\n"); + } + request.setJsonEntity(bulk.toString()); + assertEquals(200, client().performRequest(request).getStatusLine().getStatusCode()); + + ZoneId zoneId = randomZone(); + String mode = randomMode(); + String sqlRequest = + "{\"query\":\"SELECT CAST(date AS string) AS date FROM test_date_timezone ORDER BY date\"," + + "\"time_zone\":\"" + zoneId.getId() + "\", " + + "\"mode\":\"" + mode + "\", " + + "\"fetch_size\":2}"; + + String cursor = null; + for (int i = 0; i <= datetimes.length; i += 2) { + Map expected = new HashMap<>(); + Map response; + + if (i == 0) { + expected.put("columns", singletonList(columnInfo(mode, "date", "keyword", JDBCType.VARCHAR, + Integer.MAX_VALUE))); + response = runSql(new StringEntity(sqlRequest, ContentType.APPLICATION_JSON), ""); + } else { + response = runSql(new StringEntity("{\"cursor\":\"" + cursor + "\"" + mode(mode) + "}", + ContentType.APPLICATION_JSON), StringUtils.EMPTY); + } + + List values = new ArrayList<>(2); + for (int j = 0; j < (i < datetimes.length - 1 ? 2 : 1); j++) { + values.add(singletonList(StringUtils.toString( + ZonedDateTime.ofInstant(Instant.ofEpochMilli(datetimes[i + j]), zoneId)))); + } + expected.put("rows", values); + cursor = (String) response.remove("cursor"); + assertResponse(expected, response); + assertNotNull(cursor); + } + Map expected = new HashMap<>(); + expected.put("rows", emptyList()); + assertResponse(expected, runSql(new StringEntity("{ \"cursor\":\"" + cursor + "\"" + mode(mode) + "}", + ContentType.APPLICATION_JSON), StringUtils.EMPTY)); + } + + @AwaitsFix(bugUrl = "Unclear status, https://github.com/elastic/x-pack-elasticsearch/issues/2074") public void testTimeZone() throws IOException { String mode = randomMode(); index("{\"test\":\"2017-07-27 00:00:00\"}",