SQL: Extract SQL request and response classes (#30457)

Extracts SQL request and response classes. This is the first step
towards creation of a small minimal dependencies jdbc driver.

Relates #29856
This commit is contained in:
Igor Motov 2018-05-14 16:43:29 -04:00 committed by GitHub
parent 4e33443690
commit 56d32bc8b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 909 additions and 589 deletions

View File

@ -43,6 +43,10 @@ public class JdbcConnection implements Connection, JdbcWrapper {
private String catalog;
private String schema;
/**
* The SQLException is the only type of Exception the JDBC API can throw (and that the user expects).
* If we remove it, we need to make sure no other types of Exceptions (runtime or otherwise) are thrown
*/
public JdbcConnection(JdbcConfiguration connectionInfo) throws SQLException {
cfg = connectionInfo;
client = new JdbcHttpClient(connectionInfo);
@ -428,4 +432,4 @@ public class JdbcConnection implements Connection, JdbcWrapper {
int esInfoMinorVersion() throws SQLException {
return client.serverInfo().minorVersion;
}
}
}

View File

@ -7,7 +7,7 @@ package org.elasticsearch.xpack.sql.jdbc.jdbc;
import org.elasticsearch.xpack.sql.jdbc.net.client.Cursor;
import org.elasticsearch.xpack.sql.jdbc.net.client.RequestMeta;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import java.sql.Connection;
import java.sql.ResultSet;
@ -220,7 +220,7 @@ class JdbcStatement implements Statement, JdbcWrapper {
// unset (in this case -1 which the user cannot set) - in this case, the default fetch size is returned
// 0 meaning the hint is disabled (the user has called setFetch)
// >0 means actual hint
// tl;dr - unless the user set it, returning the default is fine
return requestMeta.fetchSize();
}
@ -402,4 +402,4 @@ class JdbcStatement implements Statement, JdbcWrapper {
close();
}
}
}
}

View File

@ -6,7 +6,7 @@
package org.elasticsearch.xpack.sql.jdbc.jdbc;
import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.type.DataType;
import java.sql.JDBCType;
@ -73,7 +73,7 @@ class PreparedQuery {
*/
List<SqlTypedParamValue> params() {
return Arrays.stream(this.params).map(
paramInfo -> new SqlTypedParamValue(paramInfo.value, DataType.fromJdbcType(paramInfo.type))
paramInfo -> new SqlTypedParamValue(DataType.fromJdbcType(paramInfo.type), paramInfo.value)
).collect(Collectors.toList());
}
@ -86,4 +86,4 @@ class PreparedQuery {
static PreparedQuery prepare(String sql) throws SQLException {
return new PreparedQuery(sql, SqlQueryParameterAnalyzer.parametersCount(sql));
}
}
}

View File

@ -5,22 +5,21 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.client;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.sql.client.HttpClient;
import org.elasticsearch.xpack.sql.jdbc.jdbc.JdbcConfiguration;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.InfoResponse;
import org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest;
import org.elasticsearch.xpack.sql.plugin.AbstractSqlRequest;
import org.elasticsearch.xpack.sql.plugin.SqlQueryRequest;
import org.elasticsearch.xpack.sql.plugin.SqlQueryResponse;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.Protocol;
import org.elasticsearch.xpack.sql.proto.SqlQueryRequest;
import org.elasticsearch.xpack.sql.proto.MainResponse;
import org.elasticsearch.xpack.sql.proto.SqlQueryResponse;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import java.sql.SQLException;
import java.util.List;
import java.util.TimeZone;
import java.util.stream.Collectors;
import static org.elasticsearch.xpack.sql.client.shared.StringUtils.EMPTY;
@ -34,6 +33,10 @@ public class JdbcHttpClient {
private final JdbcConfiguration conCfg;
private InfoResponse serverInfo;
/**
* The SQLException is the only type of Exception the JDBC API can throw (and that the user expects).
* If we remove it, we need to make sure no other types of Exceptions (runtime or otherwise) are thrown
*/
public JdbcHttpClient(JdbcConfiguration conCfg) throws SQLException {
httpClient = new HttpClient(conCfg);
this.conCfg = conCfg;
@ -45,9 +48,9 @@ public class JdbcHttpClient {
public Cursor query(String sql, List<SqlTypedParamValue> params, RequestMeta meta) throws SQLException {
int fetch = meta.fetchSize() > 0 ? meta.fetchSize() : conCfg.pageSize();
SqlQueryRequest sqlRequest = new SqlQueryRequest(AbstractSqlRequest.Mode.JDBC, sql, params, null,
AbstractSqlQueryRequest.DEFAULT_TIME_ZONE,
fetch, TimeValue.timeValueMillis(meta.timeoutInMs()), TimeValue.timeValueMillis(meta.queryTimeoutInMs()), "");
SqlQueryRequest sqlRequest = new SqlQueryRequest(Mode.JDBC, sql, params, null,
Protocol.TIME_ZONE,
fetch, TimeValue.timeValueMillis(meta.timeoutInMs()), TimeValue.timeValueMillis(meta.queryTimeoutInMs()));
SqlQueryResponse response = httpClient.query(sqlRequest);
return new DefaultCursor(this, response.cursor(), toJdbcColumnInfo(response.columns()), response.rows(), meta);
}
@ -57,10 +60,8 @@ public class JdbcHttpClient {
* the scroll id to use to fetch the next page.
*/
public Tuple<String, List<List<Object>>> nextPage(String cursor, RequestMeta meta) throws SQLException {
SqlQueryRequest sqlRequest = new SqlQueryRequest().cursor(cursor);
sqlRequest.mode(AbstractSqlRequest.Mode.JDBC);
sqlRequest.requestTimeout(TimeValue.timeValueMillis(meta.timeoutInMs()));
sqlRequest.pageTimeout(TimeValue.timeValueMillis(meta.queryTimeoutInMs()));
SqlQueryRequest sqlRequest = new SqlQueryRequest(Mode.JDBC, cursor, TimeValue.timeValueMillis(meta.timeoutInMs()),
TimeValue.timeValueMillis(meta.queryTimeoutInMs()));
SqlQueryResponse response = httpClient.query(sqlRequest);
return new Tuple<>(response.cursor(), response.rows());
}
@ -78,13 +79,13 @@ public class JdbcHttpClient {
private InfoResponse fetchServerInfo() throws SQLException {
MainResponse mainResponse = httpClient.serverInfo();
return new InfoResponse(mainResponse.getClusterName().value(), mainResponse.getVersion().major, mainResponse.getVersion().minor);
return new InfoResponse(mainResponse.getClusterName(), mainResponse.getVersion().major, mainResponse.getVersion().minor);
}
/**
* Converts REST column metadata into JDBC column metadata
*/
private List<ColumnInfo> toJdbcColumnInfo(List<org.elasticsearch.xpack.sql.plugin.ColumnInfo> columns) {
private List<ColumnInfo> toJdbcColumnInfo(List<org.elasticsearch.xpack.sql.proto.ColumnInfo> columns) {
return columns.stream().map(columnInfo ->
new ColumnInfo(columnInfo.name(), columnInfo.jdbcType(), EMPTY, EMPTY, EMPTY, EMPTY, columnInfo.displaySize())
).collect(Collectors.toList());

View File

@ -10,8 +10,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.plugin.AbstractSqlRequest;
import org.elasticsearch.xpack.sql.plugin.SqlQueryResponse;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.joda.time.DateTime;
import java.sql.JDBCType;
@ -51,7 +51,7 @@ public class TypeConverterTests extends ESTestCase {
XContentBuilder builder = JsonXContent.contentBuilder();
builder.startObject();
builder.field("value");
SqlQueryResponse.value(builder, AbstractSqlRequest.Mode.JDBC, value);
SqlQueryResponse.value(builder, Mode.JDBC, value);
builder.endObject();
builder.close();
Object copy = XContentHelper.convertToMap(BytesReference.bytes(builder), false, builder.contentType()).v2().get("value");

View File

@ -5,11 +5,12 @@
*/
package org.elasticsearch.xpack.sql.cli.command;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.xpack.sql.client.HttpClient;
import org.elasticsearch.xpack.sql.client.shared.ClientException;
import org.elasticsearch.xpack.sql.client.shared.Version;
import org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest;
import org.elasticsearch.xpack.sql.proto.MainResponse;
import org.elasticsearch.xpack.sql.proto.Protocol;
import java.sql.SQLException;
@ -18,7 +19,7 @@ import java.sql.SQLException;
*/
public class CliSession {
private final HttpClient httpClient;
private int fetchSize = AbstractSqlQueryRequest.DEFAULT_FETCH_SIZE;
private int fetchSize = Protocol.FETCH_SIZE;
private String fetchSeparator = "";
private boolean debug;

View File

@ -5,8 +5,8 @@
*/
package org.elasticsearch.xpack.sql.cli.command;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.xpack.sql.cli.CliTerminal;
import org.elasticsearch.xpack.sql.proto.MainResponse;
import java.sql.SQLException;
import java.util.Locale;
@ -30,7 +30,7 @@ public class ServerInfoCliCommand extends AbstractServerCliCommand {
}
terminal.line()
.text("Node:").em(info.getNodeName())
.text(" Cluster:").em(info.getClusterName().value())
.text(" Cluster:").em(info.getClusterName())
.text(" Version:").em(info.getVersion().toString())
.ln();
return true;

View File

@ -9,7 +9,7 @@ import org.elasticsearch.xpack.sql.cli.CliTerminal;
import org.elasticsearch.xpack.sql.client.HttpClient;
import org.elasticsearch.xpack.sql.client.shared.JreHttpUrlConnection;
import org.elasticsearch.xpack.sql.plugin.CliFormatter;
import org.elasticsearch.xpack.sql.plugin.SqlQueryResponse;
import org.elasticsearch.xpack.sql.proto.SqlQueryResponse;
import java.sql.SQLException;
@ -23,8 +23,8 @@ public class ServerQueryCliCommand extends AbstractServerCliCommand {
String data;
try {
response = cliClient.queryInit(line, cliSession.getFetchSize());
cliFormatter = new CliFormatter(response);
data = cliFormatter.formatWithHeader(response);
cliFormatter = new CliFormatter(response.columns(), response.rows());
data = cliFormatter.formatWithHeader(response.columns(), response.rows());
while (true) {
handleText(terminal, data);
if (response.cursor().isEmpty()) {
@ -36,7 +36,7 @@ public class ServerQueryCliCommand extends AbstractServerCliCommand {
terminal.println(cliSession.getFetchSeparator());
}
response = cliSession.getClient().nextPage(response.cursor());
data = cliFormatter.formatWithoutHeader(response);
data = cliFormatter.formatWithoutHeader(response.rows());
}
} catch (SQLException e) {
if (JreHttpUrlConnection.SQL_STATE_BAD_SERVER.equals(e.getSQLState())) {

View File

@ -6,7 +6,6 @@
package org.elasticsearch.xpack.sql.cli;
import org.elasticsearch.Build;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.test.ESTestCase;
@ -14,6 +13,7 @@ import org.elasticsearch.xpack.sql.cli.command.CliSession;
import org.elasticsearch.xpack.sql.client.HttpClient;
import org.elasticsearch.xpack.sql.client.shared.ClientException;
import org.elasticsearch.xpack.sql.client.shared.Version;
import org.elasticsearch.xpack.sql.proto.MainResponse;
import java.sql.SQLException;
@ -28,7 +28,7 @@ public class CliSessionTests extends ESTestCase {
public void testProperConnection() throws Exception {
HttpClient httpClient = mock(HttpClient.class);
when(httpClient.serverInfo()).thenReturn(new MainResponse(randomAlphaOfLength(5), org.elasticsearch.Version.CURRENT,
ClusterName.DEFAULT, UUIDs.randomBase64UUID(), Build.CURRENT));
ClusterName.DEFAULT.value(), UUIDs.randomBase64UUID(), Build.CURRENT));
CliSession cliSession = new CliSession(httpClient);
cliSession.checkConnection();
verify(httpClient, times(1)).serverInfo();
@ -58,10 +58,10 @@ public class CliSessionTests extends ESTestCase {
}
when(httpClient.serverInfo()).thenReturn(new MainResponse(randomAlphaOfLength(5),
org.elasticsearch.Version.fromString(major + "." + minor + ".23"),
ClusterName.DEFAULT, UUIDs.randomBase64UUID(), Build.CURRENT));
ClusterName.DEFAULT.value(), UUIDs.randomBase64UUID(), Build.CURRENT));
CliSession cliSession = new CliSession(httpClient);
expectThrows(ClientException.class, cliSession::checkConnection);
verify(httpClient, times(1)).serverInfo();
verifyNoMoreInteractions(httpClient);
}
}
}

View File

@ -6,12 +6,12 @@
package org.elasticsearch.xpack.sql.cli.command;
import org.elasticsearch.Build;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.cli.TestTerminal;
import org.elasticsearch.xpack.sql.client.HttpClient;
import org.elasticsearch.xpack.sql.proto.MainResponse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@ -36,7 +36,7 @@ public class ServerInfoCliCommandTests extends ESTestCase {
HttpClient client = mock(HttpClient.class);
CliSession cliSession = new CliSession(client);
when(client.serverInfo()).thenReturn(new MainResponse("my_node", org.elasticsearch.Version.fromString("1.2.3"),
new ClusterName("my_cluster"), UUIDs.randomBase64UUID(), Build.CURRENT));
new ClusterName("my_cluster").value(), UUIDs.randomBase64UUID(), Build.CURRENT));
ServerInfoCliCommand cliCommand = new ServerInfoCliCommand();
assertTrue(cliCommand.handle(testTerminal, cliSession, "info"));
assertEquals(testTerminal.toString(), "Node:<em>my_node</em> Cluster:<em>my_cluster</em> Version:<em>1.2.3</em>\n");
@ -44,4 +44,4 @@ public class ServerInfoCliCommandTests extends ESTestCase {
verifyNoMoreInteractions(client);
}
}
}

View File

@ -8,8 +8,8 @@ package org.elasticsearch.xpack.sql.cli.command;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.cli.TestTerminal;
import org.elasticsearch.xpack.sql.client.HttpClient;
import org.elasticsearch.xpack.sql.plugin.ColumnInfo;
import org.elasticsearch.xpack.sql.plugin.SqlQueryResponse;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import org.elasticsearch.xpack.sql.proto.SqlQueryResponse;
import java.sql.JDBCType;
import java.sql.SQLException;
@ -119,4 +119,4 @@ public class ServerQueryCliCommandTests extends ESTestCase {
}
return new SqlQueryResponse(cursor, columns, rows);
}
}
}

View File

@ -10,12 +10,17 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.Protocol;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.type.DataType;
import java.io.IOException;
import java.util.Collections;
@ -28,20 +33,12 @@ import java.util.function.Supplier;
* Base class for requests that contain sql queries (Query and Translate)
*/
public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest implements CompositeIndicesRequest, ToXContentFragment {
public static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("UTC");
/**
* Global choice for the default fetch size.
*/
public static final int DEFAULT_FETCH_SIZE = 1000;
public static final TimeValue DEFAULT_REQUEST_TIMEOUT = TimeValue.timeValueSeconds(90);
public static final TimeValue DEFAULT_PAGE_TIMEOUT = TimeValue.timeValueSeconds(45);
private String query = "";
private TimeZone timeZone = DEFAULT_TIME_ZONE;
private int fetchSize = DEFAULT_FETCH_SIZE;
private TimeValue requestTimeout = DEFAULT_REQUEST_TIMEOUT;
private TimeValue pageTimeout = DEFAULT_PAGE_TIMEOUT;
private TimeZone timeZone = Protocol.TIME_ZONE;
private int fetchSize = Protocol.FETCH_SIZE;
private TimeValue requestTimeout = Protocol.REQUEST_TIMEOUT;
private TimeValue pageTimeout = Protocol.PAGE_TIMEOUT;
@Nullable
private QueryBuilder filter = null;
private List<SqlTypedParamValue> params = Collections.emptyList();
@ -69,11 +66,10 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme
parser.declareObjectArray(AbstractSqlQueryRequest::params, (p, c) -> SqlTypedParamValue.fromXContent(p), new ParseField("params"));
parser.declareString((request, zoneId) -> request.timeZone(TimeZone.getTimeZone(zoneId)), new ParseField("time_zone"));
parser.declareInt(AbstractSqlQueryRequest::fetchSize, new ParseField("fetch_size"));
parser.declareString((request, timeout) -> request.requestTimeout(TimeValue.parseTimeValue(timeout, Protocol.REQUEST_TIMEOUT,
"request_timeout")), new ParseField("request_timeout"));
parser.declareString(
(request, timeout) -> request.requestTimeout(TimeValue.parseTimeValue(timeout, DEFAULT_REQUEST_TIMEOUT, "request_timeout")),
new ParseField("request_timeout"));
parser.declareString(
(request, timeout) -> request.pageTimeout(TimeValue.parseTimeValue(timeout, DEFAULT_PAGE_TIMEOUT, "page_timeout")),
(request, timeout) -> request.pageTimeout(TimeValue.parseTimeValue(timeout, Protocol.PAGE_TIMEOUT, "page_timeout")),
new ParseField("page_timeout"));
parser.declareObject(AbstractSqlQueryRequest::filter,
(p, c) -> AbstractQueryBuilder.parseInnerQueryBuilder(p), new ParseField("filter"));
@ -185,7 +181,7 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme
public AbstractSqlQueryRequest(StreamInput in) throws IOException {
super(in);
query = in.readString();
params = in.readList(SqlTypedParamValue::new);
params = in.readList(AbstractSqlQueryRequest::readSqlTypedParamValue);
timeZone = TimeZone.getTimeZone(in.readString());
fetchSize = in.readVInt();
requestTimeout = in.readTimeValue();
@ -193,11 +189,23 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme
filter = in.readOptionalNamedWriteable(QueryBuilder.class);
}
public static void writeSqlTypedParamValue(StreamOutput out, SqlTypedParamValue value) throws IOException {
out.writeEnum(value.dataType);
out.writeGenericValue(value.value);
}
public static SqlTypedParamValue readSqlTypedParamValue(StreamInput in) throws IOException {
return new SqlTypedParamValue(in.readEnum(DataType.class), in.readGenericValue());
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(query);
out.writeList(params);
out.writeVInt(params.size());
for (SqlTypedParamValue param: params) {
writeSqlTypedParamValue(out, param);
}
out.writeString(timeZone.getID());
out.writeVInt(fetchSize);
out.writeTimeValue(requestTimeout);
@ -224,36 +232,4 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme
public int hashCode() {
return Objects.hash(super.hashCode(), query, timeZone, fetchSize, requestTimeout, pageTimeout, filter);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (query != null) {
builder.field("query", query);
}
if (this.params.isEmpty() == false) {
builder.startArray("params");
for (SqlTypedParamValue val : this.params) {
val.toXContent(builder, params);
}
builder.endArray();
}
if (timeZone != null) {
builder.field("time_zone", timeZone.getID());
}
if (fetchSize != DEFAULT_FETCH_SIZE) {
builder.field("fetch_size", fetchSize);
}
if (requestTimeout != DEFAULT_REQUEST_TIMEOUT) {
builder.field("request_timeout", requestTimeout.getStringRep());
}
if (pageTimeout != DEFAULT_PAGE_TIMEOUT) {
builder.field("page_timeout", pageTimeout.getStringRep());
}
if (filter != null) {
builder.field("filter");
filter.toXContent(builder, params);
}
return builder;
}
}

View File

@ -10,9 +10,9 @@ import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.xpack.sql.proto.Mode;
import java.io.IOException;
import java.util.Locale;
import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
@ -24,24 +24,6 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
*/
public abstract class AbstractSqlRequest extends ActionRequest implements ToXContent {
public enum Mode {
PLAIN,
JDBC;
public static Mode fromString(String mode) {
if (mode == null) {
return PLAIN;
}
return Mode.valueOf(mode.toUpperCase(Locale.ROOT));
}
@Override
public String toString() {
return this.name().toLowerCase(Locale.ROOT);
}
}
private Mode mode = Mode.PLAIN;
protected AbstractSqlRequest() {

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.plugin;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import java.io.IOException;
import java.util.Arrays;
@ -28,19 +29,19 @@ public class CliFormatter implements Writeable {
/**
* Create a new {@linkplain CliFormatter} for formatting responses similar
* to the provided {@link SqlQueryResponse}.
* to the provided columns and rows.
*/
public CliFormatter(SqlQueryResponse response) {
public CliFormatter(List<ColumnInfo> columns, List<List<Object>> rows) {
// Figure out the column widths:
// 1. Start with the widths of the column names
width = new int[response.columns().size()];
width = new int[columns.size()];
for (int i = 0; i < width.length; i++) {
// TODO read the width from the data type?
width[i] = Math.max(MIN_COLUMN_WIDTH, response.columns().get(i).name().length());
width[i] = Math.max(MIN_COLUMN_WIDTH, columns.get(i).name().length());
}
// 2. Expand columns to fit the largest value
for (List<Object> row : response.rows()) {
for (List<Object> row : rows) {
for (int i = 0; i < width.length; i++) {
// TODO are we sure toString is correct here? What about dates that come back as longs.
// Tracked by https://github.com/elastic/x-pack-elasticsearch/issues/3081
@ -62,15 +63,15 @@ public class CliFormatter implements Writeable {
* Format the provided {@linkplain SqlQueryResponse} for the CLI
* including the header lines.
*/
public String formatWithHeader(SqlQueryResponse response) {
public String formatWithHeader(List<ColumnInfo> columns, List<List<Object>> rows) {
// The header lines
StringBuilder sb = new StringBuilder(estimateSize(response.rows().size() + 2));
StringBuilder sb = new StringBuilder(estimateSize(rows.size() + 2));
for (int i = 0; i < width.length; i++) {
if (i > 0) {
sb.append('|');
}
String name = response.columns().get(i).name();
String name = columns.get(i).name();
// left padding
int leftPadding = (width[i] - name.length()) / 2;
for (int j = 0; j < leftPadding; j++) {
@ -98,19 +99,19 @@ public class CliFormatter implements Writeable {
/* Now format the results. Sadly, this means that column
* widths are entirely determined by the first batch of
* results. */
return formatWithoutHeader(sb, response);
return formatWithoutHeader(sb, rows);
}
/**
* Format the provided {@linkplain SqlQueryResponse} for the CLI
* without the header lines.
*/
public String formatWithoutHeader(SqlQueryResponse response) {
return formatWithoutHeader(new StringBuilder(estimateSize(response.rows().size())), response);
public String formatWithoutHeader(List<List<Object>> rows) {
return formatWithoutHeader(new StringBuilder(estimateSize(rows.size())), rows);
}
private String formatWithoutHeader(StringBuilder sb, SqlQueryResponse response) {
for (List<Object> row : response.rows()) {
private String formatWithoutHeader(StringBuilder sb, List<List<Object>> rows) {
for (List<Object> row : rows) {
for (int i = 0; i < width.length; i++) {
if (i > 0) {
sb.append('|');
@ -138,7 +139,7 @@ public class CliFormatter implements Writeable {
}
/**
* Pick a good estimate of the buffer size needed to contain the rows.
* Pick a good estimate of the buffer size needed to contain the rows.
*/
int estimateSize(int rows) {
/* Each column has either a '|' or a '\n' after it

View File

@ -1,191 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.plugin;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.sql.JDBCType;
import java.util.Objects;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
/**
* Information about a column returned by the listColumns response
*/
public class MetaColumnInfo implements Writeable, ToXContentObject {
private static final ConstructingObjectParser<MetaColumnInfo, Void> PARSER =
new ConstructingObjectParser<>("column_info", true, objects ->
new MetaColumnInfo(
(String) objects[0],
(String) objects[1],
(String) objects[2],
objects[3] == null ? null : JDBCType.valueOf((int) objects[3]),
objects[4] == null ? 0 : (int) objects[4],
(int) objects[5]));
private static final ParseField TABLE = new ParseField("table");
private static final ParseField NAME = new ParseField("name");
private static final ParseField ES_TYPE = new ParseField("type");
private static final ParseField JDBC_TYPE = new ParseField("jdbc_type");
private static final ParseField SIZE = new ParseField("size");
private static final ParseField POSITION = new ParseField("position");
static {
PARSER.declareString(constructorArg(), TABLE);
PARSER.declareString(constructorArg(), NAME);
PARSER.declareString(constructorArg(), ES_TYPE);
PARSER.declareInt(optionalConstructorArg(), JDBC_TYPE);
PARSER.declareInt(optionalConstructorArg(), SIZE);
PARSER.declareInt(constructorArg(), POSITION);
}
private final String table;
private final String name;
private final String esType;
@Nullable
private final JDBCType jdbcType;
private final int size;
private final int position;
public MetaColumnInfo(String table, String name, String esType, JDBCType jdbcType, int size, int position) {
this.table = table;
this.name = name;
this.esType = esType;
this.jdbcType = jdbcType;
this.size = size;
this.position = position;
}
public MetaColumnInfo(String table, String name, String esType, int position) {
this(table, name, esType, null, 0, position);
}
MetaColumnInfo(StreamInput in) throws IOException {
table = in.readString();
name = in.readString();
esType = in.readString();
if (in.readBoolean()) {
jdbcType = JDBCType.valueOf(in.readVInt());
size = in.readVInt();
} else {
jdbcType = null;
size = 0;
}
position = in.readVInt();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(table);
out.writeString(name);
out.writeString(esType);
if (jdbcType != null) {
out.writeBoolean(true);
out.writeVInt(jdbcType.getVendorTypeNumber());
out.writeVInt(size);
} else {
out.writeBoolean(false);
}
out.writeVInt(position);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field("table", table);
builder.field("name", name);
builder.field("type", esType);
if (jdbcType != null) {
builder.field("jdbc_type", jdbcType.getVendorTypeNumber());
builder.field("size", size);
}
builder.field("position", position);
return builder.endObject();
}
public static MetaColumnInfo fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
/**
* Name of the table.
*/
public String table() {
return table;
}
/**
* Name of the column.
*/
public String name() {
return name;
}
/**
* The type of the column in Elasticsearch.
*/
public String esType() {
return esType;
}
/**
* The type of the column as it would be returned by a JDBC driver.
*/
public JDBCType jdbcType() {
return jdbcType;
}
/**
* Precision
*/
public int size() {
return size;
}
/**
* Column position with in the tables
*/
public int position() {
return position;
}
@Override
public String toString() {
return Strings.toString(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MetaColumnInfo that = (MetaColumnInfo) o;
return size == that.size &&
position == that.position &&
Objects.equals(table, that.table) &&
Objects.equals(name, that.name) &&
Objects.equals(esType, that.esType) &&
jdbcType == that.jdbcType;
}
@Override
public int hashCode() {
return Objects.hash(table, name, esType, jdbcType, size, position);
}
}

View File

@ -13,7 +13,6 @@ public class SqlClearCursorAction
public static final SqlClearCursorAction INSTANCE = new SqlClearCursorAction();
public static final String NAME = "indices:data/read/sql/close_cursor";
public static final String REST_ENDPOINT = "/_xpack/sql/close";
private SqlClearCursorAction() {
super(NAME);

View File

@ -10,9 +10,9 @@ import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.sql.proto.Mode;
import java.io.IOException;
import java.util.Objects;
@ -23,13 +23,13 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constru
/**
* Request to clean all SQL resources associated with the cursor
*/
public class SqlClearCursorRequest extends AbstractSqlRequest implements ToXContentObject {
public class SqlClearCursorRequest extends AbstractSqlRequest {
private static final ConstructingObjectParser<SqlClearCursorRequest, Mode> PARSER =
new ConstructingObjectParser<>(SqlClearCursorAction.NAME, true, (objects, mode) -> new SqlClearCursorRequest(
mode,
(String) objects[0]
));
new ConstructingObjectParser<>(SqlClearCursorAction.NAME, true, (objects, mode) -> new SqlClearCursorRequest(
mode,
(String) objects[0]
));
static {
PARSER.declareString(constructorArg(), new ParseField("cursor"));
@ -96,13 +96,11 @@ public class SqlClearCursorRequest extends AbstractSqlRequest implements ToXCont
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field("cursor", cursor);
builder.endObject();
return builder;
// This is needed just to test round-trip compatibility with proto.SqlClearCursorRequest
return new org.elasticsearch.xpack.sql.proto.SqlClearCursorRequest(mode(), cursor).toXContent(builder, params);
}
public static SqlClearCursorRequest fromXContent(XContentParser parser, Mode mode) {
return PARSER.apply(parser, mode);
}
}
}

View File

@ -6,13 +6,10 @@
package org.elasticsearch.xpack.sql.plugin;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.StatusToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
@ -20,20 +17,13 @@ import java.util.Objects;
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.xpack.sql.proto.SqlClearCursorResponse.SUCCEEDED;
/**
* Response to the request to clean all SQL resources associated with the cursor
*/
public class SqlClearCursorResponse extends ActionResponse implements StatusToXContentObject {
private static final ParseField SUCCEEDED = new ParseField("succeeded");
public static final ObjectParser<SqlClearCursorResponse, Void> PARSER =
new ObjectParser<>(SqlClearCursorAction.NAME, true, SqlClearCursorResponse::new);
static {
PARSER.declareBoolean(SqlClearCursorResponse::setSucceeded, SUCCEEDED);
}
private boolean succeeded;
public SqlClearCursorResponse(boolean succeeded) {
@ -93,9 +83,4 @@ public class SqlClearCursorResponse extends ActionResponse implements StatusToXC
return Objects.hash(succeeded);
}
public static SqlClearCursorResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
}

View File

@ -12,7 +12,6 @@ public class SqlQueryAction extends Action<SqlQueryRequest, SqlQueryResponse, Sq
public static final SqlQueryAction INSTANCE = new SqlQueryAction();
public static final String NAME = "indices:data/read/sql";
public static final String REST_ENDPOINT = "/_xpack/sql";
private SqlQueryAction() {
super(NAME);

View File

@ -12,11 +12,12 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import java.io.IOException;
import java.util.List;
@ -28,7 +29,7 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
/**
* Request to perform an sql query
*/
public class SqlQueryRequest extends AbstractSqlQueryRequest implements ToXContentObject {
public class SqlQueryRequest extends AbstractSqlQueryRequest {
private static final ObjectParser<SqlQueryRequest, Void> PARSER = objectParser(SqlQueryRequest::new);
public static final ParseField CURSOR = new ParseField("cursor");
@ -37,7 +38,7 @@ public class SqlQueryRequest extends AbstractSqlQueryRequest implements ToXConte
static {
PARSER.declareString(SqlQueryRequest::cursor, CURSOR);
PARSER.declareObject(SqlQueryRequest::filter,
(p, c) -> AbstractQueryBuilder.parseInnerQueryBuilder(p), FILTER);
(p, c) -> AbstractQueryBuilder.parseInnerQueryBuilder(p), FILTER);
}
private String cursor = "";
@ -108,24 +109,15 @@ public class SqlQueryRequest extends AbstractSqlQueryRequest implements ToXConte
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
super.toXContent(builder, params);
if (cursor != null) {
builder.field("cursor", cursor);
}
builder.endObject();
return builder;
}
@Override
public boolean isFragment() {
return false;
// This is needed just to test round-trip compatibility with proto.SqlQueryRequest
return new org.elasticsearch.xpack.sql.proto.SqlQueryRequest(mode(), query(), params(), timeZone(), fetchSize(),
requestTimeout(), pageTimeout(), filter(), cursor()).toXContent(builder, params);
}
public static SqlQueryRequest fromXContent(XContentParser parser, Mode mode) {
SqlQueryRequest request = PARSER.apply(parser, null);
SqlQueryRequest request = PARSER.apply(parser, null);
request.mode(mode);
return request;
}
}
}

View File

@ -9,25 +9,22 @@ import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.xpack.sql.plugin.AbstractSqlRequest.Mode;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.Protocol;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_FETCH_SIZE;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_PAGE_TIMEOUT;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_REQUEST_TIMEOUT;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_TIME_ZONE;
/**
* The builder to build sql request
*/
public class SqlQueryRequestBuilder extends ActionRequestBuilder<SqlQueryRequest, SqlQueryResponse, SqlQueryRequestBuilder> {
public SqlQueryRequestBuilder(ElasticsearchClient client, SqlQueryAction action) {
this(client, action, "", Collections.emptyList(), null, DEFAULT_TIME_ZONE, DEFAULT_FETCH_SIZE, DEFAULT_REQUEST_TIMEOUT,
DEFAULT_PAGE_TIMEOUT, "", Mode.PLAIN);
this(client, action, "", Collections.emptyList(), null, Protocol.TIME_ZONE, Protocol.FETCH_SIZE, Protocol.REQUEST_TIMEOUT,
Protocol.PAGE_TIMEOUT, "", Mode.PLAIN);
}
public SqlQueryRequestBuilder(ElasticsearchClient client, SqlQueryAction action, String query, List<SqlTypedParamValue> params,

View File

@ -7,49 +7,28 @@ package org.elasticsearch.xpack.sql.plugin;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.joda.time.ReadableDateTime;
import java.io.IOException;
import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static java.util.Collections.unmodifiableList;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
import static org.elasticsearch.common.xcontent.XContentParserUtils.parseFieldsValue;
/**
* Response to perform an sql query
*/
public class SqlQueryResponse extends ActionResponse implements ToXContentObject {
@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<SqlQueryResponse, Void> PARSER = new ConstructingObjectParser<>("sql", true,
objects -> new SqlQueryResponse(
objects[0] == null ? "" : (String) objects[0],
(List<ColumnInfo>) objects[1],
(List<List<Object>>) objects[2]));
public static final ParseField CURSOR = new ParseField("cursor");
public static final ParseField COLUMNS = new ParseField("columns");
public static final ParseField ROWS = new ParseField("rows");
static {
PARSER.declareString(optionalConstructorArg(), CURSOR);
PARSER.declareObjectArray(optionalConstructorArg(), (p, c) -> ColumnInfo.fromXContent(p), COLUMNS);
PARSER.declareField(constructorArg(), (p, c) -> parseRows(p), ROWS, ValueType.OBJECT_ARRAY);
}
// TODO: Simplify cursor handling
private String cursor;
private List<ColumnInfo> columns;
@ -109,7 +88,7 @@ public class SqlQueryResponse extends ActionResponse implements ToXContentObject
int columnCount = in.readVInt();
List<ColumnInfo> columns = new ArrayList<>(columnCount);
for (int c = 0; c < columnCount; c++) {
columns.add(new ColumnInfo(in));
columns.add(readColumnInfo(in));
}
this.columns = unmodifiableList(columns);
} else {
@ -139,7 +118,7 @@ public class SqlQueryResponse extends ActionResponse implements ToXContentObject
out.writeBoolean(true);
out.writeVInt(columns.size());
for (ColumnInfo column : columns) {
column.writeTo(out);
writeColumnInfo(out, column);
}
}
out.writeVInt(rows.size());
@ -155,7 +134,7 @@ public class SqlQueryResponse extends ActionResponse implements ToXContentObject
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
AbstractSqlRequest.Mode mode = AbstractSqlRequest.Mode.fromString(params.param("mode"));
Mode mode = Mode.fromString(params.param("mode"));
builder.startObject();
{
if (columns != null) {
@ -187,8 +166,8 @@ public class SqlQueryResponse extends ActionResponse implements ToXContentObject
/**
* Serializes the provided value in SQL-compatible way based on the client mode
*/
public static XContentBuilder value(XContentBuilder builder, AbstractSqlRequest.Mode mode, Object value) throws IOException {
if (mode == AbstractSqlRequest.Mode.JDBC && value instanceof ReadableDateTime) {
public static XContentBuilder value(XContentBuilder builder, Mode mode, Object value) throws IOException {
if (mode == Mode.JDBC && value instanceof ReadableDateTime) {
// JDBC cannot parse dates in string format
builder.value(((ReadableDateTime) value).getMillis());
} else {
@ -197,34 +176,33 @@ public class SqlQueryResponse extends ActionResponse implements ToXContentObject
return builder;
}
public static SqlQueryResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
public static ColumnInfo readColumnInfo(StreamInput in) throws IOException {
String table = in.readString();
String name = in.readString();
String esType = in.readString();
JDBCType jdbcType;
int displaySize;
if (in.readBoolean()) {
jdbcType = JDBCType.valueOf(in.readVInt());
displaySize = in.readVInt();
} else {
jdbcType = null;
displaySize = 0;
}
return new ColumnInfo(table, name, esType, jdbcType, displaySize);
}
public static List<List<Object>> parseRows(XContentParser parser) throws IOException {
List<List<Object>> list = new ArrayList<>();
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
if (parser.currentToken() == XContentParser.Token.START_ARRAY) {
list.add(parseRow(parser));
} else {
throw new IllegalStateException("expected start array but got [" + parser.currentToken() + "]");
}
public static void writeColumnInfo(StreamOutput out, ColumnInfo columnInfo) throws IOException {
out.writeString(columnInfo.table());
out.writeString(columnInfo.name());
out.writeString(columnInfo.esType());
if (columnInfo.jdbcType() != null) {
out.writeBoolean(true);
out.writeVInt(columnInfo.jdbcType().getVendorTypeNumber());
out.writeVInt(columnInfo.displaySize());
} else {
out.writeBoolean(false);
}
return list;
}
public static List<Object> parseRow(XContentParser parser) throws IOException {
List<Object> list = new ArrayList<>();
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
if (parser.currentToken().isValue()) {
list.add(parseFieldsValue(parser));
} else if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
list.add(null);
} else {
throw new IllegalStateException("expected value but got [" + parser.currentToken() + "]");
}
}
return list;
}
@Override

View File

@ -10,8 +10,11 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import java.io.IOException;
import java.util.List;
@ -56,4 +59,14 @@ public class SqlTranslateRequest extends AbstractSqlQueryRequest {
request.mode(mode);
return request;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
// This is needed just to test parsing of SqlTranslateRequest, so we can reuse SqlQuerySerialization
return new org.elasticsearch.xpack.sql.proto.SqlQueryRequest(mode(), query(), params(), timeZone(), fetchSize(),
requestTimeout(), pageTimeout(), filter(), null).toXContent(builder, params);
}
}

View File

@ -9,27 +9,25 @@ import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.Protocol;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_FETCH_SIZE;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_PAGE_TIMEOUT;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_REQUEST_TIMEOUT;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_TIME_ZONE;
/**
* Builder for the request for the sql action for translating SQL queries into ES requests
*/
public class SqlTranslateRequestBuilder extends ActionRequestBuilder<SqlTranslateRequest, SqlTranslateResponse,
SqlTranslateRequestBuilder> {
public SqlTranslateRequestBuilder(ElasticsearchClient client, SqlTranslateAction action) {
this(client, action, AbstractSqlRequest.Mode.PLAIN, null, null, Collections.emptyList(), DEFAULT_TIME_ZONE, DEFAULT_FETCH_SIZE,
DEFAULT_REQUEST_TIMEOUT, DEFAULT_PAGE_TIMEOUT);
this(client, action, Mode.PLAIN, null, null, Collections.emptyList(), Protocol.TIME_ZONE, Protocol.FETCH_SIZE,
Protocol.REQUEST_TIMEOUT, Protocol.PAGE_TIMEOUT);
}
public SqlTranslateRequestBuilder(ElasticsearchClient client, SqlTranslateAction action, AbstractSqlRequest.Mode mode, String query,
public SqlTranslateRequestBuilder(ElasticsearchClient client, SqlTranslateAction action, Mode mode, String query,
QueryBuilder filter, List<SqlTypedParamValue> params, TimeZone timeZone, int fetchSize,
TimeValue requestTimeout, TimeValue pageTimeout) {
super(client, action, new SqlTranslateRequest(mode, query, params, filter, timeZone, fetchSize, requestTimeout, pageTimeout));

View File

@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import java.util.Objects;
/**
* Base request for all SQL-related requests for JDBC/CLI client
* <p>
* Contains information about the client mode that can be used to generate different responses based on the caller type.
*/
public abstract class AbstractSqlRequest implements ToXContentFragment {
private final Mode mode;
protected AbstractSqlRequest(Mode mode) {
this.mode = mode;
}
public Mode mode() {
return mode;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractSqlRequest that = (AbstractSqlRequest) o;
return mode == that.mode;
}
@Override
public int hashCode() {
return Objects.hash(mode);
}
}

View File

@ -3,14 +3,11 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.plugin;
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -26,16 +23,16 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optiona
/**
* Information about a column returned with first query response
*/
public final class ColumnInfo implements Writeable, ToXContentObject {
public class ColumnInfo implements ToXContentObject {
private static final ConstructingObjectParser<ColumnInfo, Void> PARSER =
new ConstructingObjectParser<>("column_info", true, objects ->
new ColumnInfo(
objects[0] == null ? "" : (String) objects[0],
(String) objects[1],
(String) objects[2],
objects[3] == null ? null : JDBCType.valueOf((int) objects[3]),
objects[4] == null ? 0 : (int) objects[4]));
new ConstructingObjectParser<>("column_info", true, objects ->
new ColumnInfo(
objects[0] == null ? "" : (String) objects[0],
(String) objects[1],
(String) objects[2],
objects[3] == null ? null : JDBCType.valueOf((int) objects[3]),
objects[4] == null ? 0 : (int) objects[4]));
private static final ParseField TABLE = new ParseField("table");
private static final ParseField NAME = new ParseField("name");
@ -74,33 +71,6 @@ public final class ColumnInfo implements Writeable, ToXContentObject {
this.displaySize = 0;
}
ColumnInfo(StreamInput in) throws IOException {
table = in.readString();
name = in.readString();
esType = in.readString();
if (in.readBoolean()) {
jdbcType = JDBCType.valueOf(in.readVInt());
displaySize = in.readVInt();
} else {
jdbcType = null;
displaySize = 0;
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(table);
out.writeString(name);
out.writeString(esType);
if (jdbcType != null) {
out.writeBoolean(true);
out.writeVInt(jdbcType.getVendorTypeNumber());
out.writeVInt(displaySize);
} else {
out.writeBoolean(false);
}
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
@ -162,10 +132,10 @@ public final class ColumnInfo implements Writeable, ToXContentObject {
if (o == null || getClass() != o.getClass()) return false;
ColumnInfo that = (ColumnInfo) o;
return displaySize == that.displaySize &&
Objects.equals(table, that.table) &&
Objects.equals(name, that.name) &&
Objects.equals(esType, that.esType) &&
jdbcType == that.jdbcType;
Objects.equals(table, that.table) &&
Objects.equals(name, that.name) &&
Objects.equals(esType, that.esType) &&
jdbcType == that.jdbcType;
}
@Override

View File

@ -0,0 +1,107 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.Build;
import org.elasticsearch.Version;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import java.util.Objects;
/**
* Main (/) response for JDBC/CLI client
*/
public class MainResponse {
private String nodeName;
// TODO: Add parser for Version
private Version version;
private String clusterName;
private String clusterUuid;
// TODO: Add parser for Build
private Build build;
private MainResponse() {
}
public MainResponse(String nodeName, Version version, String clusterName, String clusterUuid, Build build) {
this.nodeName = nodeName;
this.version = version;
this.clusterName = clusterName;
this.clusterUuid = clusterUuid;
this.build = build;
}
public String getNodeName() {
return nodeName;
}
public Version getVersion() {
return version;
}
public String getClusterName() {
return clusterName;
}
public String getClusterUuid() {
return clusterUuid;
}
public Build getBuild() {
return build;
}
private static final ObjectParser<MainResponse, Void> PARSER = new ObjectParser<>(MainResponse.class.getName(), true,
MainResponse::new);
static {
PARSER.declareString((response, value) -> response.nodeName = value, new ParseField("name"));
PARSER.declareString((response, value) -> response.clusterName = value, new ParseField("cluster_name"));
PARSER.declareString((response, value) -> response.clusterUuid = value, new ParseField("cluster_uuid"));
PARSER.declareString((response, value) -> {
}, new ParseField("tagline"));
PARSER.declareObject((response, value) -> {
final String buildFlavor = (String) value.get("build_flavor");
final String buildType = (String) value.get("build_type");
response.build =
new Build(
buildFlavor == null ? Build.Flavor.UNKNOWN : Build.Flavor.fromDisplayName(buildFlavor),
buildType == null ? Build.Type.UNKNOWN : Build.Type.fromDisplayName(buildType),
(String) value.get("build_hash"),
(String) value.get("build_date"),
(boolean) value.get("build_snapshot"));
response.version = Version.fromString((String) value.get("number"));
}, (parser, context) -> parser.map(), new ParseField("version"));
}
public static MainResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MainResponse other = (MainResponse) o;
return Objects.equals(nodeName, other.nodeName) &&
Objects.equals(version, other.version) &&
Objects.equals(clusterUuid, other.clusterUuid) &&
Objects.equals(build, other.build) &&
Objects.equals(clusterName, other.clusterName);
}
@Override
public int hashCode() {
return Objects.hash(nodeName, version, clusterUuid, build, clusterName);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.proto;
import java.util.Locale;
/**
* SQL protocol mode
*/
public enum Mode {
PLAIN,
JDBC;
public static Mode fromString(String mode) {
if (mode == null) {
return PLAIN;
}
return Mode.valueOf(mode.toUpperCase(Locale.ROOT));
}
@Override
public String toString() {
return this.name().toLowerCase(Locale.ROOT);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.common.unit.TimeValue;
import java.util.TimeZone;
/**
* Sql protocol defaults and end-points shared between JDBC and REST protocol implementations
*/
public final class Protocol {
public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("UTC");
/**
* Global choice for the default fetch size.
*/
public static final int FETCH_SIZE = 1000;
public static final TimeValue REQUEST_TIMEOUT = TimeValue.timeValueSeconds(90);
public static final TimeValue PAGE_TIMEOUT = TimeValue.timeValueSeconds(45);
/**
* SQL-related endpoints
*/
public static final String CLEAR_CURSOR_REST_ENDPOINT = "/_xpack/sql/close";
public static final String SQL_QUERY_REST_ENDPOINT = "/_xpack/sql";
}

View File

@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Objects;
/**
* Request to clean all SQL resources associated with the cursor for JDBC/CLI client
*/
public class SqlClearCursorRequest extends AbstractSqlRequest {
private final String cursor;
public SqlClearCursorRequest(Mode mode, String cursor) {
super(mode);
this.cursor = cursor;
}
public String getCursor() {
return cursor;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
SqlClearCursorRequest that = (SqlClearCursorRequest) o;
return Objects.equals(cursor, that.cursor);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), cursor);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("cursor", cursor);
return builder;
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import java.util.Objects;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
/**
* Response to the request to clean all SQL resources associated with the cursor for JDBC/CLI client
*/
public class SqlClearCursorResponse {
public static final ParseField SUCCEEDED = new ParseField("succeeded");
public static final ConstructingObjectParser<SqlClearCursorResponse, Void> PARSER =
new ConstructingObjectParser<>(SqlClearCursorResponse.class.getName(), true,
objects -> new SqlClearCursorResponse(objects[0] == null ? false : (boolean) objects[0]));
static {
PARSER.declareBoolean(optionalConstructorArg(), SUCCEEDED);
}
private final boolean succeeded;
public SqlClearCursorResponse(boolean succeeded) {
this.succeeded = succeeded;
}
/**
* @return Whether the attempt to clear a cursor was successful.
*/
public boolean isSucceeded() {
return succeeded;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SqlClearCursorResponse response = (SqlClearCursorResponse) o;
return succeeded == response.succeeded;
}
@Override
public int hashCode() {
return Objects.hash(succeeded);
}
public static SqlClearCursorResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
}

View File

@ -0,0 +1,172 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;
/**
* Sql query request for JDBC/CLI client
*/
public class SqlQueryRequest extends AbstractSqlRequest {
@Nullable
private final String cursor;
private final String query;
private final TimeZone timeZone;
private final int fetchSize;
private final TimeValue requestTimeout;
private final TimeValue pageTimeout;
@Nullable
private final ToXContent filter;
private final List<SqlTypedParamValue> params;
public SqlQueryRequest(Mode mode, String query, List<SqlTypedParamValue> params, TimeZone timeZone,
int fetchSize, TimeValue requestTimeout, TimeValue pageTimeout, ToXContent filter, String cursor) {
super(mode);
this.query = query;
this.params = params;
this.timeZone = timeZone;
this.fetchSize = fetchSize;
this.requestTimeout = requestTimeout;
this.pageTimeout = pageTimeout;
this.filter = filter;
this.cursor = cursor;
}
public SqlQueryRequest(Mode mode, String query, List<SqlTypedParamValue> params, ToXContent filter, TimeZone timeZone,
int fetchSize, TimeValue requestTimeout, TimeValue pageTimeout) {
this(mode, query, params, timeZone, fetchSize, requestTimeout, pageTimeout, filter, null);
}
public SqlQueryRequest(Mode mode, String cursor, TimeValue requestTimeout, TimeValue pageTimeout) {
this(mode, "", Collections.emptyList(), Protocol.TIME_ZONE, Protocol.FETCH_SIZE, requestTimeout, pageTimeout, null, cursor);
}
/**
* The key that must be sent back to SQL to access the next page of
* results.
*/
public String cursor() {
return cursor;
}
/**
* Text of SQL query
*/
public String query() {
return query;
}
/**
* An optional list of parameters if the SQL query is parametrized
*/
public List<SqlTypedParamValue> params() {
return params;
}
/**
* The client's time zone
*/
public TimeZone timeZone() {
return timeZone;
}
/**
* Hint about how many results to fetch at once.
*/
public int fetchSize() {
return fetchSize;
}
/**
* The timeout specified on the search request
*/
public TimeValue requestTimeout() {
return requestTimeout;
}
/**
* The scroll timeout
*/
public TimeValue pageTimeout() {
return pageTimeout;
}
/**
* An optional Query DSL defined query that can added as a filter on the top of the SQL query
*/
public ToXContent filter() {
return filter;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
SqlQueryRequest that = (SqlQueryRequest) o;
return fetchSize == that.fetchSize &&
Objects.equals(query, that.query) &&
Objects.equals(params, that.params) &&
Objects.equals(timeZone, that.timeZone) &&
Objects.equals(requestTimeout, that.requestTimeout) &&
Objects.equals(pageTimeout, that.pageTimeout) &&
Objects.equals(filter, that.filter) &&
Objects.equals(cursor, that.cursor);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), query, timeZone, fetchSize, requestTimeout, pageTimeout, filter, cursor);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (query != null) {
builder.field("query", query);
}
if (this.params.isEmpty() == false) {
builder.startArray("params");
for (SqlTypedParamValue val : this.params) {
val.toXContent(builder, params);
}
builder.endArray();
}
if (timeZone != null) {
builder.field("time_zone", timeZone.getID());
}
if (fetchSize != Protocol.FETCH_SIZE) {
builder.field("fetch_size", fetchSize);
}
if (requestTimeout != Protocol.REQUEST_TIMEOUT) {
builder.field("request_timeout", requestTimeout.getStringRep());
}
if (pageTimeout != Protocol.PAGE_TIMEOUT) {
builder.field("page_timeout", pageTimeout.getStringRep());
}
if (filter != null) {
builder.field("filter");
filter.toXContent(builder, params);
}
if (cursor != null) {
builder.field("cursor", cursor);
}
return builder;
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
import static org.elasticsearch.common.xcontent.XContentParserUtils.parseFieldsValue;
/**
* Response to perform an sql query for JDBC/CLI client
*/
public class SqlQueryResponse {
@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<SqlQueryResponse, Void> PARSER = new ConstructingObjectParser<>("sql", true,
objects -> new SqlQueryResponse(
objects[0] == null ? "" : (String) objects[0],
(List<ColumnInfo>) objects[1],
(List<List<Object>>) objects[2]));
public static final ParseField CURSOR = new ParseField("cursor");
public static final ParseField COLUMNS = new ParseField("columns");
public static final ParseField ROWS = new ParseField("rows");
static {
PARSER.declareString(optionalConstructorArg(), CURSOR);
PARSER.declareObjectArray(optionalConstructorArg(), (p, c) -> ColumnInfo.fromXContent(p), COLUMNS);
PARSER.declareField(constructorArg(), (p, c) -> parseRows(p), ROWS, ValueType.OBJECT_ARRAY);
}
// TODO: Simplify cursor handling
private final String cursor;
private final List<ColumnInfo> columns;
// TODO investigate reusing Page here - it probably is much more efficient
private final List<List<Object>> rows;
public SqlQueryResponse(String cursor, @Nullable List<ColumnInfo> columns, List<List<Object>> rows) {
this.cursor = cursor;
this.columns = columns;
this.rows = rows;
}
/**
* The key that must be sent back to SQL to access the next page of
* results. If equal to "" then there is no next page.
*/
public String cursor() {
return cursor;
}
public long size() {
return rows.size();
}
public List<ColumnInfo> columns() {
return columns;
}
public List<List<Object>> rows() {
return rows;
}
public static SqlQueryResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
public static List<List<Object>> parseRows(XContentParser parser) throws IOException {
List<List<Object>> list = new ArrayList<>();
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
if (parser.currentToken() == XContentParser.Token.START_ARRAY) {
list.add(parseRow(parser));
} else {
throw new IllegalStateException("expected start array but got [" + parser.currentToken() + "]");
}
}
return list;
}
public static List<Object> parseRow(XContentParser parser) throws IOException {
List<Object> list = new ArrayList<>();
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
if (parser.currentToken().isValue()) {
list.add(parseFieldsValue(parser));
} else if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
list.add(null);
} else {
throw new IllegalStateException("expected value but got [" + parser.currentToken() + "]");
}
}
return list;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SqlQueryResponse that = (SqlQueryResponse) o;
return Objects.equals(cursor, that.cursor) &&
Objects.equals(columns, that.columns) &&
Objects.equals(rows, that.rows);
}
@Override
public int hashCode() {
return Objects.hash(cursor, columns, rows);
}
}

View File

@ -3,12 +3,9 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.plugin;
package org.elasticsearch.xpack.sql.proto;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
@ -25,12 +22,12 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constru
/**
* Represent a strongly typed parameter value
*/
public class SqlTypedParamValue implements ToXContentObject, Writeable {
public class SqlTypedParamValue implements ToXContentObject {
private static final ConstructingObjectParser<SqlTypedParamValue, Void> PARSER =
new ConstructingObjectParser<>("params", true, objects ->
new SqlTypedParamValue(
objects[0],
DataType.fromEsType((String) objects[1])));
DataType.fromEsType((String) objects[1]), objects[0]
));
private static final ParseField VALUE = new ParseField("value");
private static final ParseField TYPE = new ParseField("type");
@ -43,7 +40,7 @@ public class SqlTypedParamValue implements ToXContentObject, Writeable {
public final Object value;
public final DataType dataType;
public SqlTypedParamValue(Object value, DataType dataType) {
public SqlTypedParamValue(DataType dataType, Object value) {
this.value = value;
this.dataType = dataType;
}
@ -61,17 +58,6 @@ public class SqlTypedParamValue implements ToXContentObject, Writeable {
return PARSER.apply(parser, null);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeEnum(dataType);
out.writeGenericValue(value);
}
public SqlTypedParamValue(StreamInput in) throws IOException {
dataType = in.readEnum(DataType.class);
value = in.readGenericValue();
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -94,4 +80,4 @@ public class SqlTypedParamValue implements ToXContentObject, Writeable {
public String toString() {
return String.valueOf(value) + "[" + dataType + "]";
}
}
}

View File

@ -8,17 +8,18 @@ package org.elasticsearch.xpack.sql.plugin;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractSerializingTestCase;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.junit.Before;
import java.io.IOException;
import java.util.function.Consumer;
public class SqlClearCursorRequestTests extends AbstractSerializingTestCase<SqlClearCursorRequest> {
public AbstractSqlRequest.Mode testMode;
public Mode testMode;
@Before
public void setup() {
testMode = randomFrom(AbstractSqlRequest.Mode.values());
testMode = randomFrom(Mode.values());
}
@Override
@ -40,7 +41,7 @@ public class SqlClearCursorRequestTests extends AbstractSerializingTestCase<SqlC
protected SqlClearCursorRequest mutateInstance(SqlClearCursorRequest instance) throws IOException {
@SuppressWarnings("unchecked")
Consumer<SqlClearCursorRequest> mutator = randomFrom(
request -> request.mode(randomValueOtherThan(request.mode(), () -> randomFrom(AbstractSqlRequest.Mode.values()))),
request -> request.mode(randomValueOtherThan(request.mode(), () -> randomFrom(Mode.values()))),
request -> request.setCursor(randomValueOtherThan(request.getCursor(), SqlQueryResponseTests::randomStringCursor))
);
SqlClearCursorRequest newRequest = new SqlClearCursorRequest(instance.mode(), instance.getCursor());

View File

@ -27,6 +27,8 @@ public class SqlClearCursorResponseTests extends AbstractStreamableXContentTestC
@Override
protected SqlClearCursorResponse doParseInstance(XContentParser parser) {
return SqlClearCursorResponse.fromXContent(parser);
org.elasticsearch.xpack.sql.proto.SqlClearCursorResponse response =
org.elasticsearch.xpack.sql.proto.SqlClearCursorResponse.fromXContent(parser);
return new SqlClearCursorResponse(response.isSucceeded());
}
}

View File

@ -14,6 +14,8 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.SearchModule;
import org.elasticsearch.test.AbstractSerializingTestCase;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.type.DataType;
import org.junit.Before;
@ -28,11 +30,11 @@ import static org.elasticsearch.xpack.sql.plugin.SqlTestUtils.randomFilterOrNull
public class SqlQueryRequestTests extends AbstractSerializingTestCase<SqlQueryRequest> {
public AbstractSqlRequest.Mode testMode;
public Mode testMode;
@Before
public void setup() {
testMode = randomFrom(AbstractSqlRequest.Mode.values());
testMode = randomFrom(Mode.values());
}
@Override
@ -63,11 +65,11 @@ public class SqlQueryRequestTests extends AbstractSerializingTestCase<SqlQueryRe
List<SqlTypedParamValue> arr = new ArrayList<>(len);
for (int i = 0; i < len; i++) {
@SuppressWarnings("unchecked") Supplier<SqlTypedParamValue> supplier = randomFrom(
() -> new SqlTypedParamValue(randomBoolean(), DataType.BOOLEAN),
() -> new SqlTypedParamValue(randomLong(), DataType.LONG),
() -> new SqlTypedParamValue(randomDouble(), DataType.DOUBLE),
() -> new SqlTypedParamValue(null, DataType.NULL),
() -> new SqlTypedParamValue(randomAlphaOfLength(10), DataType.KEYWORD)
() -> new SqlTypedParamValue(DataType.BOOLEAN, randomBoolean()),
() -> new SqlTypedParamValue(DataType.LONG, randomLong()),
() -> new SqlTypedParamValue(DataType.DOUBLE, randomDouble()),
() -> new SqlTypedParamValue(DataType.NULL, null),
() -> new SqlTypedParamValue(DataType.KEYWORD, randomAlphaOfLength(10))
);
arr.add(supplier.get());
}
@ -93,7 +95,7 @@ public class SqlQueryRequestTests extends AbstractSerializingTestCase<SqlQueryRe
protected SqlQueryRequest mutateInstance(SqlQueryRequest instance) {
@SuppressWarnings("unchecked")
Consumer<SqlQueryRequest> mutator = randomFrom(
request -> request.mode(randomValueOtherThan(request.mode(), () -> randomFrom(AbstractSqlRequest.Mode.values()))),
request -> request.mode(randomValueOtherThan(request.mode(), () -> randomFrom(Mode.values()))),
request -> request.query(randomValueOtherThan(request.query(), () -> randomAlphaOfLength(5))),
request -> request.params(randomValueOtherThan(request.params(), this::randomParameters)),
request -> request.timeZone(randomValueOtherThan(request.timeZone(), ESTestCase::randomTimeZone)),

View File

@ -13,6 +13,7 @@ import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import java.io.IOException;
import java.sql.JDBCType;
@ -114,6 +115,8 @@ public class SqlQueryResponseTests extends AbstractStreamableXContentTestCase<Sq
@Override
protected SqlQueryResponse doParseInstance(XContentParser parser) {
return SqlQueryResponse.fromXContent(parser);
org.elasticsearch.xpack.sql.proto.SqlQueryResponse response =
org.elasticsearch.xpack.sql.proto.SqlQueryResponse.fromXContent(parser);
return new SqlQueryResponse(response.cursor(), response.columns(), response.rows());
}
}

View File

@ -14,6 +14,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.SearchModule;
import org.elasticsearch.test.AbstractSerializingTestCase;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.junit.Before;
import java.io.IOException;
@ -25,11 +26,11 @@ import static org.elasticsearch.xpack.sql.plugin.SqlTestUtils.randomFilterOrNull
public class SqlTranslateRequestTests extends AbstractSerializingTestCase<SqlTranslateRequest> {
public AbstractSqlRequest.Mode testMode;
public Mode testMode;
@Before
public void setup() {
testMode = randomFrom(AbstractSqlRequest.Mode.values());
testMode = randomFrom(Mode.values());
}
@Override
@ -71,7 +72,7 @@ public class SqlTranslateRequestTests extends AbstractSerializingTestCase<SqlTra
request -> request.query(randomValueOtherThan(request.query(), () -> randomAlphaOfLength(5))),
request -> request.timeZone(randomValueOtherThan(request.timeZone(), ESTestCase::randomTimeZone)),
request -> request.fetchSize(randomValueOtherThan(request.fetchSize(), () -> between(1, Integer.MAX_VALUE))),
request -> request.requestTimeout(randomValueOtherThan(request.requestTimeout(), () -> randomTV())),
request -> request.requestTimeout(randomValueOtherThan(request.requestTimeout(), this::randomTV)),
request -> request.filter(randomValueOtherThan(request.filter(),
() -> request.filter() == null ? randomFilter(random()) : randomFilterOrNull(random())))
);

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.sql.client;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
@ -22,13 +21,14 @@ import org.elasticsearch.xpack.sql.client.shared.ClientException;
import org.elasticsearch.xpack.sql.client.shared.ConnectionConfiguration;
import org.elasticsearch.xpack.sql.client.shared.JreHttpUrlConnection;
import org.elasticsearch.xpack.sql.client.shared.JreHttpUrlConnection.ResponseOrException;
import org.elasticsearch.xpack.sql.plugin.AbstractSqlRequest;
import org.elasticsearch.xpack.sql.plugin.SqlClearCursorAction;
import org.elasticsearch.xpack.sql.plugin.SqlClearCursorRequest;
import org.elasticsearch.xpack.sql.plugin.SqlClearCursorResponse;
import org.elasticsearch.xpack.sql.plugin.SqlQueryAction;
import org.elasticsearch.xpack.sql.plugin.SqlQueryRequest;
import org.elasticsearch.xpack.sql.plugin.SqlQueryResponse;
import org.elasticsearch.xpack.sql.proto.AbstractSqlRequest;
import org.elasticsearch.xpack.sql.proto.MainResponse;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.Protocol;
import org.elasticsearch.xpack.sql.proto.SqlClearCursorRequest;
import org.elasticsearch.xpack.sql.proto.SqlClearCursorResponse;
import org.elasticsearch.xpack.sql.proto.SqlQueryRequest;
import org.elasticsearch.xpack.sql.proto.SqlQueryResponse;
import java.io.IOException;
import java.io.InputStream;
@ -50,7 +50,7 @@ public class HttpClient {
private final ConnectionConfiguration cfg;
public HttpClient(ConnectionConfiguration cfg) throws SQLException {
public HttpClient(ConnectionConfiguration cfg) {
this.cfg = cfg;
}
@ -66,26 +66,25 @@ public class HttpClient {
public SqlQueryResponse queryInit(String query, int fetchSize) throws SQLException {
// TODO allow customizing the time zone - this is what session set/reset/get should be about
SqlQueryRequest sqlRequest = new SqlQueryRequest(AbstractSqlRequest.Mode.PLAIN, query, Collections.emptyList(), null,
SqlQueryRequest sqlRequest = new SqlQueryRequest(Mode.PLAIN, query, Collections.emptyList(), null,
TimeZone.getTimeZone("UTC"), fetchSize, TimeValue.timeValueMillis(cfg.queryTimeout()),
TimeValue.timeValueMillis(cfg.pageTimeout()), ""
);
TimeValue.timeValueMillis(cfg.pageTimeout()));
return query(sqlRequest);
}
public SqlQueryResponse query(SqlQueryRequest sqlRequest) throws SQLException {
return post(SqlQueryAction.REST_ENDPOINT, sqlRequest, SqlQueryResponse::fromXContent);
return post(Protocol.SQL_QUERY_REST_ENDPOINT, sqlRequest, SqlQueryResponse::fromXContent);
}
public SqlQueryResponse nextPage(String cursor) throws SQLException {
SqlQueryRequest sqlRequest = new SqlQueryRequest();
sqlRequest.cursor(cursor);
return post(SqlQueryAction.REST_ENDPOINT, sqlRequest, SqlQueryResponse::fromXContent);
SqlQueryRequest sqlRequest = new SqlQueryRequest(Mode.PLAIN, cursor, TimeValue.timeValueMillis(cfg.queryTimeout()),
TimeValue.timeValueMillis(cfg.pageTimeout()));
return post(Protocol.SQL_QUERY_REST_ENDPOINT, sqlRequest, SqlQueryResponse::fromXContent);
}
public boolean queryClose(String cursor) throws SQLException {
SqlClearCursorResponse response = post(SqlClearCursorAction.REST_ENDPOINT,
new SqlClearCursorRequest(AbstractSqlRequest.Mode.PLAIN, cursor),
SqlClearCursorResponse response = post(Protocol.CLEAR_CURSOR_REST_ENDPOINT,
new SqlClearCursorRequest(Mode.PLAIN, cursor),
SqlClearCursorResponse::fromXContent);
return response.isSucceeded();
}
@ -167,4 +166,4 @@ public class HttpClient {
throw new ClientException("Cannot parse response", ex);
}
}
}
}

View File

@ -17,7 +17,7 @@ import org.elasticsearch.xpack.sql.optimizer.Optimizer;
import org.elasticsearch.xpack.sql.plan.physical.EsQueryExec;
import org.elasticsearch.xpack.sql.planner.Planner;
import org.elasticsearch.xpack.sql.planner.PlanningException;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.session.Configuration;
import org.elasticsearch.xpack.sql.session.Cursor;
import org.elasticsearch.xpack.sql.session.RowSet;

View File

@ -8,7 +8,7 @@ package org.elasticsearch.xpack.sql.parser;
import org.antlr.v4.runtime.Token;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SingleStatementContext;
import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import java.util.Map;

View File

@ -33,7 +33,7 @@ import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumns;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTableTypes;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTables;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTypes;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.util.StringUtils;
@ -190,4 +190,4 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
public Object visitSysTableTypes(SysTableTypesContext ctx) {
return new SysTableTypes(source(ctx));
}
}
}

View File

@ -76,7 +76,7 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringLiteralContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringQueryContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SubqueryExpressionContext;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.DataTypes;
@ -516,4 +516,4 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
return params.get(token);
}
}
}

View File

@ -41,7 +41,7 @@ import org.elasticsearch.xpack.sql.plan.logical.Project;
import org.elasticsearch.xpack.sql.plan.logical.SubQueryAlias;
import org.elasticsearch.xpack.sql.plan.logical.UnresolvedRelation;
import org.elasticsearch.xpack.sql.plan.logical.With;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.session.EmptyExecutable;
import org.elasticsearch.xpack.sql.type.DataType;

View File

@ -26,7 +26,7 @@ import org.apache.logging.log4j.Logger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import java.util.Arrays;
import java.util.BitSet;

View File

@ -12,23 +12,25 @@ import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.Protocol;
import java.io.IOException;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.xpack.sql.plugin.SqlClearCursorAction.REST_ENDPOINT;
public class RestSqlClearCursorAction extends BaseRestHandler {
public RestSqlClearCursorAction(Settings settings, RestController controller) {
super(settings);
controller.registerHandler(POST, REST_ENDPOINT, this);
controller.registerHandler(POST, Protocol.CLEAR_CURSOR_REST_ENDPOINT, this);
}
@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
SqlClearCursorRequest sqlRequest;
try (XContentParser parser = request.contentOrSourceParamParser()) {
sqlRequest = SqlClearCursorRequest.fromXContent(parser, AbstractSqlRequest.Mode.fromString(request.param("mode")));
sqlRequest = SqlClearCursorRequest.fromXContent(parser, Mode.fromString(request.param("mode")));
}
return channel -> client.executeLocally(SqlClearCursorAction.INSTANCE, sqlRequest, new RestToXContentListener<>(channel));
}
@ -37,4 +39,4 @@ public class RestSqlClearCursorAction extends BaseRestHandler {
public String getName() {
return "sql_translate_action";
}
}
}

View File

@ -18,6 +18,8 @@ import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestResponseListener;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.proto.Protocol;
import org.elasticsearch.xpack.sql.session.Cursor;
import org.elasticsearch.xpack.sql.session.Cursors;
@ -31,15 +33,15 @@ public class RestSqlQueryAction extends BaseRestHandler {
public RestSqlQueryAction(Settings settings, RestController controller) {
super(settings);
controller.registerHandler(GET, SqlQueryAction.REST_ENDPOINT, this);
controller.registerHandler(POST, SqlQueryAction.REST_ENDPOINT, this);
controller.registerHandler(GET, Protocol.SQL_QUERY_REST_ENDPOINT, this);
controller.registerHandler(POST, Protocol.SQL_QUERY_REST_ENDPOINT, this);
}
@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
SqlQueryRequest sqlRequest;
try (XContentParser parser = request.contentOrSourceParamParser()) {
sqlRequest = SqlQueryRequest.fromXContent(parser, AbstractSqlRequest.Mode.fromString(request.param("mode")));
sqlRequest = SqlQueryRequest.fromXContent(parser,Mode.fromString(request.param("mode")));
}
/*

View File

@ -12,6 +12,7 @@ import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
import org.elasticsearch.xpack.sql.proto.Mode;
import java.io.IOException;
@ -32,7 +33,7 @@ public class RestSqlTranslateAction extends BaseRestHandler {
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
SqlTranslateRequest sqlRequest;
try (XContentParser parser = request.contentOrSourceParamParser()) {
sqlRequest = SqlTranslateRequest.fromXContent(parser, AbstractSqlRequest.Mode.fromString(request.param("mode")));
sqlRequest = SqlTranslateRequest.fromXContent(parser, Mode.fromString(request.param("mode")));
}
return channel -> client.executeLocally(SqlTranslateAction.INSTANCE, sqlRequest, new RestToXContentListener<>(channel));
}

View File

@ -5,6 +5,8 @@
*/
package org.elasticsearch.xpack.sql.plugin;
import org.elasticsearch.xpack.sql.proto.Mode;
import java.util.function.Consumer;
/**
@ -12,16 +14,16 @@ import java.util.function.Consumer;
*/
public class SqlLicenseChecker {
private final Consumer<AbstractSqlRequest.Mode> checkIfSqlAllowed;
private final Consumer<Mode> checkIfSqlAllowed;
public SqlLicenseChecker(Consumer<AbstractSqlRequest.Mode> checkIfSqlAllowed) {
public SqlLicenseChecker(Consumer<Mode> checkIfSqlAllowed) {
this.checkIfSqlAllowed = checkIfSqlAllowed;
}
/**
* Throws an ElasticsearchSecurityException if the specified mode is not allowed
*/
public void checkIfSqlAllowed(AbstractSqlRequest.Mode mode) {
public void checkIfSqlAllowed(Mode mode) {
checkIfSqlAllowed.accept(mode);
}
}

View File

@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.plugin;
import org.elasticsearch.common.Strings;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import org.elasticsearch.xpack.sql.session.Cursor;
import org.elasticsearch.xpack.sql.session.Cursors;
import org.elasticsearch.xpack.sql.util.StringUtils;
@ -38,17 +39,17 @@ enum TextFormat {
final CliFormatter formatter;
if (cursor instanceof CliFormatterCursor) {
formatter = ((CliFormatterCursor) cursor).getCliFormatter();
return formatter.formatWithoutHeader(response);
return formatter.formatWithoutHeader(response.rows());
} else {
formatter = new CliFormatter(response);
return formatter.formatWithHeader(response);
formatter = new CliFormatter(response.columns(), response.rows());
return formatter.formatWithHeader(response.columns(), response.rows());
}
}
@Override
Cursor wrapCursor(Cursor oldCursor, SqlQueryResponse response) {
CliFormatter formatter = (oldCursor instanceof CliFormatterCursor) ?
((CliFormatterCursor) oldCursor).getCliFormatter() : new CliFormatter(response);
((CliFormatterCursor) oldCursor).getCliFormatter() : new CliFormatter(response.columns(), response.rows());
return CliFormatterCursor.wrap(super.wrapCursor(oldCursor, response), formatter);
}

View File

@ -16,6 +16,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.sql.execution.PlanExecutor;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import org.elasticsearch.xpack.sql.session.Configuration;
import org.elasticsearch.xpack.sql.session.Cursors;
import org.elasticsearch.xpack.sql.session.RowSet;
@ -26,7 +27,7 @@ import java.util.ArrayList;
import java.util.List;
import static java.util.Collections.unmodifiableList;
import static org.elasticsearch.xpack.sql.plugin.AbstractSqlRequest.Mode.JDBC;
import static org.elasticsearch.xpack.sql.proto.Mode.JDBC;
public class TransportSqlQueryAction extends HandledTransportAction<SqlQueryRequest, SqlQueryResponse> {
private final PlanExecutor planExecutor;

View File

@ -9,16 +9,14 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest;
import org.elasticsearch.xpack.sql.proto.Protocol;
import java.util.TimeZone;
// Typed object holding properties for a given action
// Typed object holding properties for a given action
public class Configuration {
public static final Configuration DEFAULT = new Configuration(TimeZone.getTimeZone("UTC"),
AbstractSqlQueryRequest.DEFAULT_FETCH_SIZE,
AbstractSqlQueryRequest.DEFAULT_REQUEST_TIMEOUT,
AbstractSqlQueryRequest.DEFAULT_PAGE_TIMEOUT,
null);
Protocol.FETCH_SIZE, Protocol.REQUEST_TIMEOUT, Protocol.PAGE_TIMEOUT, null);
private TimeZone timeZone;
private int pageSize;

View File

@ -21,7 +21,7 @@ import org.elasticsearch.xpack.sql.plan.TableIdentifier;
import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
import org.elasticsearch.xpack.sql.plan.physical.PhysicalPlan;
import org.elasticsearch.xpack.sql.planner.Planner;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.rule.RuleExecutor;
import java.util.List;
@ -162,4 +162,4 @@ public class SqlSession {
public Configuration settings() {
return settings;
}
}
}

View File

@ -7,10 +7,10 @@ package org.elasticsearch.xpack.sql.action;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.xpack.sql.plugin.AbstractSqlRequest.Mode;
import org.elasticsearch.xpack.sql.plugin.ColumnInfo;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import org.elasticsearch.xpack.sql.plugin.SqlQueryAction;
import org.elasticsearch.xpack.sql.plugin.SqlQueryResponse;
import org.elasticsearch.xpack.sql.proto.Mode;
import java.sql.JDBCType;

View File

@ -14,7 +14,7 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.SqlException;
import org.elasticsearch.xpack.sql.plugin.CliFormatter;
import org.elasticsearch.xpack.sql.plugin.CliFormatterCursor;
import org.elasticsearch.xpack.sql.plugin.ColumnInfo;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import org.elasticsearch.xpack.sql.plugin.SqlQueryResponse;
import org.elasticsearch.xpack.sql.session.Configuration;
import org.elasticsearch.xpack.sql.session.Cursor;
@ -80,7 +80,8 @@ public class CursorTests extends ESTestCase {
() -> {
SqlQueryResponse response = createRandomSqlResponse();
if (response.columns() != null && response.rows() != null) {
return CliFormatterCursor.wrap(ScrollCursorTests.randomScrollCursor(), new CliFormatter(response));
return CliFormatterCursor.wrap(ScrollCursorTests.randomScrollCursor(),
new CliFormatter(response.columns(), response.rows()));
} else {
return ScrollCursorTests.randomScrollCursor();
}

View File

@ -12,7 +12,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.Sub;
import org.elasticsearch.xpack.sql.expression.predicate.Equals;
import org.elasticsearch.xpack.sql.parser.ParsingException;
import org.elasticsearch.xpack.sql.parser.SqlParser;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Arrays;
@ -28,7 +28,7 @@ public class ParameterTests extends ESTestCase {
public void testSingleParameter() {
Expression expression = new SqlParser().createExpression("a = \n?",
Collections.singletonList(
new SqlTypedParamValue("foo", DataType.KEYWORD)
new SqlTypedParamValue(DataType.KEYWORD, "foo")
));
logger.info(expression);
assertThat(expression, instanceOf(Equals.class));
@ -42,10 +42,10 @@ public class ParameterTests extends ESTestCase {
public void testMultipleParameters() {
Expression expression = new SqlParser().createExpression("(? + ? * ?) - ?", Arrays.asList(
new SqlTypedParamValue(1L, DataType.LONG),
new SqlTypedParamValue(2L, DataType.LONG),
new SqlTypedParamValue(3L, DataType.LONG),
new SqlTypedParamValue(4L, DataType.LONG)
new SqlTypedParamValue(DataType.LONG, 1L),
new SqlTypedParamValue(DataType.LONG, 2L),
new SqlTypedParamValue(DataType.LONG, 3L),
new SqlTypedParamValue(DataType.LONG, 4L)
));
assertThat(expression, instanceOf(Sub.class));
Sub sub = (Sub) expression;
@ -62,9 +62,9 @@ public class ParameterTests extends ESTestCase {
public void testNotEnoughParameters() {
ParsingException ex = expectThrows(ParsingException.class,
() -> new SqlParser().createExpression("(? + ? * ?) - ?", Arrays.asList(
new SqlTypedParamValue(1L, DataType.LONG),
new SqlTypedParamValue(2L, DataType.LONG),
new SqlTypedParamValue(3L, DataType.LONG)
new SqlTypedParamValue(DataType.LONG, 1L),
new SqlTypedParamValue(DataType.LONG, 2L),
new SqlTypedParamValue(DataType.LONG, 3L)
)));
assertThat(ex.getMessage(), containsString("Not enough actual parameters"));
}

View File

@ -9,7 +9,7 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.regex.Like;
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
@ -33,7 +33,7 @@ public class LikeEscapingParsingTests extends ESTestCase {
Expression exp = null;
boolean parameterized = randomBoolean();
if (parameterized) {
exp = parser.createExpression("exp LIKE ?", singletonList(new SqlTypedParamValue(pattern, DataType.KEYWORD)));
exp = parser.createExpression("exp LIKE ?", singletonList(new SqlTypedParamValue(DataType.KEYWORD, pattern)));
} else {
exp = parser.createExpression(String.format(Locale.ROOT, "exp LIKE '%s'", pattern));
}
@ -63,9 +63,9 @@ public class LikeEscapingParsingTests extends ESTestCase {
assertThat(error("'%string' ESCAPE '%'"),
is("line 1:28: Char [%] cannot be used for escaping"));
}
public void testCannotUseStar() {
assertThat(error("'|*string' ESCAPE '|'"),
is("line 1:11: Invalid char [*] found in pattern [|*string] at position 1; use [%] or [_] instead"));
}
}
}

View File

@ -17,7 +17,7 @@ import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexType;
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
import org.elasticsearch.xpack.sql.parser.SqlParser;
import org.elasticsearch.xpack.sql.plan.logical.command.Command;
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
import org.elasticsearch.xpack.sql.session.SchemaRowSet;
import org.elasticsearch.xpack.sql.session.SqlSession;
import org.elasticsearch.xpack.sql.type.DataTypes;
@ -228,7 +228,7 @@ public class SysTablesTests extends ESTestCase {
}
private SqlTypedParamValue param(Object value) {
return new SqlTypedParamValue(value, DataTypes.fromJava(value));
return new SqlTypedParamValue(DataTypes.fromJava(value), value);
}
private Tuple<Command, SqlSession> sql(String sql, List<SqlTypedParamValue> params) {

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.plugin;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import java.sql.JDBCType;
import java.util.Arrays;
@ -23,17 +24,17 @@ public class CliFormatterTests extends ESTestCase {
Arrays.asList(
Arrays.asList("15charwidedata!", 1, 6.888, 12, "rabbit"),
Arrays.asList("dog", 1.7976931348623157E308, 123124.888, 9912, "goat")));
private final CliFormatter formatter = new CliFormatter(firstResponse);
private final CliFormatter formatter = new CliFormatter(firstResponse.columns(), firstResponse.rows());
/**
* Tests for {@link CliFormatter#formatWithHeader(SqlQueryResponse)}, values
* Tests for {@link CliFormatter#formatWithHeader}, values
* of exactly the minimum column size, column names of exactly
* the minimum column size, column headers longer than the
* minimum column size, and values longer than the minimum
* column size.
*/
public void testFormatWithHeader() {
String[] result = formatter.formatWithHeader(firstResponse).split("\n");
String[] result = formatter.formatWithHeader(firstResponse.columns(), firstResponse.rows()).split("\n");
assertThat(result, arrayWithSize(4));
assertEquals(" foo | bar |15charwidename!|superduperwidename!!!| baz ", result[0]);
assertEquals("---------------+----------------------+---------------+---------------------+---------------", result[1]);
@ -42,14 +43,14 @@ public class CliFormatterTests extends ESTestCase {
}
/**
* Tests for {@link CliFormatter#formatWithoutHeader(SqlQueryResponse)} and
* Tests for {@link CliFormatter#formatWithoutHeader} and
* truncation of long columns.
*/
public void testFormatWithoutHeader() {
String[] result = formatter.formatWithoutHeader(new SqlQueryResponse("", null,
String[] result = formatter.formatWithoutHeader(
Arrays.asList(
Arrays.asList("ohnotruncateddata", 4, 1, 77, "wombat"),
Arrays.asList("dog", 2, 123124.888, 9912, "goat")))).split("\n");
Arrays.asList("dog", 2, 123124.888, 9912, "goat"))).split("\n");
assertThat(result, arrayWithSize(2));
assertEquals("ohnotruncatedd~|4 |1 |77 |wombat ", result[0]);
assertEquals("dog |2 |123124.888 |9912 |goat ", result[1]);
@ -59,9 +60,9 @@ public class CliFormatterTests extends ESTestCase {
* Ensure that our estimates are perfect in at least some cases.
*/
public void testEstimateSize() {
assertEquals(formatter.formatWithHeader(firstResponse).length(),
assertEquals(formatter.formatWithHeader(firstResponse.columns(), firstResponse.rows()).length(),
formatter.estimateSize(firstResponse.rows().size() + 2));
assertEquals(formatter.formatWithoutHeader(firstResponse).length(),
assertEquals(formatter.formatWithoutHeader(firstResponse.rows()).length(),
formatter.estimateSize(firstResponse.rows().size()));
}
}

View File

@ -10,6 +10,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.rest.FakeRestRequest;
import org.elasticsearch.xpack.sql.proto.ColumnInfo;
import java.util.ArrayList;
import java.util.List;