diff --git a/dev-tools/checkstyle_suppressions.xml b/dev-tools/checkstyle_suppressions.xml
index 3ac4049d484..3f987ea4f7c 100644
--- a/dev-tools/checkstyle_suppressions.xml
+++ b/dev-tools/checkstyle_suppressions.xml
@@ -10,7 +10,6 @@
-
diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java
index 4ea069e3db8..d581e636b93 100644
--- a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java
+++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java
@@ -59,9 +59,9 @@ import org.elasticsearch.xpack.security.user.ElasticUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
-import org.elasticsearch.xpack.sql.plugin.jdbc.action.JdbcAction;
import org.elasticsearch.xpack.sql.plugin.sql.action.SqlAction;
import org.elasticsearch.xpack.sql.server.cli.CliAction;
+import org.elasticsearch.xpack.sql.server.jdbc.JdbcAction;
import java.util.Arrays;
import java.util.Collections;
diff --git a/sql/build.gradle b/sql/build.gradle
index dfd4887fe7a..333689d889f 100644
--- a/sql/build.gradle
+++ b/sql/build.gradle
@@ -1,10 +1,9 @@
description = 'SQL for Elasticsearch'
-import org.gradle.plugins.ide.eclipse.model.*;
-
subprojects {
apply plugin: 'elasticsearch.build'
+ // NOCOMMIT this is abnormal enough it is worth removing
sourceSets.test.resources.srcDirs = ["src/test/resources", "src/test/java"]
dependencies {
diff --git a/sql/cli-proto/build.gradle b/sql/cli-proto/build.gradle
index cefc4c5c532..98ab91a53d5 100644
--- a/sql/cli-proto/build.gradle
+++ b/sql/cli-proto/build.gradle
@@ -1,9 +1,8 @@
-apply plugin: 'elasticsearch.build'
-
description = 'Request and response objects shared by the cli and ' +
'its backend in :sql:server'
dependencies {
+ compile project(':x-pack-elasticsearch:sql:shared-proto')
testCompile project(':x-pack-elasticsearch:sql:test-utils')
}
@@ -11,3 +10,8 @@ forbiddenApisMain {
// does not depend on core, so only jdk and http signatures should be checked
signaturesURLs = [this.class.getResource('/forbidden/jdk-signatures.txt')]
}
+
+dependencyLicenses {
+ mapping from: /shared-proto.*/, to: 'elasticsearch'
+ ignoreSha 'shared-proto'
+}
diff --git a/sql/cli-proto/licenses/elasticsearch-LICENSE.txt b/sql/cli-proto/licenses/elasticsearch-LICENSE.txt
new file mode 100644
index 00000000000..d6456956733
--- /dev/null
+++ b/sql/cli-proto/licenses/elasticsearch-LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/sql/cli-proto/licenses/elasticsearch-NOTICE.txt b/sql/cli-proto/licenses/elasticsearch-NOTICE.txt
new file mode 100644
index 00000000000..643a060cd05
--- /dev/null
+++ b/sql/cli-proto/licenses/elasticsearch-NOTICE.txt
@@ -0,0 +1,5 @@
+Elasticsearch
+Copyright 2009-2017 Elasticsearch
+
+This product includes software developed by The Apache Software
+Foundation (http://www.apache.org/).
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandRequest.java b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandRequest.java
index 7bef63d37ad..ae73d068931 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandRequest.java
+++ b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandRequest.java
@@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.cli.net.protocol;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
import java.io.DataInput;
import java.io.DataOutput;
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandResponse.java b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandResponse.java
index b33ad674894..4b18df390f6 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandResponse.java
+++ b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandResponse.java
@@ -7,6 +7,8 @@ package org.elasticsearch.xpack.sql.cli.net.protocol;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import java.io.DataInput;
import java.io.DataOutput;
@@ -26,7 +28,7 @@ public class CommandResponse extends Response {
this.data = data;
}
- CommandResponse(DataInput in) throws IOException {
+ CommandResponse(Request request, DataInput in) throws IOException {
serverTimeQueryReceived = in.readLong();
serverTimeResponseSent = in.readLong();
requestId = in.readUTF();
@@ -34,7 +36,7 @@ public class CommandResponse extends Response {
}
@Override
- void write(int clientVersion, DataOutput out) throws IOException {
+ protected void write(int clientVersion, DataOutput out) throws IOException {
out.writeLong(serverTimeQueryReceived);
out.writeLong(serverTimeResponseSent);
out.writeUTF(requestId);
@@ -50,12 +52,12 @@ public class CommandResponse extends Response {
}
@Override
- RequestType requestType() {
+ public RequestType requestType() {
return RequestType.COMMAND;
}
@Override
- ResponseType responseType() {
+ public ResponseType responseType() {
return ResponseType.COMMAND;
}
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/ErrorResponse.java b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/ErrorResponse.java
index ba3f7360c5c..70e63448d59 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/ErrorResponse.java
+++ b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/ErrorResponse.java
@@ -7,74 +7,26 @@ package org.elasticsearch.xpack.sql.cli.net.protocol;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractErrorResponse;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
import java.io.DataInput;
-import java.io.DataOutput;
import java.io.IOException;
-import java.util.Objects;
/**
* Response sent when there is a server side error.
*/
-public class ErrorResponse extends Response {
- private final RequestType requestType;
- public final String message, cause, stack;
-
+public class ErrorResponse extends AbstractErrorResponse {
public ErrorResponse(RequestType requestType, String message, String cause, String stack) {
- this.requestType = requestType;
- this.message = message;
- this.cause = cause;
- this.stack = stack;
+ super(requestType, message, cause, stack);
}
- ErrorResponse(DataInput in) throws IOException {
- requestType = RequestType.read(in);
- message = in.readUTF();
- cause = in.readUTF();
- stack = in.readUTF();
+ ErrorResponse(Request request, DataInput in) throws IOException {
+ super((RequestType) request.requestType(), in);
}
@Override
- void write(int clientVersion, DataOutput out) throws IOException {
- requestType.write(out);
- out.writeUTF(message);
- out.writeUTF(cause);
- out.writeUTF(stack);
- }
-
- @Override
- protected String toStringBody() {
- return "request=[" + requestType
- + "] message=[" + message
- + "] cause=[" + cause
- + "] stack=[" + stack + "]";
- }
-
- @Override
- RequestType requestType() {
- return requestType;
- }
-
- @Override
- ResponseType responseType() {
+ public ResponseType responseType() {
return ResponseType.ERROR;
}
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || obj.getClass() != getClass()) {
- return false;
- }
- ErrorResponse other = (ErrorResponse) obj;
- return Objects.equals(requestType, other.requestType)
- && Objects.equals(message, other.message)
- && Objects.equals(cause, other.cause)
- && Objects.equals(stack, other.stack);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(requestType, message, cause, stack);
- }
-
-}
+}
\ No newline at end of file
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/ExceptionResponse.java b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/ExceptionResponse.java
index be1fef71f94..7751802ff8a 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/ExceptionResponse.java
+++ b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/ExceptionResponse.java
@@ -7,68 +7,27 @@ package org.elasticsearch.xpack.sql.cli.net.protocol;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractExceptionResponse;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
import java.io.DataInput;
-import java.io.DataOutput;
import java.io.IOException;
-import java.util.Objects;
/**
* Response sent when there is a client side error.
*/
-public class ExceptionResponse extends Response {
- private final RequestType requestType;
- public final String message, cause;
-
- public ExceptionResponse(RequestType requestType, String message, String cause) {
- this.requestType = requestType;
- this.message = message;
- this.cause = cause;
+public class ExceptionResponse extends AbstractExceptionResponse {
+ public ExceptionResponse(RequestType requestType, String message, String cause, SqlExceptionType exceptionType) {
+ super(requestType, message, cause, exceptionType);
}
- ExceptionResponse(DataInput in) throws IOException {
- requestType = RequestType.read(in);
- message = in.readUTF();
- cause = in.readUTF();
+ ExceptionResponse(Request request, DataInput in) throws IOException {
+ super((RequestType) request.requestType(), in);
}
@Override
- void write(int clientVersion, DataOutput out) throws IOException {
- requestType.write(out);
- out.writeUTF(message);
- out.writeUTF(cause);
- }
-
- @Override
- protected String toStringBody() {
- return "request=[" + requestType
- + "] message=[" + message
- + "] cause=[" + cause + "]";
- }
-
- @Override
- RequestType requestType() {
- return requestType;
- }
-
- @Override
- ResponseType responseType() {
+ public ResponseType responseType() {
return ResponseType.EXCEPTION;
}
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || obj.getClass() != getClass()) {
- return false;
- }
- ExceptionResponse other = (ExceptionResponse) obj;
- return Objects.equals(requestType, other.requestType)
- && Objects.equals(message, other.message)
- && Objects.equals(cause, other.cause);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(requestType, message, cause);
- }
}
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoRequest.java b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoRequest.java
index b4a6f06f036..28a85894236 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoRequest.java
+++ b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoRequest.java
@@ -6,80 +6,32 @@
package org.elasticsearch.xpack.sql.cli.net.protocol;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractInfoRequest;
import java.io.DataInput;
-import java.io.DataOutput;
import java.io.IOException;
-import java.util.Objects;
-
-public class InfoRequest extends Request {
- public final String jvmVersion, jvmVendor, jvmClassPath, osName, osVersion;
+/**
+ * Request general information about the server.
+ */
+public class InfoRequest extends AbstractInfoRequest {
/**
* Build the info request containing information about the current JVM.
*/
public InfoRequest() {
- jvmVersion = System.getProperty("java.version", "");
- jvmVendor = System.getProperty("java.vendor", "");
- jvmClassPath = System.getProperty("java.class.path", "");
- osName = System.getProperty("os.name", "");
- osVersion = System.getProperty("os.version", "");
+ super();
}
InfoRequest(String jvmVersion, String jvmVendor, String jvmClassPath, String osName, String osVersion) {
- this.jvmVersion = jvmVersion;
- this.jvmVendor = jvmVendor;
- this.jvmClassPath = jvmClassPath;
- this.osName = osName;
- this.osVersion = osVersion;
+ super(jvmVersion, jvmVendor, jvmClassPath, osName, osVersion);
}
InfoRequest(int clientVersion, DataInput in) throws IOException {
- jvmVersion = in.readUTF();
- jvmVendor = in.readUTF();
- jvmClassPath = in.readUTF();
- osName = in.readUTF();
- osVersion = in.readUTF();
- }
-
- @Override
- public void write(DataOutput out) throws IOException {
- out.writeUTF(jvmVersion);
- out.writeUTF(jvmVendor);
- out.writeUTF(jvmClassPath);
- out.writeUTF(osName);
- out.writeUTF(osVersion);
- }
-
- @Override
- protected String toStringBody() {
- return "jvm=[version=[" + jvmVersion
- + "] vendor=[" + jvmVendor
- + "] classPath=[" + jvmClassPath
- + "]] os=[name=[" + osName
- + "] version=[" + osVersion + "]]";
+ super(clientVersion, in);
}
@Override
public RequestType requestType() {
return RequestType.INFO;
}
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || obj.getClass() != getClass()) {
- return false;
- }
- InfoRequest other = (InfoRequest) obj;
- return Objects.equals(jvmVersion, other.jvmVersion)
- && Objects.equals(jvmVendor, other.jvmVendor)
- && Objects.equals(jvmClassPath, other.jvmClassPath)
- && Objects.equals(osName, other.osName)
- && Objects.equals(osVersion, other.osVersion);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(jvmVersion, jvmVendor, jvmClassPath, osName, osVersion);
- }
}
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoResponse.java b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoResponse.java
index ed49bb79768..52de73d97e3 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoResponse.java
+++ b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoResponse.java
@@ -7,88 +7,32 @@ package org.elasticsearch.xpack.sql.cli.net.protocol;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractInfoResponse;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
import java.io.DataInput;
-import java.io.DataOutput;
import java.io.IOException;
-import java.util.Objects;
-
-public class InfoResponse extends Response {
-
- public final String node, cluster, versionString, versionHash, versionDate;
- public final int majorVersion, minorVersion;
+/**
+ * General information about the server.
+ */
+public class InfoResponse extends AbstractInfoResponse {
public InfoResponse(String nodeName, String clusterName, byte versionMajor, byte versionMinor, String version,
String versionHash, String versionDate) {
- this.node = nodeName;
- this.cluster = clusterName;
- this.versionString = version;
- this.versionHash = versionHash;
- this.versionDate = versionDate;
-
- this.majorVersion = versionMajor;
- this.minorVersion = versionMinor;
+ super(nodeName, clusterName, versionMajor, versionMinor, version, versionHash, versionDate);
}
- InfoResponse(DataInput in) throws IOException {
- node = in.readUTF();
- cluster = in.readUTF();
- majorVersion = in.readByte();
- minorVersion = in.readByte();
- versionString = in.readUTF();
- versionHash = in.readUTF();
- versionDate = in.readUTF();
+ InfoResponse(Request request, DataInput in) throws IOException {
+ super(request, in);
}
@Override
- void write(int clientVersion, DataOutput out) throws IOException {
- out.writeUTF(node);
- out.writeUTF(cluster);
- out.writeByte(majorVersion);
- out.writeByte(minorVersion);
- out.writeUTF(versionString);
- out.writeUTF(versionHash);
- out.writeUTF(versionDate);
- }
-
- @Override
- protected String toStringBody() {
- return "node=[" + node
- + "] cluster=[" + cluster
- + "] version=[" + versionString
- + "]/[major=[" + majorVersion
- + "] minor=[" + minorVersion
- + "] hash=[" + versionHash
- + "] date=[" + versionDate + "]]";
- }
-
- @Override
- RequestType requestType() {
+ public RequestType requestType() {
return RequestType.INFO;
}
@Override
- ResponseType responseType() {
+ public ResponseType responseType() {
return ResponseType.INFO;
}
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || obj.getClass() != getClass()) {
- return false;
- }
- InfoResponse other = (InfoResponse) obj;
- return Objects.equals(node, other.node)
- && Objects.equals(cluster, other.cluster)
- && Objects.equals(majorVersion, other.majorVersion)
- && Objects.equals(minorVersion, other.minorVersion)
- && Objects.equals(versionString, other.versionString)
- && Objects.equals(versionHash, other.versionHash)
- && Objects.equals(versionDate, other.versionDate);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(node, cluster, majorVersion, minorVersion, versionString, versionHash, versionDate);
- }
}
\ No newline at end of file
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Proto.java b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Proto.java
index 86dafa64085..f8f7804c3c3 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Proto.java
+++ b/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Proto.java
@@ -5,6 +5,8 @@
*/
package org.elasticsearch.xpack.sql.cli.net.protocol;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto;
+
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@@ -13,73 +15,22 @@ import java.io.IOException;
* Binary protocol for the CLI. All backwards compatibility is done using the
* version number sent in the header.
*/
-public abstract class Proto {
- private static final int MAGIC_NUMBER = 0x0C0DEC110;
- public static final int CURRENT_VERSION = 000_000_001;
+public final class Proto extends AbstractProto {
+ public static final Proto INSTANCE = new Proto();
- private Proto() {
- // Static utilities
+ private Proto() {}
+
+ @Override
+ protected RequestType readRequestType(DataInput in) throws IOException {
+ return RequestType.read(in);
}
- public static void writeRequest(Request request, DataOutput out) throws IOException {
- writeHeader(CURRENT_VERSION, out);
- request.requestType().write(out);
- request.write(out);
+ @Override
+ protected ResponseType readResponseType(DataInput in) throws IOException {
+ return ResponseType.read(in);
}
- public static Request readRequest(DataInput in) throws IOException {
- int clientVersion = readHeader(in);
- if (clientVersion > CURRENT_VERSION) {
- throw new IOException("Unknown client version [" + clientVersion + "]. Always upgrade sql last.");
- // NOCOMMIT I believe we usually advise upgrading the clients *first* so this might be backwards.....
- }
- return RequestType.read(in).reader.read(clientVersion, in);
- }
-
- public static void writeResponse(Response response, int clientVersion, DataOutput out) throws IOException {
- writeHeader(clientVersion, out);
- response.responseType().write(out);
- response.write(clientVersion, out);
- }
-
- public static Response readResponse(RequestType expectedRequestType, DataInput in) throws IOException {
- int version = readHeader(in);
- if (version != CURRENT_VERSION) {
- throw new IOException("Response version [" + version + "] does not match client version ["
- + CURRENT_VERSION + "]. Server is busted.");
- }
- Response response = ResponseType.read(in).reader.read(in);
- if (response.requestType() != expectedRequestType) {
- throw new IOException("Expected request type to be [" + expectedRequestType
- + "] but was [" + response.requestType() + "]. Server is busted.");
- }
- return response;
- }
-
- private static void writeHeader(int clientVersion, DataOutput out) throws IOException {
- out.writeInt(MAGIC_NUMBER);
- out.writeInt(clientVersion);
- }
-
- /**
- * Read the protocol header.
- * @return the version
- * @throws IOException if there is an underlying {@linkplain IOException} or if the protocol is malformed
- */
- private static int readHeader(DataInput in) throws IOException {
- int magic = in.readInt();
- if (magic != MAGIC_NUMBER) {
- throw new IOException("Unknown protocol magic number [" + Integer.toHexString(magic) + "]");
- }
- int version = in.readInt();
- return version;
- }
-
- @FunctionalInterface
- interface RequestReader {
- Request read(int clientVersion, DataInput in) throws IOException;
- }
- public enum RequestType {
+ public enum RequestType implements AbstractProto.RequestType {
INFO(InfoRequest::new),
COMMAND(CommandRequest::new);
@@ -89,25 +40,27 @@ public abstract class Proto {
this.reader = reader;
}
- void write(DataOutput out) throws IOException {
- out.writeByte(ordinal());
- }
-
static RequestType read(DataInput in) throws IOException {
byte b = in.readByte();
try {
return values()[b];
} catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalArgumentException("Unknown response type [" + b + "]", e);
+ throw new IllegalArgumentException("Unknown request type [" + b + "]", e);
}
}
+
+ @Override
+ public void write(DataOutput out) throws IOException {
+ out.writeByte(ordinal());
+ }
+
+ @Override
+ public RequestReader reader() {
+ return reader;
+ }
}
- @FunctionalInterface
- interface ResponseReader {
- Response read(DataInput in) throws IOException;
- }
- enum ResponseType {
+ enum ResponseType implements AbstractProto.ResponseType {
EXCEPTION(ExceptionResponse::new),
ERROR(ErrorResponse::new),
INFO(InfoResponse::new),
@@ -119,10 +72,6 @@ public abstract class Proto {
this.reader = reader;
}
- void write(DataOutput out) throws IOException {
- out.writeByte(ordinal());
- }
-
static ResponseType read(DataInput in) throws IOException {
byte b = in.readByte();
try {
@@ -131,5 +80,15 @@ public abstract class Proto {
throw new IllegalArgumentException("Unknown response type [" + b + "]", e);
}
}
+
+ @Override
+ public void write(DataOutput out) throws IOException {
+ out.writeByte(ordinal());
+ }
+
+ @Override
+ public ResponseReader reader() {
+ return reader;
+ }
}
}
\ No newline at end of file
diff --git a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/CliRoundTripTestUtils.java b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/CliRoundTripTestUtils.java
index 34212bb24de..40155d3af80 100644
--- a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/CliRoundTripTestUtils.java
+++ b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/CliRoundTripTestUtils.java
@@ -5,6 +5,8 @@
*/
package org.elasticsearch.xpack.sql.cli.net.protocol;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import org.elasticsearch.xpack.sql.test.RoundTripTestUtils;
import java.io.IOException;
@@ -15,12 +17,12 @@ public final class CliRoundTripTestUtils {
}
static void assertRoundTripCurrentVersion(Request request) throws IOException {
- RoundTripTestUtils.assertRoundTrip(request, Proto::writeRequest, Proto::readRequest);
+ RoundTripTestUtils.assertRoundTrip(request, Proto.INSTANCE::writeRequest, Proto.INSTANCE::readRequest);
}
- static void assertRoundTripCurrentVersion(Response response) throws IOException {
+ static void assertRoundTripCurrentVersion(Request request, Response response) throws IOException {
RoundTripTestUtils.assertRoundTrip(response,
- (r, out) -> Proto.writeResponse(r, Proto.CURRENT_VERSION, out),
- in -> Proto.readResponse(response.requestType(), in));
+ (r, out) -> Proto.INSTANCE.writeResponse(r, Proto.CURRENT_VERSION, out),
+ in -> Proto.INSTANCE.readResponse(request, in));
}
}
diff --git a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandResponseTests.java b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandResponseTests.java
index 4abaa6c017f..03209276e68 100644
--- a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandResponseTests.java
+++ b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/CommandResponseTests.java
@@ -10,6 +10,8 @@ import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import static org.elasticsearch.xpack.sql.cli.net.protocol.CliRoundTripTestUtils.assertRoundTripCurrentVersion;
+import static org.elasticsearch.xpack.sql.cli.net.protocol.CommandRequestTests.randomCommandRequest;
+
public class CommandResponseTests extends ESTestCase {
static CommandResponse randomCommandResponse() {
long start = randomNonNegativeLong();
@@ -18,7 +20,7 @@ public class CommandResponseTests extends ESTestCase {
}
public void testRoundTrip() throws IOException {
- assertRoundTripCurrentVersion(randomCommandResponse());
+ assertRoundTripCurrentVersion(randomCommandRequest(), randomCommandResponse());
}
public void testToString() {
diff --git a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/ErrorResponseTests.java b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/ErrorResponseTests.java
index a53276435cd..e829e817fb3 100644
--- a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/ErrorResponseTests.java
+++ b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/ErrorResponseTests.java
@@ -11,14 +11,16 @@ import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
import java.io.IOException;
import static org.elasticsearch.xpack.sql.cli.net.protocol.CliRoundTripTestUtils.assertRoundTripCurrentVersion;
+import static org.elasticsearch.xpack.sql.cli.net.protocol.CommandRequestTests.randomCommandRequest;
+
public class ErrorResponseTests extends ESTestCase {
static ErrorResponse randomErrorResponse() {
- return new ErrorResponse(randomFrom(RequestType.values()), randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5));
+ return new ErrorResponse(RequestType.COMMAND, randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5));
}
public void testRoundTrip() throws IOException {
- assertRoundTripCurrentVersion(randomErrorResponse());
+ assertRoundTripCurrentVersion(randomCommandRequest(), randomErrorResponse());
}
public void testToString() {
diff --git a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/ExceptionResponseTests.java b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/ExceptionResponseTests.java
index aae9f6b93e9..0fd3ec51674 100644
--- a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/ExceptionResponseTests.java
+++ b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/ExceptionResponseTests.java
@@ -7,22 +7,26 @@ package org.elasticsearch.xpack.sql.cli.net.protocol;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
import java.io.IOException;
import static org.elasticsearch.xpack.sql.cli.net.protocol.CliRoundTripTestUtils.assertRoundTripCurrentVersion;
+import static org.elasticsearch.xpack.sql.cli.net.protocol.CommandRequestTests.randomCommandRequest;
+
public class ExceptionResponseTests extends ESTestCase {
static ExceptionResponse randomExceptionResponse() {
- return new ExceptionResponse(randomFrom(RequestType.values()), randomAlphaOfLength(5), randomAlphaOfLength(5));
+ return new ExceptionResponse(RequestType.COMMAND, randomAlphaOfLength(5), randomAlphaOfLength(5),
+ randomFrom(SqlExceptionType.values()));
}
public void testRoundTrip() throws IOException {
- assertRoundTripCurrentVersion(randomExceptionResponse());
+ assertRoundTripCurrentVersion(randomCommandRequest(), randomExceptionResponse());
}
public void testToString() {
- assertEquals("ExceptionResponse",
- new ExceptionResponse(RequestType.COMMAND, "test", "test").toString());
+ assertEquals("ExceptionResponse",
+ new ExceptionResponse(RequestType.COMMAND, "test", "test", SqlExceptionType.SYNTAX).toString());
}
}
diff --git a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoResponseTests.java b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoResponseTests.java
index 7c535b4a4ae..5f3b08480ef 100644
--- a/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoResponseTests.java
+++ b/sql/cli-proto/src/test/java/org/elasticsearch/xpack/sql/cli/net/protocol/InfoResponseTests.java
@@ -10,6 +10,7 @@ import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import static org.elasticsearch.xpack.sql.cli.net.protocol.CliRoundTripTestUtils.assertRoundTripCurrentVersion;
+import static org.elasticsearch.xpack.sql.cli.net.protocol.InfoRequestTests.randomInfoRequest;
public class InfoResponseTests extends ESTestCase {
static InfoResponse randomInfoResponse() {
@@ -18,7 +19,7 @@ public class InfoResponseTests extends ESTestCase {
}
public void testRoundTrip() throws IOException {
- assertRoundTripCurrentVersion(randomInfoResponse());
+ assertRoundTripCurrentVersion(randomInfoRequest(), randomInfoResponse());
}
public void testToString() {
diff --git a/sql/cli/build.gradle b/sql/cli/build.gradle
index 472db245dda..b9d518ca46c 100644
--- a/sql/cli/build.gradle
+++ b/sql/cli/build.gradle
@@ -10,22 +10,23 @@ dependencies {
compile "org.jline:jline:3.3.1"
compile project(':x-pack-elasticsearch:sql:net-client')
compile project(':x-pack-elasticsearch:sql:cli-proto')
+ compile project(':x-pack-elasticsearch:sql:shared-proto')
- testCompile project(":x-pack-elasticsearch:transport-client") // NOCOMMIT probably can remove this
- testCompile project(path: ':x-pack-elasticsearch:plugin', configuration: 'testArtifacts')
+ // Used by embedded sql instance
+ testCompile project(":x-pack-elasticsearch:transport-client")
+ testCompile project(path: ':x-pack-elasticsearch:plugin', configuration: 'testArtifacts') // NOCOMMIT remove this?
testCompile project(':x-pack-elasticsearch:sql:test-utils')
- // Used by the hack to run InternalTestCluster if not running against a gradle-started cluster.
- testCompile project(path: ':modules:lang-painless', configuration: 'runtime')
-
runtime "org.fusesource.jansi:jansi:1.16"
runtime "org.elasticsearch:jna:4.4.0-1"
}
dependencyLicenses {
mapping from: /cli-proto.*/, to: 'elasticsearch'
- mapping from: /net-client.*/, to: 'elasticsearch'
+ mapping from: /shared-proto.*/, to: 'elasticsearch'
+ mapping from: /net.*/, to: 'elasticsearch'
ignoreSha 'cli-proto'
+ ignoreSha 'shared-proto'
ignoreSha 'net-client'
}
diff --git a/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/ResponseToString.java b/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/ResponseToString.java
index acdca547205..b5d1572a0ed 100644
--- a/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/ResponseToString.java
+++ b/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/ResponseToString.java
@@ -9,8 +9,8 @@ import org.elasticsearch.xpack.sql.cli.net.protocol.CommandResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.ErrorResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.ExceptionResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.InfoResponse;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Response;
import org.elasticsearch.xpack.sql.net.client.SuppressForbidden;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import org.jline.utils.AttributedStringBuilder;
import java.awt.Desktop;
diff --git a/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/net/client/CliHttpClient.java b/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/net/client/CliHttpClient.java
index f69f32ce304..07589b5b36e 100644
--- a/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/net/client/CliHttpClient.java
+++ b/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/net/client/CliHttpClient.java
@@ -10,9 +10,8 @@ import org.elasticsearch.xpack.sql.cli.CliException;
import org.elasticsearch.xpack.sql.cli.net.protocol.CommandRequest;
import org.elasticsearch.xpack.sql.cli.net.protocol.InfoRequest;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Response;
import org.elasticsearch.xpack.sql.net.client.util.Bytes;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
@@ -27,13 +26,15 @@ public class CliHttpClient implements AutoCloseable {
}
public Response serverInfo() {
- Bytes ba = http.put(out -> Proto.writeRequest(new InfoRequest(), out));
- return doIO(ba, in -> Proto.readResponse(RequestType.INFO, in));
+ InfoRequest request = new InfoRequest();
+ Bytes ba = http.post(out -> Proto.INSTANCE.writeRequest(request, out));
+ return doIO(ba, in -> Proto.INSTANCE.readResponse(request, in));
}
public Response command(String command, String requestId) {
- Bytes ba = http.put(out -> Proto.writeRequest(new CommandRequest(command), out));
- return doIO(ba, in -> Proto.readResponse(RequestType.COMMAND, in));
+ CommandRequest request = new CommandRequest(command);
+ Bytes ba = http.post(out -> Proto.INSTANCE.writeRequest(request, out));
+ return doIO(ba, in -> Proto.INSTANCE.readResponse(request, in));
}
private static T doIO(Bytes ba, DataInputFunction action) {
diff --git a/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/net/client/HttpClient.java b/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/net/client/HttpClient.java
index 8c72c3f8250..9109b1773e1 100644
--- a/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/net/client/HttpClient.java
+++ b/sql/cli/src/main/java/org/elasticsearch/xpack/sql/cli/net/client/HttpClient.java
@@ -13,8 +13,6 @@ import org.elasticsearch.xpack.sql.net.client.util.CheckedConsumer;
import java.io.DataOutput;
import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -26,32 +24,10 @@ class HttpClient {
this.cfg = cfg;
}
- private URL url(String subPath) {
- try {
- return new URL(cfg.asUrl(), subPath);
- } catch (MalformedURLException ex) {
- throw new ClientException(ex, "Invalid subpath %s", subPath);
- }
- }
-
- boolean head(String path) {
- try {
- return AccessController.doPrivileged((PrivilegedAction) () -> {
- return JreHttpUrlConnection.http(url(path), cfg, JreHttpUrlConnection::head);
- });
- } catch (ClientException ex) {
- throw new RuntimeException("Transport failure", ex);
- }
- }
-
- Bytes put(CheckedConsumer os) {
- return put("", os);
- }
-
- Bytes put(String path, CheckedConsumer os) {
+ Bytes post(CheckedConsumer os) {
try {
return AccessController.doPrivileged((PrivilegedAction) () -> {
- return JreHttpUrlConnection.http(url(path), cfg, con -> {
+ return JreHttpUrlConnection.http(cfg.asUrl(), cfg, con -> {
return con.post(os);
});
});
diff --git a/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliHttpServer.java b/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliHttpServer.java
index 0c2f71eb9dc..11eaf71acfd 100644
--- a/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliHttpServer.java
+++ b/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliHttpServer.java
@@ -6,7 +6,7 @@
package org.elasticsearch.xpack.sql.cli;
import org.elasticsearch.client.Client;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Response;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import org.elasticsearch.xpack.sql.test.server.ProtoHttpServer;
/**
diff --git a/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliProtoHandler.java b/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliProtoHandler.java
index b64af895d14..9575a99dcd5 100644
--- a/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliProtoHandler.java
+++ b/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliProtoHandler.java
@@ -10,10 +10,11 @@ import com.sun.net.httpserver.HttpExchange;
import org.elasticsearch.client.Client;
import org.elasticsearch.xpack.sql.TestUtils;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Request;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Response;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+import org.elasticsearch.xpack.sql.server.AbstractSqlServer;
import org.elasticsearch.xpack.sql.server.cli.CliServer;
-import org.elasticsearch.xpack.sql.server.cli.CliServerProtoUtils;
import org.elasticsearch.xpack.sql.test.server.ProtoHandler;
import java.io.DataInput;
@@ -26,14 +27,14 @@ class CliProtoHandler extends ProtoHandler {
private final CliServer server;
CliProtoHandler(Client client) {
- super(client, in -> null, CliServerProtoUtils::write);
+ super(client, response -> AbstractSqlServer.write(AbstractProto.CURRENT_VERSION, response));
this.server = new CliServer(TestUtils.planExecutor(client), clusterName, () -> info.getNode().getName(), info.getVersion(),
info.getBuild());
}
@Override
protected void handle(HttpExchange http, DataInput in) throws IOException {
- Request req = Proto.readRequest(in);
+ Request req = Proto.INSTANCE.readRequest(in);
server.handle(req, wrap(resp -> sendHttpResponse(http, resp), ex -> fail(http, ex)));
}
}
\ No newline at end of file
diff --git a/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/ResponseToStringTests.java b/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/ResponseToStringTests.java
index e27b366c64c..cd35022fd95 100644
--- a/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/ResponseToStringTests.java
+++ b/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/ResponseToStringTests.java
@@ -9,6 +9,7 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.cli.net.protocol.CommandResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.ExceptionResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
import org.jline.terminal.Terminal;
import org.jline.utils.AttributedStringBuilder;
@@ -23,7 +24,8 @@ public class ResponseToStringTests extends ESTestCase {
}
public void testExceptionResponse() {
- AttributedStringBuilder s = ResponseToString.toAnsi(new ExceptionResponse(RequestType.INFO, "test message", "test cause"));
+ AttributedStringBuilder s = ResponseToString.toAnsi(new ExceptionResponse(RequestType.INFO, "test message", "test cause",
+ randomFrom(SqlExceptionType.values())));
assertEquals("test message", unstyled(s));
assertEquals("[1;36mtest message[0m", fullyStyled(s));
}
diff --git a/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/integration/net/protocol/ProtoTests.java b/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/integration/net/protocol/ProtoTests.java
index a4aa4dae1f8..eaf83af4d17 100644
--- a/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/integration/net/protocol/ProtoTests.java
+++ b/sql/cli/src/test/java/org/elasticsearch/xpack/sql/cli/integration/net/protocol/ProtoTests.java
@@ -16,8 +16,8 @@ import org.elasticsearch.xpack.sql.cli.net.client.CliHttpClient;
import org.elasticsearch.xpack.sql.cli.net.protocol.CommandResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.ExceptionResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.InfoResponse;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Response;
import org.elasticsearch.xpack.sql.net.client.SuppressForbidden;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import org.junit.AfterClass;
import org.junit.BeforeClass;
diff --git a/sql/jdbc-proto/build.gradle b/sql/jdbc-proto/build.gradle
index e1386d39036..22b0f8639d1 100644
--- a/sql/jdbc-proto/build.gradle
+++ b/sql/jdbc-proto/build.gradle
@@ -1,9 +1,8 @@
-apply plugin: 'elasticsearch.build'
-
description = 'Request and response objects shared by the jdbc driver and ' +
'its backend in :sql:server'
dependencies {
+ compile project(':x-pack-elasticsearch:sql:shared-proto')
testCompile project(':x-pack-elasticsearch:sql:test-utils')
}
@@ -11,3 +10,8 @@ forbiddenApisMain {
// does not depend on core, so only jdk and http signatures should be checked
signaturesURLs = [this.class.getResource('/forbidden/jdk-signatures.txt')]
}
+
+dependencyLicenses {
+ mapping from: /shared-proto.*/, to: 'elasticsearch'
+ ignoreSha 'shared-proto'
+}
diff --git a/sql/jdbc-proto/licenses/elasticsearch-LICENSE.txt b/sql/jdbc-proto/licenses/elasticsearch-LICENSE.txt
new file mode 100644
index 00000000000..d6456956733
--- /dev/null
+++ b/sql/jdbc-proto/licenses/elasticsearch-LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/sql/jdbc-proto/licenses/elasticsearch-NOTICE.txt b/sql/jdbc-proto/licenses/elasticsearch-NOTICE.txt
new file mode 100644
index 00000000000..643a060cd05
--- /dev/null
+++ b/sql/jdbc-proto/licenses/elasticsearch-NOTICE.txt
@@ -0,0 +1,5 @@
+Elasticsearch
+Copyright 2009-2017 Elasticsearch
+
+This product includes software developed by The Apache Software
+Foundation (http://www.apache.org/).
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfo.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfo.java
index a167a01c3c8..c27a71b41a9 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfo.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfo.java
@@ -5,32 +5,101 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.sql.JDBCType;
+import java.util.Objects;
+
public class ColumnInfo {
-
public String catalog, schema, table, label, name;
- public int type;
+ public JDBCType type;
- public ColumnInfo() {}
+ public ColumnInfo(String name, JDBCType type, String table, String catalog, String schema, String label) {
+ if (name == null) {
+ throw new IllegalArgumentException("[name] must not be null");
+ }
+ if (type == null) {
+ throw new IllegalArgumentException("[type] must not be null");
+ }
+ if (table == null) {
+ throw new IllegalArgumentException("[table] must not be null");
+ }
+ if (catalog == null) {
+ throw new IllegalArgumentException("[catalog] must not be null");
+ }
+ if (schema == null) {
+ throw new IllegalArgumentException("[schema] must not be null");
+ }
+ if (label == null) {
+ throw new IllegalArgumentException("[label] must not be null");
+ }
+ this.name = name;
+ this.type = type;
+ this.table = table;
+ this.catalog = catalog;
+ this.schema = schema;
+ this.label = label;
+ }
- public ColumnInfo(String columnName, int columnType,
- String tableName,
- String catalogName,
- String schemaName,
- String columnLabel) {
- this.type = columnType;
- this.catalog = catalogName;
- this.table = tableName;
- this.label = columnLabel;
- this.name = columnName;
- this.schema = schemaName;
+ ColumnInfo(DataInput in) throws IOException {
+ name = in.readUTF();
+ type = JDBCType.valueOf(in.readInt());
+ table = in.readUTF();
+ catalog = in.readUTF();
+ schema = in.readUTF();
+ label = in.readUTF();
+ }
+
+ void write(DataOutput out) throws IOException {
+ out.writeUTF(name);
+ out.writeInt(type.getVendorTypeNumber());
+ out.writeUTF(table);
+ out.writeUTF(catalog);
+ out.writeUTF(schema);
+ out.writeUTF(label);
}
public int displaySize() {
+ // NOCOMMIT look at this one.....
return -1;
}
@Override
public String toString() {
- return name;
+ StringBuilder b = new StringBuilder();
+ if (false == "".equals(table)) {
+ b.append(table).append('.');
+ }
+ b.append(name).append("').toString();
}
-}
\ No newline at end of file
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ ColumnInfo other = (ColumnInfo) obj;
+ return name.equals(other.name)
+ && type.equals(other.type)
+ && table.equals(other.table)
+ && catalog.equals(other.catalog)
+ && schema.equals(other.schema)
+ && label.equals(other.label);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, type, table, catalog, schema, label);
+ }
+}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/DataResponse.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/DataResponse.java
deleted file mode 100644
index d9baa456f60..00000000000
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/DataResponse.java
+++ /dev/null
@@ -1,20 +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.jdbc.net.protocol;
-
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-
-public abstract class DataResponse extends Response {
-
- // there is no type for this field since depending on where it is used, the data is represented differently
- // on the server it is a RowSetCursor (before being sent to the wire), on the client a Page (after being read from the wire)
- public final Object data;
-
- public DataResponse(Action action, Object data) {
- super(action);
- this.data = data;
- }
-}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ErrorResponse.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ErrorResponse.java
index 4bcd8eb47f5..6610384d813 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ErrorResponse.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ErrorResponse.java
@@ -5,36 +5,28 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractErrorResponse;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+
import java.io.DataInput;
-import java.io.DataOutput;
import java.io.IOException;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Status;
+/**
+ * Response sent when there is a server side error.
+ */
+public class ErrorResponse extends AbstractErrorResponse {
+ public ErrorResponse(RequestType requestType, String message, String cause, String stack) {
+ super(requestType, message, cause, stack);
+ }
-public class ErrorResponse extends Response {
-
- public final String message, cause, stack;
-
- public ErrorResponse(Action requestedAction, String message, String cause, String stack) {
- super(requestedAction);
- this.message = message;
- this.cause = cause;
- this.stack = stack;
+ ErrorResponse(Request request, DataInput in) throws IOException {
+ super((RequestType) request.requestType(), in);
}
@Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(Status.toError(action));
- out.writeUTF(message);
- out.writeUTF(cause);
- out.writeUTF(stack);
- }
-
- public static ErrorResponse decode(DataInput in, Action action) throws IOException {
- String message = in.readUTF();
- String cause = in.readUTF();
- String stack = in.readUTF();
- return new ErrorResponse(action, message, cause, stack);
+ public ResponseType responseType() {
+ return ResponseType.ERROR;
}
}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ExceptionResponse.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ExceptionResponse.java
index db78b3a8562..7fadb708125 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ExceptionResponse.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ExceptionResponse.java
@@ -5,38 +5,29 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractExceptionResponse;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+
import java.io.DataInput;
-import java.io.DataOutput;
import java.io.IOException;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.SqlExceptionType;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Status;
+/**
+ * Response sent when there is a client side error.
+ */
+public class ExceptionResponse extends AbstractExceptionResponse {
+ public ExceptionResponse(RequestType requestType, String message, String cause, SqlExceptionType exceptionType) {
+ super(requestType, message, cause, exceptionType);
+ }
-public class ExceptionResponse extends Response {
-
- public final SqlExceptionType asSql;
- public final String message, cause;
-
- public ExceptionResponse(Action requestedAction, String message, String cause, SqlExceptionType asSql) {
- super(requestedAction);
- this.message = message;
- this.cause = cause;
- this.asSql = asSql;
+ ExceptionResponse(Request request, DataInput in) throws IOException {
+ super((RequestType) request.requestType(), in);
}
@Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(Status.toException(action));
- out.writeUTF(message);
- out.writeUTF(cause);
- out.writeInt(asSql.value());
- }
-
- public static ExceptionResponse decode(DataInput in, Action action) throws IOException {
- String message = in.readUTF();
- String cause = in.readUTF();
- int sqlType = in.readInt();
- return new ExceptionResponse(action, message, cause, SqlExceptionType.from(sqlType));
+ public ResponseType responseType() {
+ return ResponseType.EXCEPTION;
}
}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoRequest.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoRequest.java
index 73cba13350d..fb18a0e9d47 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoRequest.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoRequest.java
@@ -5,55 +5,33 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractInfoRequest;
import java.io.DataInput;
-import java.io.DataOutput;
import java.io.IOException;
-import static org.elasticsearch.xpack.sql.jdbc.net.protocol.StringUtils.EMPTY;
-
-public class InfoRequest extends Request {
- public final String jvmVersion, jvmVendor, jvmClassPath, osName, osVersion;
-
+/**
+ * Request general information about the server.
+ */
+public class InfoRequest extends AbstractInfoRequest {
/**
* Build the info request containing information about the current JVM.
*/
public InfoRequest() {
- super(Action.INFO);
- jvmVersion = System.getProperty("java.version", EMPTY);
- jvmVendor = System.getProperty("java.vendor", EMPTY);
- jvmClassPath = System.getProperty("java.class.path", EMPTY);
- osName = System.getProperty("os.name", EMPTY);
- osVersion = System.getProperty("os.version", EMPTY);
+ super();
}
- public InfoRequest(String jvmVersion, String jvmVendor, String jvmClassPath, String osName, String osVersion) {
- super(Action.INFO);
- this.jvmVersion = jvmVersion;
- this.jvmVendor = jvmVendor;
- this.jvmClassPath = jvmClassPath;
- this.osName = osName;
- this.osVersion = osVersion;
+ InfoRequest(String jvmVersion, String jvmVendor, String jvmClassPath, String osName, String osVersion) {
+ super(jvmVersion, jvmVendor, jvmClassPath, osName, osVersion);
+ }
+
+ InfoRequest(int clientVersion, DataInput in) throws IOException {
+ super(clientVersion, in);
}
@Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(action.value());
- out.writeUTF(jvmVersion);
- out.writeUTF(jvmVendor);
- out.writeUTF(jvmClassPath);
- out.writeUTF(osName);
- out.writeUTF(osVersion);
- }
-
- public static InfoRequest decode(DataInput in) throws IOException {
- String jvmVersion = in.readUTF();
- String jvmVendor = in.readUTF();
- String jvmClassPath = in.readUTF();
- String osName = in.readUTF();
- String osVersion = in.readUTF();
-
- return new InfoRequest(jvmVersion, jvmVendor, jvmClassPath, osName, osVersion);
+ public RequestType requestType() {
+ return RequestType.INFO;
}
}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoResponse.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoResponse.java
index 140de975c18..c85e72a2589 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoResponse.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoResponse.java
@@ -5,51 +5,34 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractInfoResponse;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+
import java.io.DataInput;
-import java.io.DataOutput;
import java.io.IOException;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Status;
-
-public class InfoResponse extends Response {
-
- public final String node, cluster, versionString, versionHash, versionDate;
- public final int majorVersion, minorVersion;
-
- public InfoResponse(String nodeName, String clusterName, byte versionMajor, byte versionMinor, String version, String versionHash, String versionDate) {
- super(Action.INFO);
-
- this.node = nodeName;
- this.cluster = clusterName;
- this.versionString = version;
- this.versionHash = versionHash;
- this.versionDate = versionDate;
-
- this.majorVersion = versionMajor;
- this.minorVersion = versionMinor;
+/**
+ * General information about the server.
+ */
+public class InfoResponse extends AbstractInfoResponse {
+ public InfoResponse(String nodeName, String clusterName, byte versionMajor, byte versionMinor, String version,
+ String versionHash, String versionDate) {
+ super(nodeName, clusterName, versionMajor, versionMinor, version, versionHash, versionDate);
}
- public void encode(DataOutput out) throws IOException {
- out.writeInt(Status.toSuccess(action));
- out.writeUTF(node);
- out.writeUTF(cluster);
- out.writeByte(majorVersion);
- out.writeByte(minorVersion);
- out.writeUTF(versionString);
- out.writeUTF(versionHash);
- out.writeUTF(versionDate);
+ InfoResponse(Request request, DataInput in) throws IOException {
+ super(request, in);
}
- public static InfoResponse decode(DataInput in) throws IOException {
- String node = in.readUTF();
- String cluster = in.readUTF();
- byte versionMajor = in.readByte();
- byte versionMinor = in.readByte();
- String version = in.readUTF();
- String versionHash = in.readUTF();
- String versionBuild = in.readUTF();
+ @Override
+ public RequestType requestType() {
+ return RequestType.INFO;
+ }
- return new InfoResponse(node, cluster, versionMajor, versionMinor, version, versionHash, versionBuild);
+ @Override
+ public ResponseType responseType() {
+ return ResponseType.INFO;
}
}
\ No newline at end of file
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Message.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Message.java
deleted file mode 100644
index 234b486c2e9..00000000000
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Message.java
+++ /dev/null
@@ -1,27 +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.jdbc.net.protocol;
-
-import java.io.DataOutput;
-import java.io.IOException;
-
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-
-abstract class Message {
-
- public final Action action;
-
- protected Message(Action action) {
- this.action = action;
- }
-
- @Override
- public String toString() {
- return action.name();
- }
-
- public abstract void encode(DataOutput out) throws IOException;
-}
\ No newline at end of file
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnInfo.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnInfo.java
index e7ffb31b777..221d3d6fdc3 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnInfo.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnInfo.java
@@ -5,31 +5,73 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
import java.sql.JDBCType;
-import java.util.Locale;
-
-import static java.lang.String.format;
+import java.util.Objects;
public class MetaColumnInfo {
+ public final String table, name;
+ public final JDBCType type;
+ public final int size, position;
- // column.name - string - column name
- // table.name - string - index.type
- // data.type - int - data type
- // column.size - int
- // ordinal.position - int - position inside table
- public final String name, table;
- public final int type, size, position;
-
- public MetaColumnInfo(String name, String table, int type, int size, int position) {
- this.name = name;
+ public MetaColumnInfo(String table, String name, JDBCType type, int size, int position) {
+ if (table == null) {
+ throw new IllegalArgumentException("[table] must not be null");
+ }
+ if (name == null) {
+ throw new IllegalArgumentException("[name] must not be null");
+ }
+ if (type == null) {
+ throw new IllegalArgumentException("[type] must not be null");
+ }
this.table = table;
+ this.name = name;
this.type = type;
this.size = size;
this.position = position;
}
+ MetaColumnInfo(DataInput in) throws IOException {
+ table = in.readUTF();
+ name = in.readUTF();
+ type = JDBCType.valueOf(in.readInt());
+ size = in.readInt();
+ position = in.readInt();
+ }
+
+ void write(DataOutput out) throws IOException {
+ out.writeUTF(table);
+ out.writeUTF(name);
+ out.writeInt(type.getVendorTypeNumber());
+ out.writeInt(size);
+ out.writeInt(position);
+ }
+
@Override
public String toString() {
- return format(Locale.ROOT, "%s,%s,%s,%d,%d", name, table, JDBCType.valueOf(type), size, position);
+ return table + "." + name
+ + "";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ MetaColumnInfo other = (MetaColumnInfo) obj;
+ return table.equals(other.table)
+ && name.equals(other.name)
+ && type.equals(other.type)
+ && size == other.size
+ && position == other.position;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(table, name, type, size, position);
}
}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnRequest.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnRequest.java
index cb202d73138..4c88d72bf21 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnRequest.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnRequest.java
@@ -5,48 +5,64 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
-
-import static org.elasticsearch.xpack.sql.jdbc.net.protocol.StringUtils.nullAsEmpty;
-import static org.elasticsearch.xpack.sql.jdbc.net.protocol.StringUtils.splitToIndexAndType;
+import java.util.Objects;
public class MetaColumnRequest extends Request {
-
private final String tablePattern, columnPattern;
- public final String index, type, column;
public MetaColumnRequest(String tablePattern, String columnPattern) {
- super(Action.META_COLUMN);
+ this.tablePattern = tablePattern == null ? "" : tablePattern;
+ this.columnPattern = columnPattern == null ? "" : columnPattern;
+ }
- this.tablePattern = nullAsEmpty(tablePattern);
- this.columnPattern = nullAsEmpty(columnPattern);
-
- String[] split = splitToIndexAndType(tablePattern);
-
- this.index = split[0];
- this.type = split[1];
- this.column = nullAsEmpty(columnPattern);
+ MetaColumnRequest(int clientVersion, DataInput in) throws IOException {
+ tablePattern = in.readUTF();
+ columnPattern = in.readUTF();
}
@Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(action.value());
+ protected void write(DataOutput out) throws IOException {
out.writeUTF(tablePattern);
out.writeUTF(columnPattern);
}
- public static MetaColumnRequest decode(DataInput in) throws IOException {
- String tablePattern = in.readUTF();
- String columnPattern = in.readUTF();
- return new MetaColumnRequest(tablePattern, columnPattern);
+ public String tablePattern() {
+ return tablePattern;
+ }
+
+ public String columnPattern() {
+ return columnPattern;
}
@Override
- public String toString() {
- return "MetaColumn[index=" + index + ", type=" + type + " column=" + column + "]";
+ protected String toStringBody() {
+ return "table=[" + tablePattern
+ + "] column=[" + columnPattern + "]";
+ }
+
+ @Override
+ public RequestType requestType() {
+ return RequestType.META_COLUMN;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ MetaColumnRequest other = (MetaColumnRequest) obj;
+ return tablePattern.equals(other.tablePattern)
+ && columnPattern.equals(other.columnPattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tablePattern, columnPattern);
}
}
\ No newline at end of file
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnResponse.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnResponse.java
index b402df7c798..cd0e1311503 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnResponse.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnResponse.java
@@ -5,65 +5,74 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Status;
-
-import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
+import static java.util.stream.Collectors.joining;
public class MetaColumnResponse extends Response {
-
- public static final MetaColumnResponse EMPTY = new MetaColumnResponse(emptyList());
-
public final List columns;
public MetaColumnResponse(List columns) {
- super(Action.META_COLUMN);
+ if (columns == null) {
+ throw new IllegalArgumentException("[columns] must not be null");
+ }
this.columns = columns;
}
- @Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(Status.toSuccess(action));
- out.writeInt(columns.size());
-
- for (MetaColumnInfo info : columns) {
- // NOCOMMIT core would make MetaColumnInfo know how to read and write itself which feels cleaner.
- out.writeUTF(info.name);
- out.writeUTF(info.table);
- out.writeInt(info.type);
- out.writeInt(info.size);
- out.writeInt(info.position);
- }
- }
-
- public static MetaColumnResponse decode(DataInput in) throws IOException {
+ public MetaColumnResponse(Request request, DataInput in) throws IOException {
int length = in.readInt();
-
- if (length < 1) {
- return MetaColumnResponse.EMPTY;
- }
List list = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
- String name = in.readUTF();
- String table = in.readUTF();
- int type = in.readInt();
- int size = in.readInt();
- int pos = in.readInt();
- list.add(new MetaColumnInfo(name, table, type, size, pos));
+ list.add(new MetaColumnInfo(in));
}
-
- return new MetaColumnResponse(list);
+ columns = unmodifiableList(list);
}
@Override
- public String toString() {
- return columns.toString();
+ protected void write(int clientVersion, DataOutput out) throws IOException {
+ out.writeInt(columns.size());
+ for (MetaColumnInfo info : columns) {
+ info.write(out);
+ }
+ }
+
+ @Override
+ protected String toStringBody() {
+ return columns.stream().map(Object::toString).collect(joining(", "));
+ }
+
+ @Override
+ public RequestType requestType() {
+ return RequestType.META_COLUMN;
+ }
+
+ @Override
+ public ResponseType responseType() {
+ return ResponseType.META_COLUMN;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ MetaColumnResponse other = (MetaColumnResponse) obj;
+ return columns.equals(other.columns);
+ }
+
+ @Override
+ public int hashCode() {
+ return columns.hashCode();
}
}
\ No newline at end of file
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableRequest.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableRequest.java
index bb3d34e32b9..b613d379e82 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableRequest.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableRequest.java
@@ -5,45 +5,57 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
-import java.util.Locale;
-
-import static java.lang.String.format;
-import static org.elasticsearch.xpack.sql.jdbc.net.protocol.StringUtils.splitToIndexAndType;
public class MetaTableRequest extends Request {
-
private final String pattern;
- public final String index;
- public final String type;
public MetaTableRequest(String pattern) {
- super(Action.META_TABLE);
-
+ if (pattern == null) {
+ throw new IllegalArgumentException("[pattern] must not be null");
+ }
this.pattern = pattern;
- String[] split = splitToIndexAndType(pattern);
+ }
- this.index = split[0];
- this.type = split[1];
+ MetaTableRequest(int clientVersion, DataInput in) throws IOException {
+ this.pattern = in.readUTF();
}
@Override
- public String toString() {
- return format(Locale.ROOT, "MetaTable[index=%s, type=%s]", index, type);
- }
-
- @Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(action.value());
+ public void write(DataOutput out) throws IOException {
out.writeUTF(pattern);
}
- public static MetaTableRequest decode(DataInput in) throws IOException {
- String pattern = in.readUTF();
- return new MetaTableRequest(pattern);
+ public String pattern() {
+ return pattern;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return pattern;
+ }
+
+ @Override
+ public RequestType requestType() {
+ return RequestType.META_TABLE;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ MetaTableRequest other = (MetaTableRequest) obj;
+ return pattern.equals(other.pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return pattern.hashCode();
}
}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableResponse.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableResponse.java
index 054c07c1f0a..5f79036e42f 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableResponse.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableResponse.java
@@ -5,53 +5,73 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Status;
-
-import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
public class MetaTableResponse extends Response {
-
- public static final MetaTableResponse EMPTY = new MetaTableResponse(emptyList());
-
public final List tables;
public MetaTableResponse(List tables) {
- super(Action.META_TABLE);
+ if (tables == null) {
+ throw new IllegalArgumentException("[tables] must not be null");
+ }
this.tables = tables;
}
+ MetaTableResponse(Request request, DataInput in) throws IOException {
+ int length = in.readInt();
+ List list = new ArrayList<>(length);
+ for (int i = 0; i < length; i++) {
+ list.add(in.readUTF());
+ }
+ tables = unmodifiableList(list);
+ }
+
@Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(Status.toSuccess(action));
+ public void write(int clientVersion, DataOutput out) throws IOException {
out.writeInt(tables.size());
for (String t : tables) {
out.writeUTF(t);
}
}
- public static MetaTableResponse decode(DataInput in) throws IOException {
- int length = in.readInt();
- if (length < 1) {
- return MetaTableResponse.EMPTY;
- }
-
- List list = new ArrayList<>(length);
-
- for (int i = 0; i < length; i++) {
- list.add(in.readUTF());
- }
- return new MetaTableResponse(list);
+ @Override
+ protected String toStringBody() {
+ return String.join(", ", tables);
}
@Override
- public String toString() {
- return tables.toString();
+ public RequestType requestType() {
+ return RequestType.META_TABLE;
}
+
+ @Override
+ public ResponseType responseType() {
+ return ResponseType.META_TABLE;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ MetaTableResponse other = (MetaTableResponse) obj;
+ return tables.equals(other.tables);
+ }
+
+ @Override
+ public int hashCode() {
+ return tables.hashCode();
+ }
+
}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Page.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Page.java
new file mode 100644
index 00000000000..8b868ee6df0
--- /dev/null
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Page.java
@@ -0,0 +1,175 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.sql.JDBCType;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Stores a page of data in a columnar format.
+ */
+public class Page extends ResultPage {
+ private final List columnInfo;
+
+ /**
+ * The actual data, one array per column.
+ */
+ private final Object[][] data;
+
+ /**
+ * The number of rows in this page. The {@link #data} arrays may be larger
+ * but data after the end of the arrays is garbage.
+ */
+ private int rows;
+
+ private int maxRows;
+
+ /**
+ * Build empty, call {@link #read(DataInput)} after to fill it.
+ */
+ Page(List columnInfo) {
+ this.columnInfo = columnInfo;
+ data = new Object[columnInfo.size()][];
+ }
+
+ /**
+ * Build with a particular set of rows. Use this for testing.
+ */
+ Page(List columnInfo, Object[][] rows) {
+ this(columnInfo);
+ makeRoomFor(rows.length);
+ this.rows = rows.length;
+ for (int row = 0; row < rows.length; row++) {
+ if (columnInfo.size() != rows[row].length) {
+ throw new IllegalArgumentException("Column count mismatch. Got [" + columnInfo.size()
+ + "] ColumnInfos but [" + rows.length + "] columns on the [" + row + "] row.");
+ }
+ }
+ for (int column = 0; column < columnInfo.size(); column++) {
+ for (int row = 0; row < rows.length; row++) {
+ data[column][row] = rows[row][column];
+ }
+ }
+ }
+
+ public int rows() {
+ return rows;
+ }
+
+ public List columnInfo() {
+ return columnInfo;
+ }
+
+ Object[] column(int index) {
+ if (index < 0 || index >= data.length) {
+ // NOCOMMIT this was once JdbcException. Make sure it isn't now busted
+ throw new IllegalArgumentException("Invalid column [" + index + "] (max is [" + (data.length - 1) + "])");
+ }
+
+ return data[index];
+ }
+
+ public Object entry(int row, int column) {
+ if (row < 0 || row >= rows) {
+ // NOCOMMIT this was once JdbcException. Make sure it isn't now busted
+ throw new IllegalArgumentException("Invalid row [" + row + "] (max is [" + (rows -1) + "])");
+ }
+ return column(column)[row];
+ }
+
+ /**
+ * Read a value from the stream
+ */
+ void read(DataInput in) throws IOException {
+ int rows = in.readInt();
+ // this.rows may be less than the number of rows we have space for
+ if (rows > maxRows) {
+ makeRoomFor(rows);
+ }
+ this.rows = rows;
+
+ for (int row = 0; row < rows; row++) {
+ for (int column = 0; column < columnInfo.size(); column++) {
+ data[column][row] = readValue(in, columnInfo.get(column).type);
+ }
+ }
+ }
+
+ @Override
+ public void write(DataOutput out) throws IOException {
+ int rows = rows();
+ out.writeInt(rows);
+ for (int row = 0; row < rows; row++) {
+ for (int column = 0; column < columnInfo.size(); column++) {
+ JDBCType columnType = columnInfo.get(column).type;
+ writeValue(out, entry(row, column), columnType);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ for (int row = 0; row < rows(); row++) {
+ for (int column = 0; column < columnInfo.size(); column++) {
+ if (column > 0) {
+ b.append(", ");
+ }
+ b.append(entry(row, column));
+ }
+ b.append('\n');
+ }
+ return b.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj instanceof ResultPage == false) {
+ return false;
+ }
+ Page other = (Page) obj;
+ if (rows != other.rows) {
+ return false;
+ }
+ if (false == columnInfo.equals(other.columnInfo)) {
+ return false;
+ }
+ for (int row = 0; row < rows(); row++) {
+ for (int column = 0; column < columnInfo.size(); column++) {
+ if (false == Objects.equals(entry(row, column), other.entry(row, column))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(rows(), columnInfo.size());
+ for (int row = 0; row < rows(); row++) {
+ for (int column = 0; column < columnInfo.size(); column++) {
+ Object entry = entry(row, column);
+ result = result * 31 + (entry == null ? 0 : entry.hashCode());
+ }
+ }
+ return result;
+ }
+
+
+ private void makeRoomFor(int rows) {
+ maxRows = rows;
+ for (int i = 0; i < columnInfo.size(); i++) {
+ Class> type = classOf(columnInfo.get(i).type);
+ data[i] = (Object[]) Array.newInstance(type, rows);
+ }
+ }
+}
\ No newline at end of file
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Proto.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Proto.java
index c8a150e9b41..a3f3c51dbf3 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Proto.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Proto.java
@@ -5,295 +5,100 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
-import java.sql.SQLClientInfoException;
-import java.sql.SQLDataException;
-import java.sql.SQLException;
-import java.sql.SQLRecoverableException;
-import java.sql.SQLSyntaxErrorException;
-import java.sql.SQLTimeoutException;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.function.Function;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto;
-import javax.sql.rowset.serial.SerialException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
-import static java.util.Collections.emptyMap;
-import static java.util.stream.Collectors.toMap;
+/**
+ * Binary protocol for the JDBC. All backwards compatibility is done using the
+ * version number sent in the header.
+ */
+public final class Proto extends AbstractProto {
+ public static final Proto INSTANCE = new Proto();
-//
-// Basic tabular messaging for the JDBC driver
-//
-// Note this message is transmitted through HTTP and thus things like transport error codes
-// are handled through that.
+ private Proto() {}
-// The proto is based around a simple, single request-response model.
-// Note the field order is _important_.
-// To simplify things, the protocol is not meant to be backwards compatible.
-//
-public interface Proto {
-
- // All requests start with
- // magic_number - int - just because
- // version - int - the version the client understands
- // action - int - action to perform
- // (see below)
-
- int MAGIC_NUMBER = 0x0C0DE1DBC;
- int VERSION = 000_000_001;
-
- public interface Header {
- int value();
+ @Override
+ protected RequestType readRequestType(DataInput in) throws IOException {
+ return RequestType.read(in);
}
- // The response start with a similar pattern
- // magic_number
- // version
- // action reply (status)
- // payload
-
- enum Status implements Header {
- // If successful, each method has its own params (describe for each method)
- SUCCESS (0x5000000),
-
- // Expected exceptions contain
- // message - string - exception message
- // exception - string - exception class
- // sql exception - int - to what SqlException type this maps to (see below)
- EXCEPTION(0x3000000),
-
- // Unexpected error contains the following fields
-
- // message - string - exception message
- // exception - string - exception class
- // stacktrace - string - exception stacktrace (should be massaged)
- ERROR (0xF000000);
-
- private static final Map MAP = Arrays.stream(Status.class.getEnumConstants())
- .collect(toMap(Status::value, Function.identity()));
-
- private final int value;
-
- Status(int value) {
- this.value = value;
- }
-
- @Override
- public int value() {
- return value;
- }
-
- public static Status from(int value) {
- return MAP.get(value & 0xF000000);
- }
-
- public static int toSuccess(Action action) {
- return action.value() | SUCCESS.value();
- }
-
- public static int toException(Action action) {
- return action.value() | EXCEPTION.value();
- }
-
- public static int toError(Action action) {
- return action.value() | ERROR.value();
- }
+ @Override
+ protected ResponseType readResponseType(DataInput in) throws IOException {
+ return ResponseType.read(in);
}
- enum SqlExceptionType {
- UNKNOWN (0x001),
- SERIAL (0x010),
- CLIENT_INFO(0x020),
- DATA (0x100),
- SYNTAX (0x200),
-
- RECOVERABLE(0x300),
- TIMEOUT (0x400);
-
+ public enum RequestType implements AbstractProto.RequestType {
+ INFO(InfoRequest::new),
+ META_TABLE(MetaTableRequest::new),
+ META_COLUMN(MetaColumnRequest::new),
+ QUERY_INIT(QueryInitRequest::new),
+ QUERY_PAGE(QueryPageRequest::new),
+// QUERY_CLOSE(QueryClosenRequest::new), TODO implement me
+ ;
- private static final Map MAP = Arrays.stream(SqlExceptionType.class.getEnumConstants())
- .collect(toMap(SqlExceptionType::value, Function.identity()));
+ private final RequestReader reader;
- private final int value;
-
- SqlExceptionType(int value) {
- this.value = value;
+ RequestType(RequestReader reader) {
+ this.reader = reader;
}
- public int value() {
- return value;
- }
-
- public static SqlExceptionType from(int value) {
- return MAP.get(value);
- }
-
- public static SQLException asException(SqlExceptionType type, String message) {
- if (message == null) {
- message = "";
+ static RequestType read(DataInput in) throws IOException {
+ byte b = in.readByte();
+ try {
+ return values()[b];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Unknown response type [" + b + "]", e);
}
- if (type == SERIAL) {
- return new SerialException(message);
- }
- if (type == CLIENT_INFO) {
- return new SQLClientInfoException(message, emptyMap());
- }
- if (type == DATA) {
- return new SQLDataException(message);
- }
- if (type == SYNTAX) {
- return new SQLSyntaxErrorException(message);
- }
- if (type == RECOVERABLE) {
- return new SQLRecoverableException(message);
- }
- if (type == TIMEOUT) {
- return new SQLTimeoutException(message);
- }
-
- return new SQLException("Unexpected 'expected' exception " + type);
- }
- }
-
- //
- // RPC
- //
-
- enum Action implements Header {
-
- //
- // Retrieves information about the server
- //
- //
- // java.version - string
- // java.vendor - string
- // java.class.path - string
- // os.name - string
- // os.version - string
- //
- //
- // node.name - string
- // cluster.name - string
- // version.major - byte
- // version.minor - byte
- // version.number - string
- // version.hash - string
- // version.build - string
- // # nodes - fall back nodes to connect to
- // for each node
- // node.name - string
- // node.address - string
- //
-
- INFO(0x01),
-
-
- //
- // Retrieves metadata about tables
- //
- // Request:
- //
- // name pattern - string
- //
- // Response:
- //
- // # tables - int - index.type
- // for each table
- // name - string - table name
- //
-
- META_TABLE(0x04),
-
- //
- // Retrieves metadata about columns
- //
- // Request:
- //
- // table pattern - string
- // column pattern - string
- //
- // Response:
- //
- // # columns - int - columns that match
- // for each column (MetaColumnInfo):
- // table.name - string - index.type
- // column.name - string - column name
- // data.type - int - data type
- // column.size - int
- // ordinal.position - int - position inside table
-
- META_COLUMN(0x05),
-
-
- // Request (QueryInfo):
- // Contains several _header_ fields
- //
- // fetch-size - int - the number of results returned in a response by the server
- // (TimeoutInfo)
- // client_time - long - milliseconds since the epoch (in UTC)
- // timeout - long - how much time (in ms) the server has to deliver an answer.
- // request_timeout - long - how much time (in ms) a scroll/cursor needs to be kept alive between requests
-
- // And the actual payload.
- //
- // query - string - the actual SQL query
- //
-
- // Response:
- // Header fields (ResultInfo):
- //
- // time_received - long - (in UTC)
- // time_sent - long - (in UTC)
- // request_id - string - id for this page; if it's null it means there are no more results
- // # columns - int - number of columns
- // row schema
- // for each column (ColumnInfo):
- // name - string - name of the column
- // alias - string - if the column has an alias or label
- // table - string - index.type
- // schema - string - TBD (could be user)
- // catalog - string - TBD (could be cluster/node id)
- // type - int - JDBC type
- // # rows - int - number of rows
- // for each row, the actual values (the schema is not sent anymore)
-
- QUERY_INIT(0x10),
-
- QUERY_PAGE(0x15),
-
- // Request (PageInfo):
-
- // request_id - string - the request/scroll id
- // (TimeoutInfo):
- // client_time - long - ms since the epoch (in UTC)
- // timeout - long - how much time (in ms) the server has to deliver an answer.
- // request_timeout - long - how much time (in ms) the request needs to be kept alive until the next request
-
-
- // Returns (ResultPageInfo):
- // request_id - string - id for this page; if it's null it means there are no more results
- // # rows - int - number of rows
- // for each row, the actual values (the schema is not sent anymore)
-
- // TODO: needs implementing
- QUERY_CLOSE(0x19);
-
-
- private static final Map MAP = Arrays.stream(Action.class.getEnumConstants())
- .collect(toMap(Action::value, Function.identity()));
-
- private final int value;
-
- Action(int value) {
- this.value = value;
}
@Override
- public int value() {
- return value;
+ public void write(DataOutput out) throws IOException {
+ out.writeByte(ordinal());
}
- public static Action from(int value) {
- return MAP.get(value & 0x00000FF);
+ @Override
+ public RequestReader reader() {
+ return reader;
+ }
+ }
+
+ public enum ResponseType implements AbstractProto.ResponseType {
+ EXCEPTION(ExceptionResponse::new),
+ ERROR(ErrorResponse::new),
+ INFO(InfoResponse::new),
+ META_TABLE(MetaTableResponse::new),
+ META_COLUMN(MetaColumnResponse::new),
+ QUERY_INIT(QueryInitResponse::new),
+ QUERY_PAGE(QueryPageResponse::new),
+// QUERY_CLOSE(QueryClosenResponse::new) TODO implement me
+ ;
+
+ private final ResponseReader reader;
+
+ ResponseType(ResponseReader reader) {
+ this.reader = reader;
+ }
+
+ static ResponseType read(DataInput in) throws IOException {
+ byte b = in.readByte();
+ try {
+ return values()[b];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Unknown response type [" + b + "]", e);
+ }
+ }
+
+ @Override
+ public void write(DataOutput out) throws IOException {
+ out.writeByte(ordinal());
+ }
+
+ @Override
+ public ResponseReader reader() {
+ return reader;
}
}
}
\ No newline at end of file
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitRequest.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitRequest.java
index 96722ebe73a..f6071af55c3 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitRequest.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitRequest.java
@@ -5,7 +5,8 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
import java.io.DataInput;
import java.io.DataOutput;
@@ -20,15 +21,13 @@ public class QueryInitRequest extends Request {
public final TimeoutInfo timeout;
public QueryInitRequest(int fetchSize, String query, TimeZone timeZone, TimeoutInfo timeout) {
- super(Action.QUERY_INIT);
this.fetchSize = fetchSize;
this.query = query;
this.timeZone = timeZone;
this.timeout = timeout;
}
- QueryInitRequest(DataInput in) throws IOException {
- super(Action.QUERY_INIT);
+ QueryInitRequest(int clientVersion, DataInput in) throws IOException {
fetchSize = in.readInt();
query = in.readUTF();
timeZone = TimeZone.getTimeZone(in.readUTF());
@@ -36,24 +35,28 @@ public class QueryInitRequest extends Request {
}
@Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(action.value()); // NOCOMMIT this should be written by the caller
+ public void write(DataOutput out) throws IOException {
out.writeInt(fetchSize);
out.writeUTF(query);
out.writeUTF(timeZone.getID());
- timeout.encode(out);
+ timeout.write(out);
}
@Override
- public String toString() {
+ protected String toStringBody() {
StringBuilder b = new StringBuilder();
- b.append("SqlInitReq[").append(query).append(']');
+ b.append("query=[").append(query).append(']');
if (false == timeZone.getID().equals("UTC")) {
- b.append('[').append(timeZone.getID()).append(']');
+ b.append(" timeZone=[").append(timeZone.getID()).append(']');
}
return b.toString();
}
+ @Override
+ public RequestType requestType() {
+ return RequestType.QUERY_INIT;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitResponse.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitResponse.java
index f92b6c5f9b1..1bca9fbe5ae 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitResponse.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitResponse.java
@@ -5,78 +5,96 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
+import java.util.Objects;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Status;
+import static java.util.Collections.unmodifiableList;
-import static java.lang.String.format;
-import static java.util.Collections.emptyList;
-
-public class QueryInitResponse extends DataResponse {
-
- public final long serverTimeQueryReceived, serverTimeResponseSent, timeSpent;
+public class QueryInitResponse extends Response {
+ public final long serverTimeQueryReceived, serverTimeResponseSent;
public final String requestId;
public final List columns;
+ public final ResultPage data;
- public QueryInitResponse(long serverTimeQueryReceived, long serverTimeResponseSent, String requestId, List columns, Object data) {
- super(Action.QUERY_INIT, data);
+ public QueryInitResponse(long serverTimeQueryReceived, long serverTimeResponseSent, String requestId, List columns,
+ ResultPage data) {
this.serverTimeQueryReceived = serverTimeQueryReceived;
this.serverTimeResponseSent = serverTimeResponseSent;
- this.timeSpent = serverTimeQueryReceived - serverTimeResponseSent;
this.requestId = requestId;
this.columns = columns;
+ this.data = data;
+ }
+
+ QueryInitResponse(Request request, DataInput in) throws IOException {
+ serverTimeQueryReceived = in.readLong();
+ serverTimeResponseSent = in.readLong();
+ requestId = in.readUTF();
+ int size = in.readInt();
+ List columns = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ columns.add(new ColumnInfo(in));
+ }
+ this.columns = unmodifiableList(columns);
+ Page data = new Page(columns);
+ data.read(in);
+ this.data = data;
}
@Override
- public String toString() {
- return format(Locale.ROOT, "QueryInitRes[%s]", requestId);
- }
-
- @Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(Status.toSuccess(action));
-
+ public void write(int clientVersion, DataOutput out) throws IOException {
out.writeLong(serverTimeQueryReceived);
out.writeLong(serverTimeResponseSent);
out.writeUTF(requestId);
-
out.writeInt(columns.size());
for (ColumnInfo c : columns) {
- out.writeUTF(c.name);
- out.writeUTF(c.label);
- out.writeUTF(c.table);
- out.writeUTF(c.schema);
- out.writeUTF(c.catalog);
- out.writeInt(c.type);
+ c.write(out);
}
+ data.write(out);
}
- public static QueryInitResponse decode(DataInput in) throws IOException {
- long serverTimeQueryReceived = in.readLong();
- long serverTimeResponseSent = in.readLong();
- String requestId = in.readUTF();
+ @Override
+ protected String toStringBody() {
+ return "timeReceived=[" + serverTimeQueryReceived
+ + "] timeSent=[" + serverTimeResponseSent
+ + "] requestId=[" + requestId
+ + "] columns=" + columns
+ + " data=[\n" + data + "]";
+ }
+ @Override
+ public RequestType requestType() {
+ return RequestType.QUERY_INIT;
+ }
- int length = in.readInt();
- List list = length < 1 ? emptyList() : new ArrayList<>(length);
+ @Override
+ public ResponseType responseType() {
+ return ResponseType.QUERY_INIT;
+ }
- for (int i = 0; i < length; i++) {
- String name = in.readUTF();
- String label = in.readUTF();
- String table = in.readUTF();
- String schema = in.readUTF();
- String catalog = in.readUTF();
- int type = in.readInt();
-
- list.add(new ColumnInfo(name, type, schema, catalog, table, label));
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
}
+ QueryInitResponse other = (QueryInitResponse) obj;
+ return serverTimeQueryReceived == other.serverTimeQueryReceived
+ && serverTimeResponseSent == other.serverTimeResponseSent
+ && requestId.equals(other.requestId)
+ && columns.equals(other.columns);
+ // NOCOMMIT data
+ }
- return new QueryInitResponse(serverTimeQueryReceived, serverTimeResponseSent, requestId, list, null);
+ @Override
+ public int hashCode() {
+ return Objects.hash(serverTimeQueryReceived, serverTimeResponseSent, requestId, columns); // NOCOMMIT data
}
}
\ No newline at end of file
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageRequest.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageRequest.java
index e71da30130d..5b0c1c21529 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageRequest.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageRequest.java
@@ -5,41 +5,72 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.Nullable;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
-import java.util.Locale;
-
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-
-import static java.lang.String.format;
+import java.util.Objects;
public class QueryPageRequest extends Request {
-
public final String requestId;
public final TimeoutInfo timeout;
+ private final transient Page data;
- public QueryPageRequest(String requestId, TimeoutInfo timeout) {
- super(Action.QUERY_PAGE);
+ public QueryPageRequest(String requestId, TimeoutInfo timeout, @Nullable Page data) {
+ if (requestId == null) {
+ throw new IllegalArgumentException("[requestId] must not be null");
+ }
+ if (timeout == null) {
+ throw new IllegalArgumentException("[timeout] must not be null");
+ }
this.requestId = requestId;
this.timeout = timeout;
+ this.data = data;
+ }
+
+ QueryPageRequest(int clientVersion, DataInput in) throws IOException {
+ this.requestId = in.readUTF();
+ this.timeout = new TimeoutInfo(in);
+ this.data = null; // Data isn't used on the server side
}
@Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(action.value());
+ public void write(DataOutput out) throws IOException {
out.writeUTF(requestId);
- timeout.encode(out);
+ timeout.write(out);
}
- public static QueryPageRequest decode(DataInput in) throws IOException {
- String requestId = in.readUTF();
- TimeoutInfo timeout = new TimeoutInfo(in);
- return new QueryPageRequest(requestId, timeout);
+ public Page data() {
+ return data;
}
@Override
- public String toString() {
- return format(Locale.ROOT, "QueryPageReq[%s]", requestId);
+ protected String toStringBody() {
+ return requestId;
+ }
+
+ @Override
+ public RequestType requestType() {
+ return RequestType.QUERY_PAGE;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ QueryPageRequest other = (QueryPageRequest) obj;
+ return requestId.equals(other.requestId)
+ && timeout.equals(other.timeout);
+ // data is intentionally ignored
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(requestId, timeout);
+ // data is intentionally ignored
}
}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageResponse.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageResponse.java
index 828860d71d5..85b8bb7eea9 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageResponse.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageResponse.java
@@ -5,38 +5,72 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
-import java.util.Locale;
-
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Status;
-
-import static java.lang.String.format;
-
-public class QueryPageResponse extends DataResponse {
+import java.util.Objects;
+public class QueryPageResponse extends Response {
public final String requestId;
+ private final ResultPage data;
- public QueryPageResponse(String requestId, Object data) {
- super(Action.QUERY_PAGE, data);
+ public QueryPageResponse(String requestId, ResultPage data) {
+ if (requestId == null) {
+ throw new IllegalArgumentException("[requestId] must not be null");
+ }
+ if (data == null) {
+ throw new IllegalArgumentException("[data] must not be null");
+ }
this.requestId = requestId;
+ this.data = data;
+ }
+
+ QueryPageResponse(Request request, DataInput in) throws IOException {
+ this.requestId = in.readUTF();
+ QueryPageRequest queryPageRequest = (QueryPageRequest) request;
+ data = queryPageRequest.data();
+ queryPageRequest.data().read(in);
}
@Override
- public void encode(DataOutput out) throws IOException {
- out.writeInt(Status.toSuccess(action));
+ public void write(int clientVersion, DataOutput out) throws IOException {
out.writeUTF(requestId);
- }
-
- public static QueryPageResponse decode(DataInput in) throws IOException {
- String requestId = in.readUTF();
- return new QueryPageResponse(requestId, null);
+ data.write(out);
}
@Override
- public String toString() {
- return format(Locale.ROOT, "QueryPageRes[%s]", requestId);
+ protected String toStringBody() {
+ return "requestId=[" + requestId
+ + "] data=[\n" + data + "]";
+ }
+
+ @Override
+ public RequestType requestType() {
+ return RequestType.QUERY_PAGE;
+ }
+
+ @Override
+ public ResponseType responseType() {
+ return ResponseType.QUERY_PAGE;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ QueryPageResponse other = (QueryPageResponse) obj;
+ return requestId.equals(other.requestId)
+ && data.equals(other.data);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(requestId, data);
}
}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Request.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Request.java
deleted file mode 100644
index 9db872e8f12..00000000000
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Request.java
+++ /dev/null
@@ -1,15 +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.jdbc.net.protocol;
-
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-
-public abstract class Request extends Message {
-
- public Request(Action action) {
- super(action);
- }
-}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Response.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Response.java
deleted file mode 100644
index cdb12ed0c31..00000000000
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/Response.java
+++ /dev/null
@@ -1,15 +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.jdbc.net.protocol;
-
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-
-public abstract class Response extends Message {
-
- public Response(Action action) {
- super(action);
- }
-}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ProtoUtils.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ResultPage.java
similarity index 51%
rename from sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ProtoUtils.java
rename to sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ResultPage.java
index e695ae3fa1b..551125333bc 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ProtoUtils.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ResultPage.java
@@ -5,115 +5,24 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.protocol;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Status;
-
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.Blob;
+import java.sql.Clob;
import java.sql.JDBCType;
-import java.util.Locale;
-import static java.lang.String.format;
-import static java.sql.Types.BIGINT;
-import static java.sql.Types.BINARY;
-import static java.sql.Types.BIT;
-import static java.sql.Types.BOOLEAN;
-import static java.sql.Types.CHAR;
-import static java.sql.Types.DOUBLE;
-import static java.sql.Types.FLOAT;
-import static java.sql.Types.INTEGER;
-import static java.sql.Types.LONGVARBINARY;
-import static java.sql.Types.LONGVARCHAR;
-import static java.sql.Types.NULL;
-import static java.sql.Types.REAL;
-import static java.sql.Types.SMALLINT;
-import static java.sql.Types.TIMESTAMP;
-import static java.sql.Types.TINYINT;
-import static java.sql.Types.VARBINARY;
-import static java.sql.Types.VARCHAR;
-import static org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.MAGIC_NUMBER;
-import static org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.VERSION;
-
-public abstract class ProtoUtils {
-
- private static final byte[] EMPTY_BYTES = new byte[0];
-
- public static void write(DataOutput out, Message m) throws IOException {
- out.writeInt(MAGIC_NUMBER);
- out.writeInt(VERSION);
- m.encode(out);
- }
-
- public static Request readRequest(DataInput in) throws IOException {
- switch (Action.from(in.readInt())) {
- case INFO:
- return InfoRequest.decode(in);
- case META_TABLE:
- return MetaTableRequest.decode(in);
- case META_COLUMN:
- return MetaColumnRequest.decode(in);
- case QUERY_INIT:
- return new QueryInitRequest(in);
- case QUERY_PAGE:
- return QueryPageRequest.decode(in);
- default:
- // cannot find action type
- return null;
- }
- }
-
- public static Response readResponse(DataInput in, int header) throws IOException {
- Action action = Action.from(header);
-
- switch (Status.from(header)) {
- case EXCEPTION:
- return ExceptionResponse.decode(in, action);
- case ERROR:
- return ErrorResponse.decode(in, action);
- case SUCCESS:
- switch (action) {
- case INFO:
- return InfoResponse.decode(in);
- case META_TABLE:
- return MetaTableResponse.decode(in);
- case META_COLUMN:
- return MetaColumnResponse.decode(in);
- case QUERY_INIT:
- return QueryInitResponse.decode(in);
- case QUERY_PAGE:
- return QueryPageResponse.decode(in);
- default:
- // cannot find action type
- // NOCOMMIT it feels like this should throw *something*
- return null;
- }
- default:
- return null;
- }
- }
-
- public static String readHeader(DataInput in) throws IOException {
- // NOCOMMIT why not just throw?
- int magic = in.readInt();
- if (MAGIC_NUMBER != magic) {
- return "Invalid protocol";
- }
- int ver = in.readInt();
- if (VERSION != ver) {
- return format(Locale.ROOT, "Expected JDBC protocol version %s, found %s", VERSION, ver);
- }
-
- return null;
- }
-
- //
- // value read
- //
+/**
+ * Abstract base class for a page of results. The canonical implementation in {@link Page}
+ * and implementation must write usings the same format as {@linkplain Page}.
+ */
+public abstract class ResultPage {
+ public abstract void write(DataOutput out) throws IOException;
// See Jdbc spec, appendix B
@SuppressWarnings("unchecked")
- public static T readValue(DataInput in, int type) throws IOException {
+ protected static T readValue(DataInput in, JDBCType type) throws IOException {
// NOCOMMIT feels slippery here
Object result;
byte hasNext = in.readByte();
@@ -124,6 +33,7 @@ public abstract class ProtoUtils {
switch (type) {
case NULL:
// used to move the stream forward
+ // NOCOMMIT why serialize NULL types at all?
in.readBoolean();
return null;
case BIT:
@@ -153,14 +63,9 @@ public abstract class ProtoUtils {
case VARBINARY:
case LONGVARBINARY:
int size = in.readInt();
- if (size == 0) {
- result = EMPTY_BYTES;
- }
- else {
- byte[] ar = new byte[size];
- in.readFully(ar, 0, size);
- result = ar;
- }
+ byte[] ar = new byte[size];
+ in.readFully(ar, 0, size);
+ result = ar;
break;
case CHAR:
case VARCHAR:
@@ -172,12 +77,12 @@ public abstract class ProtoUtils {
result = in.readLong();
break;
default:
- throw new IOException("Don't know how to read type [" + type + " / " + JDBCType.valueOf(type) + "]");
+ throw new IOException("Don't know how to read type [" + type + "]");
}
return (T) result;
}
- public static void writeValue(DataOutput out, Object o, int type) throws IOException {
+ protected static void writeValue(DataOutput out, Object o, JDBCType type) throws IOException {
if (o == null) {
out.writeByte(0);
return;
@@ -233,6 +138,53 @@ public abstract class ProtoUtils {
out.writeLong(((Number) o).longValue());
return;
default:
+ throw new IOException("Don't know how to write type [" + type + "]");
}
}
-}
\ No newline at end of file
+
+ /**
+ * The type of the array used to store columns of this type.
+ */
+ protected static Class> classOf(JDBCType jdbcType) {
+ switch (jdbcType) {
+ case NUMERIC:
+ case DECIMAL:
+ return BigDecimal.class;
+ case BOOLEAN:
+ case BIT:
+ return Boolean.class;
+ case TINYINT:
+ return Byte.class;
+ case SMALLINT:
+ return Short.class;
+ case INTEGER:
+ // NOCOMMIT should we be using primitives instead?
+ return Integer.class;
+ case BIGINT:
+ return Long.class;
+ case REAL:
+ return Float.class;
+ case FLOAT:
+ case DOUBLE:
+ return Double.class;
+ case BINARY:
+ case VARBINARY:
+ case LONGVARBINARY:
+ return byte[].class;
+ case CHAR:
+ case VARCHAR:
+ case LONGVARCHAR:
+ return String.class;
+ case DATE:
+ case TIME:
+ case TIMESTAMP:
+ return Long.class;
+ case BLOB:
+ return Blob.class;
+ case CLOB:
+ return Clob.class;
+ default:
+ throw new IllegalArgumentException("Unsupported JDBC type [" + jdbcType + "]");
+ }
+ }
+}
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/StringUtils.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/StringUtils.java
deleted file mode 100644
index 428d795b203..00000000000
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/StringUtils.java
+++ /dev/null
@@ -1,76 +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.jdbc.net.protocol;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.StringTokenizer;
-
-abstract class StringUtils {
-
- static final String EMPTY = "";
-
- static String nullAsEmpty(String string) {
- return string == null ? EMPTY : string;
- }
-
- static boolean hasText(CharSequence sequence) {
- if (!hasLength(sequence)) {
- return false;
- }
- int length = sequence.length();
- for (int i = 0; i < length; i++) {
- if (!Character.isWhitespace(sequence.charAt(i))) {
- return true;
- }
- }
- return false;
- }
-
- static boolean hasLength(CharSequence sequence) {
- return (sequence != null && sequence.length() > 0);
- }
-
- static String[] splitToIndexAndType(String pattern) {
- List tokens = tokenize(pattern, ".");
-
- String[] results = new String[2];
- if (tokens.size() == 2) {
- results[0] = tokens.get(0);
- results[1] = tokens.get(1);
- }
- else {
- results[0] = nullAsEmpty(pattern);
- results[1] = EMPTY;
- }
-
- return results;
- }
-
- static List tokenize(String string, String delimiters) {
- return tokenize(string, delimiters, true, true);
- }
-
- static List tokenize(String string, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
- if (!hasText(string)) {
- return Collections.emptyList();
- }
- StringTokenizer st = new StringTokenizer(string, delimiters);
- List tokens = new ArrayList();
- while (st.hasMoreTokens()) {
- String token = st.nextToken();
- if (trimTokens) {
- token = token.trim();
- }
- if (!ignoreEmptyTokens || token.length() > 0) {
- tokens.add(token);
- }
- }
- return tokens;
- }
-
-}
\ No newline at end of file
diff --git a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/TimeoutInfo.java b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/TimeoutInfo.java
index 3408cbdb5a7..1ed1c0c2634 100644
--- a/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/TimeoutInfo.java
+++ b/sql/jdbc-proto/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/TimeoutInfo.java
@@ -26,7 +26,7 @@ public class TimeoutInfo {
requestTimeout = in.readLong();
}
- void encode(DataOutput out) throws IOException {
+ void write(DataOutput out) throws IOException {
out.writeLong(clientTime);
out.writeLong(timeout);
out.writeLong(requestTimeout);
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java
new file mode 100644
index 00000000000..beaf9af219a
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java
@@ -0,0 +1,56 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.sql.JDBCType;
+
+import static org.elasticsearch.xpack.sql.test.RoundTripTestUtils.assertRoundTrip;
+
+public class ColumnInfoTests extends ESTestCase {
+ static ColumnInfo varcharInfo(String name) {
+ return new ColumnInfo(name, JDBCType.VARCHAR, "", "", "", "");
+ }
+
+ static ColumnInfo intInfo(String name) {
+ return new ColumnInfo(name, JDBCType.INTEGER, "", "", "", "");
+ }
+
+ static ColumnInfo doubleInfo(String name) {
+ return new ColumnInfo(name, JDBCType.DOUBLE, "", "", "", "");
+ }
+
+ static Object randomValueFor(ColumnInfo info) {
+ switch (info.type) {
+ case VARCHAR: return randomAlphaOfLength(5);
+ case INTEGER: return randomInt();
+ case DOUBLE: return randomDouble();
+ default:
+ throw new IllegalArgumentException("Unsupported type [" + info.type + "]");
+ }
+ }
+
+ static ColumnInfo randomColumnInfo() {
+ return new ColumnInfo(randomAlphaOfLength(5), randomFrom(JDBCType.values()), randomAlphaOfLength(5), randomAlphaOfLength(5),
+ randomAlphaOfLength(5), randomAlphaOfLength(5));
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTrip(randomColumnInfo(), ColumnInfo::write, ColumnInfo::new);
+ }
+
+ public void testToString() {
+ assertEquals("test.doc.a",
+ new ColumnInfo("a", JDBCType.VARCHAR, "test.doc", "as", "ads", "lab").toString());
+ assertEquals("test.doc.a",
+ new ColumnInfo("a", JDBCType.VARCHAR, "test.doc", "", "", "").toString());
+ assertEquals("string", varcharInfo("string").toString());
+ assertEquals("int", intInfo("int").toString());
+ assertEquals("d", doubleInfo("d").toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ErrorResponseTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ErrorResponseTests.java
new file mode 100644
index 00000000000..d8773977d4b
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ErrorResponseTests.java
@@ -0,0 +1,28 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+
+import java.io.IOException;
+
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+
+public class ErrorResponseTests extends ESTestCase {
+ static ErrorResponse randomErrorResponse() {
+ return new ErrorResponse(RequestType.META_TABLE, randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5));
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(MetaTableRequestTests::randomMetaTableRequest, randomErrorResponse());
+ }
+
+ public void testToString() {
+ assertEquals("ErrorResponse",
+ new ErrorResponse(RequestType.INFO, "test", "test", "stack\nstack").toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ExceptionResponseTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ExceptionResponseTests.java
new file mode 100644
index 00000000000..02d5fc7dac1
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ExceptionResponseTests.java
@@ -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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
+
+import java.io.IOException;
+
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+
+public class ExceptionResponseTests extends ESTestCase {
+ static ExceptionResponse randomExceptionResponse() {
+ return new ExceptionResponse(RequestType.META_TABLE, randomAlphaOfLength(5), randomAlphaOfLength(5),
+ randomFrom(SqlExceptionType.values()));
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(MetaTableRequestTests::randomMetaTableRequest, randomExceptionResponse());
+ }
+
+ public void testToString() {
+ assertEquals("ExceptionResponse",
+ new ExceptionResponse(RequestType.INFO, "test", "test", SqlExceptionType.SYNTAX).toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoRequestTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoRequestTests.java
new file mode 100644
index 00000000000..7fefba5eba4
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoRequestTests.java
@@ -0,0 +1,28 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+
+public class InfoRequestTests extends ESTestCase {
+ static InfoRequest randomInfoRequest() {
+ return new InfoRequest(randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5),
+ randomAlphaOfLength(5), randomAlphaOfLength(5));
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(randomInfoRequest());
+ }
+
+ public void testToString() {
+ assertEquals("InfoRequest",
+ new InfoRequest("1.8.0_131", "testvendor", "testcp", "Mac OS X", "10.12.5").toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoResponseTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoResponseTests.java
new file mode 100644
index 00000000000..be2087aeb57
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/InfoResponseTests.java
@@ -0,0 +1,28 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+
+public class InfoResponseTests extends ESTestCase {
+ static InfoResponse randomInfoResponse() {
+ return new InfoResponse(randomAlphaOfLength(5), randomAlphaOfLength(5), randomByte(), randomByte(),
+ randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5));
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(InfoRequestTests::randomInfoRequest, randomInfoResponse());
+ }
+
+ public void testToString() {
+ assertEquals("InfoResponse",
+ new InfoResponse("adsf", "test_cluster", (byte) 6, (byte) 0, "6.0.0", "feed", "date").toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/JdbcRoundTripTestUtils.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/JdbcRoundTripTestUtils.java
new file mode 100644
index 00000000000..43bfc20dd71
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/JdbcRoundTripTestUtils.java
@@ -0,0 +1,29 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+import org.elasticsearch.xpack.sql.test.RoundTripTestUtils;
+
+import java.io.IOException;
+import java.util.function.Supplier;
+
+public final class JdbcRoundTripTestUtils {
+ private JdbcRoundTripTestUtils() {
+ // Just static utilities
+ }
+
+ static void assertRoundTripCurrentVersion(Request request) throws IOException {
+ RoundTripTestUtils.assertRoundTrip(request, Proto.INSTANCE::writeRequest, Proto.INSTANCE::readRequest);
+ }
+
+ static void assertRoundTripCurrentVersion(Supplier request, Response response) throws IOException {
+ RoundTripTestUtils.assertRoundTrip(response,
+ (r, out) -> Proto.INSTANCE.writeResponse(r, Proto.CURRENT_VERSION, out),
+ in -> Proto.INSTANCE.readResponse(request.get(), in));
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnInfoTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnInfoTests.java
new file mode 100644
index 00000000000..a0990fb905a
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnInfoTests.java
@@ -0,0 +1,29 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.sql.JDBCType;
+
+import static org.elasticsearch.xpack.sql.test.RoundTripTestUtils.assertRoundTrip;
+
+public class MetaColumnInfoTests extends ESTestCase {
+ static MetaColumnInfo randomMetaColumnInfo() {
+ return new MetaColumnInfo(randomAlphaOfLength(5), randomAlphaOfLength(5), randomFrom(JDBCType.values()),
+ between(1, Integer.MAX_VALUE), between(1, Integer.MAX_VALUE));
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTrip(randomMetaColumnInfo(), MetaColumnInfo::write, MetaColumnInfo::new);
+ }
+
+ public void testToString() {
+ assertEquals("test.doc.col",
+ new MetaColumnInfo("test.doc", "col", JDBCType.VARCHAR, 100, 1).toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnRequestTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnRequestTests.java
new file mode 100644
index 00000000000..edfaa0ec322
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnRequestTests.java
@@ -0,0 +1,26 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+
+public class MetaColumnRequestTests extends ESTestCase {
+ public static MetaColumnRequest randomMetaColumnRequest() {
+ return new MetaColumnRequest(randomAlphaOfLength(10), randomAlphaOfLength(10));
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(randomMetaColumnRequest());
+ }
+
+ public void testToString() {
+ assertEquals("MetaColumnRequest", new MetaColumnRequest("test.do%", "d%").toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnResponseTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnResponseTests.java
new file mode 100644
index 00000000000..8928cf8a448
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaColumnResponseTests.java
@@ -0,0 +1,44 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.sql.JDBCType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.Collections.emptyList;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaColumnInfoTests.randomMetaColumnInfo;
+
+
+public class MetaColumnResponseTests extends ESTestCase {
+ static MetaColumnResponse randomMetaColumnResponse() {
+ int size = between(0, 10);
+ List columns = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ columns.add(randomMetaColumnInfo());
+ }
+ return new MetaColumnResponse(columns);
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(MetaColumnRequestTests::randomMetaColumnRequest, randomMetaColumnResponse());
+ }
+
+ public void testToString() {
+ assertEquals("MetaColumnResponse<>", new MetaColumnResponse(emptyList()).toString());
+ assertEquals("MetaColumnResponse, "
+ + "a.doc.col2, "
+ + "b.doc.col1>", new MetaColumnResponse(Arrays.asList(
+ new MetaColumnInfo("a.doc", "col1", JDBCType.VARCHAR, 100, 1),
+ new MetaColumnInfo("a.doc", "col2", JDBCType.INTEGER, 16, 2),
+ new MetaColumnInfo("b.doc", "col1", JDBCType.VARCHAR, 100, 1))).toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableRequestTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableRequestTests.java
new file mode 100644
index 00000000000..52caf21859c
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableRequestTests.java
@@ -0,0 +1,26 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+
+public class MetaTableRequestTests extends ESTestCase {
+ public static MetaTableRequest randomMetaTableRequest() {
+ return new MetaTableRequest(randomAlphaOfLength(10));
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(randomMetaTableRequest());
+ }
+
+ public void testToString() {
+ assertEquals("MetaTableRequest", new MetaTableRequest("test.do%").toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableResponseTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableResponseTests.java
new file mode 100644
index 00000000000..c0bcfecac9f
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/MetaTableResponseTests.java
@@ -0,0 +1,36 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.Collections.emptyList;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+
+public class MetaTableResponseTests extends ESTestCase {
+ static MetaTableResponse randomMetaTableResponse() {
+ int size = between(0, 10);
+ List tables = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ tables.add(randomAlphaOfLength(5));
+ }
+ return new MetaTableResponse(tables);
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(MetaTableRequestTests::randomMetaTableRequest, randomMetaTableResponse());
+ }
+
+ public void testToString() {
+ assertEquals("MetaTableResponse<>", new MetaTableResponse(emptyList()).toString());
+ assertEquals("MetaTableResponse", new MetaTableResponse(Arrays.asList("a.doc", "b.doc", "c.doc")).toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/PageTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/PageTests.java
new file mode 100644
index 00000000000..3bbf253cb59
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/PageTests.java
@@ -0,0 +1,92 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Supplier;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfoTests.doubleInfo;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfoTests.intInfo;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfoTests.randomValueFor;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfoTests.varcharInfo;
+import static org.elasticsearch.xpack.sql.test.RoundTripTestUtils.assertRoundTrip;
+import static org.elasticsearch.xpack.sql.test.RoundTripTestUtils.roundTrip;
+
+public class PageTests extends ESTestCase {
+ static Page randomPage() {
+ int columns = between(0, 10);
+ List columnInfo = new ArrayList<>();
+ for (int c = 0; c < columns; c++) {
+ @SuppressWarnings("unchecked")
+ Supplier info = randomFrom(
+ () -> varcharInfo(randomAlphaOfLength(5)),
+ () -> intInfo(randomAlphaOfLength(5)),
+ () -> doubleInfo(randomAlphaOfLength(5)));
+ columnInfo.add(info.get());
+ }
+ return randomPageContents(columnInfo);
+ }
+
+ static Page randomPageContents(List columnInfo) {
+ Object[][] rows = new Object[between(0, 10)][];
+ for (int r = 0; r < rows.length; r++) {
+ rows[r] = new Object[columnInfo.size()];
+ for (int c = 0; c < columnInfo.size(); c++) {
+ rows[r][c] = randomValueFor(columnInfo.get(c));
+ }
+ }
+ return new Page(columnInfo, rows);
+ }
+
+ public void testRoundTripNoReuse() throws IOException {
+ Page example = randomPage();
+ assertRoundTrip(example, Page::write, in -> {
+ Page page = new Page(example.columnInfo());
+ page.read(in);
+ return page;
+ });
+ }
+
+ public void testRoundTripReuse() throws IOException {
+ Page example = randomPage();
+ Page target = new Page(example.columnInfo());
+ roundTrip(example, Page::write, in -> {target.read(in); return null;});
+ assertEquals(example, target);
+
+ example = randomPageContents(example.columnInfo());
+ roundTrip(example, Page::write, in -> {target.read(in); return null;});
+ assertEquals(example, target);
+ }
+
+ public void testToString() {
+ assertEquals("\n\n",
+ new Page(emptyList(), new Object[][] {
+ new Object[] {},
+ new Object[] {},
+ }).toString());
+ assertEquals("test\n",
+ new Page(singletonList(varcharInfo("a")), new Object[][] {
+ new Object[] {"test"}
+ }).toString());
+ assertEquals("test, 1\n",
+ new Page(Arrays.asList(varcharInfo("a"), intInfo("b")), new Object[][] {
+ new Object[] {"test", 1}
+ }).toString());
+ assertEquals("test, 1\nbar, 7\n",
+ new Page(Arrays.asList(varcharInfo("a"), intInfo("b")), new Object[][] {
+ new Object[] {"test", 1},
+ new Object[] {"bar", 7}
+ }).toString());
+
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitRequestTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitRequestTests.java
index 06530c78878..618324d34f5 100644
--- a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitRequestTests.java
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitRequestTests.java
@@ -8,9 +8,11 @@ package org.elasticsearch.xpack.sql.jdbc.net.protocol;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
+import java.util.TimeZone;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
import static org.elasticsearch.xpack.sql.jdbc.net.protocol.TimeoutInfoTests.randomTimeoutInfo;
-import static org.elasticsearch.xpack.sql.test.RoundTripTestUtils.assertRoundTrip;
+
public class QueryInitRequestTests extends ESTestCase {
public static QueryInitRequest randomQueryInitRequest() {
@@ -18,6 +20,13 @@ public class QueryInitRequestTests extends ESTestCase {
}
public void testRoundTrip() throws IOException {
- assertRoundTrip(randomQueryInitRequest(), QueryInitRequest::encode, in -> (QueryInitRequest) ProtoUtils.readRequest(in));
+ assertRoundTripCurrentVersion(randomQueryInitRequest());
+ }
+
+ public void testToString() {
+ assertEquals("QueryInitRequest",
+ new QueryInitRequest(10, "SELECT * FROM test.doc", TimeZone.getTimeZone("UTC"), new TimeoutInfo(1, 1, 1)).toString());
+ assertEquals("QueryInitRequest",
+ new QueryInitRequest(10, "SELECT * FROM test.doc", TimeZone.getTimeZone("GMT-5"), new TimeoutInfo(1, 1, 1)).toString());
}
}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitResponseTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitResponseTests.java
new file mode 100644
index 00000000000..d7ff14545d2
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryInitResponseTests.java
@@ -0,0 +1,36 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+
+import static java.util.Collections.singletonList;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfoTests.varcharInfo;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.PageTests.randomPage;
+
+public class QueryInitResponseTests extends ESTestCase {
+ static QueryInitResponse randomQueryInitResponse() {
+ Page page = randomPage();
+ return new QueryInitResponse(randomNonNegativeLong(), randomNonNegativeLong(), randomAlphaOfLength(5), page.columnInfo(), page);
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(QueryInitRequestTests::randomQueryInitRequest, randomQueryInitResponse());
+ }
+
+ public void testToString() {
+ Page page = new Page(singletonList(varcharInfo("a")), new Object[][] {
+ new Object[] {"test"},
+ new Object[] {"string"},
+ });
+ assertEquals("QueryInitResponse] data=["
+ + "\ntest\nstring\n]>",
+ new QueryInitResponse(123, 456, "test_id", page.columnInfo(), page).toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageRequestTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageRequestTests.java
new file mode 100644
index 00000000000..8dced293faa
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageRequestTests.java
@@ -0,0 +1,27 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.TimeoutInfoTests.randomTimeoutInfo;
+
+public class QueryPageRequestTests extends ESTestCase {
+ public static QueryPageRequest randomQueryPageRequest(Page page) {
+ return new QueryPageRequest(randomAlphaOfLength(5), randomTimeoutInfo(), page);
+ }
+
+ public void testRoundTrip() throws IOException {
+ assertRoundTripCurrentVersion(randomQueryPageRequest(null));
+ }
+
+ public void testToString() {
+ assertEquals("QueryPageRequest", new QueryPageRequest("test_id", new TimeoutInfo(1, 1, 1), null).toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageResponseTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageResponseTests.java
new file mode 100644
index 00000000000..0968a17252f
--- /dev/null
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/QueryPageResponseTests.java
@@ -0,0 +1,34 @@
+/*
+ * 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.jdbc.net.protocol;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+
+import static java.util.Collections.singletonList;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfoTests.varcharInfo;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcRoundTripTestUtils.assertRoundTripCurrentVersion;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.PageTests.randomPage;
+import static org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryPageRequestTests.randomQueryPageRequest;
+
+public class QueryPageResponseTests extends ESTestCase {
+ static QueryPageResponse randomQueryPageResponse(Page page) {
+ return new QueryPageResponse(randomAlphaOfLength(5), page);
+ }
+
+ public void testRoundTrip() throws IOException {
+ Page page = randomPage();
+ assertRoundTripCurrentVersion(() -> randomQueryPageRequest(new Page(page.columnInfo())), randomQueryPageResponse(page));
+ }
+
+ public void testToString() {
+ Page results = new Page(singletonList(varcharInfo("a")), new Object[][] {
+ new Object[] {"test"}
+ });
+ assertEquals("QueryPageResponse", new QueryPageResponse("test_id", results).toString());
+ }
+}
diff --git a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/TimeoutInfoTests.java b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/TimeoutInfoTests.java
index 282d978a871..acc075826db 100644
--- a/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/TimeoutInfoTests.java
+++ b/sql/jdbc-proto/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/TimeoutInfoTests.java
@@ -17,6 +17,6 @@ public class TimeoutInfoTests extends ESTestCase {
}
public void testRoundTrip() throws IOException {
- assertRoundTrip(randomTimeoutInfo(), TimeoutInfo::encode, TimeoutInfo::new);
+ assertRoundTrip(randomTimeoutInfo(), TimeoutInfo::write, TimeoutInfo::new);
}
}
diff --git a/sql/jdbc/build.gradle b/sql/jdbc/build.gradle
index 2eff5b1c595..894851e2ceb 100644
--- a/sql/jdbc/build.gradle
+++ b/sql/jdbc/build.gradle
@@ -43,11 +43,13 @@ task generateGitHash {
dependencies {
compile project(':x-pack-elasticsearch:sql:net-client')
compile project(':x-pack-elasticsearch:sql:jdbc-proto')
+ compile project(':x-pack-elasticsearch:sql:shared-proto')
/* We want to limit these dependencies so do not add anything to this list
* without serious consideration, and probably shading. */
+ // Used by the embedded sql tests
testCompile project(path: ':client:transport', configuration: 'runtime')
- testCompile project(path: ':x-pack-elasticsearch:plugin', configuration: 'testArtifacts')
+ testCompile project(path: ':x-pack-elasticsearch:plugin', configuration: 'testArtifacts') // NOCOMMIT remove me?
testCompile project(':x-pack-elasticsearch:sql:test-utils')
testCompile "net.sourceforge.csvjdbc:csvjdbc:1.0.31"
@@ -56,8 +58,10 @@ dependencies {
dependencyLicenses {
mapping from: /jdbc-proto.*/, to: 'elasticsearch'
+ mapping from: /shared-proto.*/, to: 'elasticsearch'
mapping from: /net-client.*/, to: 'elasticsearch'
ignoreSha 'jdbc-proto'
+ ignoreSha 'shared-proto'
ignoreSha 'net-client'
}
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java
index 7d42a546b21..2c13f302b15 100644
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java
+++ b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java
@@ -5,22 +5,22 @@
*/
package org.elasticsearch.xpack.sql.jdbc.jdbc;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.RowIdLifetime;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.sql.Types;
-import java.util.ArrayList;
-import java.util.List;
-
import org.elasticsearch.xpack.sql.jdbc.net.client.Cursor;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaColumnInfo;
import org.elasticsearch.xpack.sql.jdbc.util.Version;
import org.elasticsearch.xpack.sql.net.client.util.ObjectUtils;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.JDBCType;
+import java.sql.ResultSet;
+import java.sql.RowIdLifetime;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.ArrayList;
+import java.util.List;
+
import static org.elasticsearch.xpack.sql.net.client.util.StringUtils.EMPTY;
import static org.elasticsearch.xpack.sql.net.client.util.StringUtils.hasText;
@@ -824,8 +824,8 @@ class JdbcDatabaseMetaData implements DatabaseMetaData, JdbcWrapper {
row[ 1] = EMPTY;
row[ 2] = col.table;
row[ 3] = col.name;
- row[ 4] = col.type;
- row[ 5] = JdbcUtils.nameOf(col.type);
+ row[ 4] = col.type.getVendorTypeNumber();
+ row[ 5] = col.type.getName();
row[ 6] = col.position; // NOCOMMIT this doesn't seem right
row[ 7] = null;
row[ 8] = null;
@@ -1186,11 +1186,11 @@ class JdbcDatabaseMetaData implements DatabaseMetaData, JdbcWrapper {
Object obj = cols[i];
if (obj instanceof String) {
String name = obj.toString();
- int type = Types.VARCHAR;
+ JDBCType type = JDBCType.VARCHAR;
if (i + 1 < cols.length) {
// check if the next item it's a type
if (cols[i + 1] instanceof Class) {
- type = JdbcUtils.fromClass((Class>) cols[i + 1]);
+ type = JDBCType.valueOf(JdbcUtils.fromClass((Class>) cols[i + 1]));
i++;
}
// it's not, use the default and move on
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java
index d1c8220a8df..e2c91075b5d 100644
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java
+++ b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java
@@ -59,7 +59,7 @@ class JdbcParameterMetaData implements ParameterMetaData, JdbcWrapper {
@Override
public String getParameterClassName(int param) throws SQLException {
- return JdbcUtils.nameOf(paramInfo(param).type.getVendorTypeNumber().intValue());
+ return paramInfo(param).type.name(); // NOCOMMIT this is almost certainly wrong
}
@Override
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java
index 754f62a7e79..d44f1c65c23 100644
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java
+++ b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java
@@ -16,6 +16,7 @@ import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
+import java.sql.JDBCType;
import java.sql.NClob;
import java.sql.Ref;
import java.sql.ResultSet;
@@ -316,14 +317,14 @@ class JdbcResultSet implements ResultSet, JdbcWrapper {
return type.cast(val);
}
- int columnType = cursor.columns().get(columnIndex - 1).type;
+ JDBCType columnType = cursor.columns().get(columnIndex - 1).type;
T t = TypeConverter.convert(val, columnType, type);
if (t != null) {
return t;
}
- throw new SQLException(format(Locale.ROOT, "Conversion from type %s to %s not supported", JdbcUtils.nameOf(columnType), type.getName()));
+ throw new SQLException("Conversion from type [" + columnType + "] to [" + type.getName() + "] not supported");
}
@Override
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSetMetaData.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSetMetaData.java
index 7ea9bccc49f..403a720896d 100644
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSetMetaData.java
+++ b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSetMetaData.java
@@ -109,12 +109,12 @@ class JdbcResultSetMetaData implements ResultSetMetaData, JdbcWrapper {
@Override
public int getColumnType(int column) throws SQLException {
- return column(column).type;
+ return column(column).type.getVendorTypeNumber();
}
@Override
public String getColumnTypeName(int column) throws SQLException {
- return JdbcUtils.typeName(column(column).type);
+ return column(column).type.name();
}
@Override
@@ -137,7 +137,7 @@ class JdbcResultSetMetaData implements ResultSetMetaData, JdbcWrapper {
@Override
public String getColumnClassName(int column) throws SQLException {
- return JdbcUtils.nameOf(column(column).type);
+ return column(column).type.getName(); // NOCOMMIT this is almost certainly wrong.
}
private void checkOpen() throws SQLException {
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcUtils.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcUtils.java
index e553b1eaa50..be56735d0fe 100644
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcUtils.java
+++ b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcUtils.java
@@ -14,19 +14,14 @@ import java.sql.Time;
import java.sql.Timestamp;
import static java.sql.Types.BIGINT;
-import static java.sql.Types.BINARY;
-import static java.sql.Types.BIT;
import static java.sql.Types.BLOB;
import static java.sql.Types.BOOLEAN;
-import static java.sql.Types.CHAR;
import static java.sql.Types.CLOB;
import static java.sql.Types.DATE;
import static java.sql.Types.DECIMAL;
import static java.sql.Types.DOUBLE;
import static java.sql.Types.FLOAT;
import static java.sql.Types.INTEGER;
-import static java.sql.Types.LONGVARBINARY;
-import static java.sql.Types.LONGVARCHAR;
import static java.sql.Types.NULL;
import static java.sql.Types.NUMERIC;
import static java.sql.Types.REAL;
@@ -71,57 +66,6 @@ public abstract class JdbcUtils {
return wrapperClass;
}
- public static String nameOf(int jdbcType) {
- return JDBCType.valueOf(jdbcType).getName();
- }
-
- // see javax.sql.rowset.RowSetMetaDataImpl
- // and https://db.apache.org/derby/docs/10.5/ref/rrefjdbc20377.html
- public static Class> classOf(int jdbcType) {
-
- switch (jdbcType) {
- case NUMERIC:
- case DECIMAL:
- return BigDecimal.class;
- case BOOLEAN:
- case BIT:
- return Boolean.class;
- case TINYINT:
- return Byte.class;
- case SMALLINT:
- return Short.class;
- case INTEGER:
- return Integer.class;
- case BIGINT:
- return Long.class;
- case REAL:
- return Float.class;
- case FLOAT:
- case DOUBLE:
- return Double.class;
- case BINARY:
- case VARBINARY:
- case LONGVARBINARY:
- return byte[].class;
- case CHAR:
- case VARCHAR:
- case LONGVARCHAR:
- return String.class;
- case DATE:
- return Date.class;
- case TIME:
- return Time.class;
- case TIMESTAMP:
- return Timestamp.class;
- case BLOB:
- return Blob.class;
- case CLOB:
- return Clob.class;
- default:
- throw new IllegalArgumentException("Unsupported JDBC type [" + jdbcType + "/" + nameOf(jdbcType) + "]");
- }
- }
-
public static int fromClass(Class> clazz) {
if (clazz == null) {
return NULL;
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java
index c377d04166b..c7fb17fd8c7 100644
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java
+++ b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java
@@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.jdbc.jdbc;
import java.sql.Date;
+import java.sql.JDBCType;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
@@ -21,25 +22,6 @@ import java.util.TimeZone;
import java.util.function.Function;
import static java.lang.String.format;
-import static java.sql.Types.BIGINT;
-import static java.sql.Types.BINARY;
-import static java.sql.Types.BIT;
-import static java.sql.Types.BOOLEAN;
-import static java.sql.Types.CHAR;
-import static java.sql.Types.DATE;
-import static java.sql.Types.DOUBLE;
-import static java.sql.Types.FLOAT;
-import static java.sql.Types.INTEGER;
-import static java.sql.Types.LONGVARBINARY;
-import static java.sql.Types.LONGVARCHAR;
-import static java.sql.Types.REAL;
-import static java.sql.Types.SMALLINT;
-import static java.sql.Types.TIME;
-import static java.sql.Types.TIMESTAMP;
-import static java.sql.Types.TIMESTAMP_WITH_TIMEZONE;
-import static java.sql.Types.TINYINT;
-import static java.sql.Types.VARBINARY;
-import static java.sql.Types.VARCHAR;
import static java.util.Calendar.DAY_OF_MONTH;
import static java.util.Calendar.ERA;
import static java.util.Calendar.HOUR_OF_DAY;
@@ -99,7 +81,7 @@ abstract class TypeConverter {
}
@SuppressWarnings("unchecked")
- static T convert(Object val, int columnType, Class type) throws SQLException {
+ static T convert(Object val, JDBCType columnType, Class type) throws SQLException {
if (type == null) {
return (T) asNative(val, columnType);
}
@@ -161,7 +143,7 @@ abstract class TypeConverter {
}
// keep in check with JdbcUtils#columnType
- private static Object asNative(Object v, int columnType) {
+ private static Object asNative(Object v, JDBCType columnType) {
Object result = null;
switch (columnType) {
case BIT:
@@ -198,7 +180,7 @@ abstract class TypeConverter {
return nativeValue == null ? null : String.valueOf(nativeValue);
}
- private static Boolean asBoolean(Object val, int columnType) {
+ private static Boolean asBoolean(Object val, JDBCType columnType) {
switch (columnType) {
case BIT:
case BOOLEAN:
@@ -215,7 +197,7 @@ abstract class TypeConverter {
}
}
- private static Byte asByte(Object val, int columnType) throws SQLException {
+ private static Byte asByte(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case BIT:
case BOOLEAN:
@@ -235,7 +217,7 @@ abstract class TypeConverter {
return null;
}
- private static Short asShort(Object val, int columnType) throws SQLException {
+ private static Short asShort(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case BIT:
case BOOLEAN:
@@ -255,7 +237,7 @@ abstract class TypeConverter {
return null;
}
- private static Integer asInteger(Object val, int columnType) throws SQLException {
+ private static Integer asInteger(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case BIT:
case BOOLEAN:
@@ -275,7 +257,7 @@ abstract class TypeConverter {
return null;
}
- private static Long asLong(Object val, int columnType) throws SQLException {
+ private static Long asLong(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case BIT:
case BOOLEAN:
@@ -302,7 +284,7 @@ abstract class TypeConverter {
return null;
}
- private static Float asFloat(Object val, int columnType) throws SQLException {
+ private static Float asFloat(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case BIT:
case BOOLEAN:
@@ -322,7 +304,7 @@ abstract class TypeConverter {
return null;
}
- private static Double asDouble(Object val, int columnType) throws SQLException {
+ private static Double asDouble(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case BIT:
case BOOLEAN:
@@ -342,7 +324,7 @@ abstract class TypeConverter {
return null;
}
- private static Date asDate(Object val, int columnType) throws SQLException {
+ private static Date asDate(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case TIME:
// time has no date component
@@ -357,7 +339,7 @@ abstract class TypeConverter {
return null;
}
- private static Time asTime(Object val, int columnType) throws SQLException {
+ private static Time asTime(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case DATE:
// date has no time component
@@ -372,7 +354,7 @@ abstract class TypeConverter {
return null;
}
- private static Timestamp asTimestamp(Object val, int columnType) throws SQLException {
+ private static Timestamp asTimestamp(Object val, JDBCType columnType) throws SQLException {
switch (columnType) {
case DATE:
return new Timestamp(utcMillisRemoveTime(((Number) val).longValue()));
@@ -387,26 +369,26 @@ abstract class TypeConverter {
return null;
}
- private static byte[] asByteArray(Object val, int columnType) {
+ private static byte[] asByteArray(Object val, JDBCType columnType) {
throw new UnsupportedOperationException();
}
- private static LocalDate asLocalDate(Object val, int columnType) {
+ private static LocalDate asLocalDate(Object val, JDBCType columnType) {
throw new UnsupportedOperationException();
}
- private static LocalTime asLocalTime(Object val, int columnType) {
+ private static LocalTime asLocalTime(Object val, JDBCType columnType) {
throw new UnsupportedOperationException();
}
- private static LocalDateTime asLocalDateTime(Object val, int columnType) {
+ private static LocalDateTime asLocalDateTime(Object val, JDBCType columnType) {
throw new UnsupportedOperationException();
}
- private static OffsetTime asOffsetTime(Object val, int columnType) {
+ private static OffsetTime asOffsetTime(Object val, JDBCType columnType) {
throw new UnsupportedOperationException();
}
- private static OffsetDateTime asOffsetDateTime(Object val, int columnType) {
+ private static OffsetDateTime asOffsetDateTime(Object val, JDBCType columnType) {
throw new UnsupportedOperationException();
}
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/DefaultCursor.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/DefaultCursor.java
index 512caaa3cc4..678058c13d9 100644
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/DefaultCursor.java
+++ b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/DefaultCursor.java
@@ -5,12 +5,13 @@
*/
package org.elasticsearch.xpack.sql.jdbc.net.client;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Page;
+import org.elasticsearch.xpack.sql.net.client.util.StringUtils;
+
import java.sql.SQLException;
import java.util.List;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
-import org.elasticsearch.xpack.sql.net.client.util.StringUtils;
-
class DefaultCursor implements Cursor {
private final JdbcHttpClient client;
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java
index f4469752fe9..43aeb2a6cc0 100644
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java
+++ b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java
@@ -16,23 +16,23 @@ import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaColumnRequest;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaColumnResponse;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaTableRequest;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaTableResponse;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.SqlExceptionType;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.ProtoUtils;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Page;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.ResponseType;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryInitRequest;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryInitResponse;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryPageRequest;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryPageResponse;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Response;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.TimeoutInfo;
import org.elasticsearch.xpack.sql.jdbc.util.BytesArray;
import org.elasticsearch.xpack.sql.jdbc.util.FastByteArrayInputStream;
import org.elasticsearch.xpack.sql.net.client.util.StringUtils;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import java.io.Closeable;
import java.io.DataInput;
import java.io.DataInputStream;
-import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.time.Instant;
@@ -66,67 +66,21 @@ public class JdbcHttpClient implements Closeable {
}
public Cursor query(String sql, TimeZone timeZone, RequestMeta meta) throws SQLException {
- BytesArray ba = http.put(out -> queryRequest(out, meta, sql, timeZone));
- return doIO(ba, in -> queryResponse(in, meta));
- }
-
- private void queryRequest(DataOutput out, RequestMeta meta, String sql, TimeZone timeZone) throws IOException {
int fetch = meta.fetchSize() >= 0 ? meta.fetchSize() : conCfg.pageSize();
- ProtoUtils.write(out, new QueryInitRequest(fetch, sql, timeZone, timeout(meta)));
+ QueryInitRequest request = new QueryInitRequest(fetch, sql, timeZone, timeout(meta));
+ BytesArray ba = http.put(out -> Proto.INSTANCE.writeRequest(request, out));
+ QueryInitResponse response = doIO(ba, in -> (QueryInitResponse) readResponse(request, in));
+ return new DefaultCursor(this, response.requestId, (Page) response.data, meta);
}
+ /**
+ * Read the next page of results, updating the {@link Page} and returning
+ * the scroll id to use to fetch the next page.
+ */
public String nextPage(String requestId, Page page, RequestMeta meta) throws SQLException {
- BytesArray ba = http.put(out -> ProtoUtils.write(out, new QueryPageRequest(requestId, timeout(meta))));
- return doIO(ba, in -> pageResponse(in, page));
- }
-
- private TimeoutInfo timeout(RequestMeta meta) {
- // client time
- long clientTime = Instant.now().toEpochMilli();
-
- // timeout (in ms)
- long timeout = meta.timeoutInMs();
- if (timeout == 0) {
- timeout = conCfg.getQueryTimeout();
- }
- return new TimeoutInfo(clientTime, timeout, conCfg.getPageTimeout());
- }
-
- private Cursor queryResponse(DataInput in, RequestMeta meta) throws IOException, SQLException {
- QueryInitResponse response = readResponse(in, Action.QUERY_INIT);
-
- // finally read data
- // allocate columns
- int rows = in.readInt();
- Page page = Page.of(response.columns, rows);
- readData(in, page, rows);
-
- return new DefaultCursor(this, response.requestId, page, meta);
- }
-
- private void readData(DataInput in, Page page, int rows) throws IOException {
- page.resize(rows);
- int[] jdbcTypes = page.columnInfo().stream()
- .mapToInt(c -> c.type)
- .toArray();
-
- for (int row = 0; row < rows; row++) {
- for (int column = 0; column < jdbcTypes.length; column++) {
- page.column(column)[row] = ProtoUtils.readValue(in, jdbcTypes[column]);
- }
- }
- }
-
- private String pageResponse(DataInput in, Page page) throws IOException, SQLException {
- QueryPageResponse response = readResponse(in, Action.QUERY_PAGE);
-
- // read the data
- // allocate columns
- int rows = in.readInt();
- page.resize(rows); // NOCOMMIT I believe this is duplicated with readData
- readData(in, page, rows);
-
- return response.requestId;
+ QueryPageRequest request = new QueryPageRequest(requestId, timeout(meta), page);
+ BytesArray ba = http.put(out -> Proto.INSTANCE.writeRequest(request, out));
+ return doIO(ba, in -> ((QueryPageResponse) readResponse(request, in)).requestId);
}
public InfoResponse serverInfo() throws SQLException {
@@ -137,26 +91,21 @@ public class JdbcHttpClient implements Closeable {
}
private InfoResponse fetchServerInfo() throws SQLException {
- BytesArray ba = http.put(out -> ProtoUtils.write(out, new InfoRequest()));
- return doIO(ba, in -> readResponse(in, Action.INFO));
+ InfoRequest request = new InfoRequest();
+ BytesArray ba = http.put(out -> Proto.INSTANCE.writeRequest(request, out));
+ return doIO(ba, in -> (InfoResponse) readResponse(request, in));
}
public List metaInfoTables(String pattern) throws SQLException {
- BytesArray ba = http.put(out -> ProtoUtils.write(out, new MetaTableRequest(pattern)));
-
- return doIO(ba, in -> {
- MetaTableResponse res = readResponse(in, Action.META_TABLE);
- return res.tables;
- });
+ MetaTableRequest request = new MetaTableRequest(pattern);
+ BytesArray ba = http.put(out -> Proto.INSTANCE.writeRequest(request, out));
+ return doIO(ba, in -> ((MetaTableResponse) readResponse(request, in)).tables);
}
public List metaInfoColumns(String tablePattern, String columnPattern) throws SQLException {
- BytesArray ba = http.put(out -> ProtoUtils.write(out, new MetaColumnRequest(tablePattern, columnPattern)));
-
- return doIO(ba, in -> {
- MetaColumnResponse res = readResponse(in, Action.META_COLUMN);
- return res.columns;
- });
+ MetaColumnRequest request = new MetaColumnRequest(tablePattern, columnPattern);
+ BytesArray ba = http.put(out -> Proto.INSTANCE.writeRequest(request, out));
+ return doIO(ba, in -> ((MetaColumnResponse) readResponse(request, in)).columns);
}
public void close() {
@@ -179,36 +128,29 @@ public class JdbcHttpClient implements Closeable {
}
}
- @SuppressWarnings("unchecked")
- private static R readResponse(DataInput in, Action expected) throws IOException, SQLException {
- String errorMessage = ProtoUtils.readHeader(in);
- if (errorMessage != null) {
- throw new JdbcException(errorMessage);
- }
-
- int header = in.readInt();
+ private static Response readResponse(Request request, DataInput in) throws IOException, SQLException {
+ Response response = Proto.INSTANCE.readResponse(request, in);
- Action action = Action.from(header);
- if (expected != action) {
- throw new JdbcException("Expected response for %s, found %s", expected, action);
- }
-
- Response response = ProtoUtils.readResponse(in, header);
-
- // NOCOMMIT why not move the throw login into readResponse?
- if (response instanceof ExceptionResponse) {
+ if (response.responseType() == ResponseType.EXCEPTION) {
ExceptionResponse ex = (ExceptionResponse) response;
- throw SqlExceptionType.asException(ex.asSql, ex.message);
+ throw ex.asException();
}
- if (response instanceof ErrorResponse) {
+ if (response.responseType() == ResponseType.EXCEPTION) {
ErrorResponse error = (ErrorResponse) response;
throw new JdbcException("%s", error.stack);
}
- if (response instanceof Response) {
- // NOCOMMIT I'd feel more comfortable either returning Response and passing the class in and calling responseClass.cast(response)
- return (R) response;
- }
+ return response;
+ }
- throw new JdbcException("Invalid response status %08X", header);
+ private TimeoutInfo timeout(RequestMeta meta) {
+ // client time
+ long clientTime = Instant.now().toEpochMilli();
+
+ // timeout (in ms)
+ long timeout = meta.timeoutInMs();
+ if (timeout == 0) {
+ timeout = conCfg.getQueryTimeout();
+ }
+ return new TimeoutInfo(clientTime, timeout, conCfg.getPageTimeout());
}
}
diff --git a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/Page.java b/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/Page.java
deleted file mode 100644
index 8c36cf3e93d..00000000000
--- a/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/Page.java
+++ /dev/null
@@ -1,86 +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.jdbc.net.client;
-
-import org.elasticsearch.xpack.sql.jdbc.jdbc.JdbcException;
-import org.elasticsearch.xpack.sql.jdbc.jdbc.JdbcUtils;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
-
-import java.lang.reflect.Array;
-import java.sql.Date;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.util.List;
-
-// Stores a page of data in a columnar-format since:
-// * the structure does not change
-// * array allocation can be quite efficient
-// * array can be reallocated (especially since the pages have the same size)
-
-// c1 c1 c1
-// c2 c2 c2 ...
-public class Page {
-
- // logical limit
- private int rows;
-
- private final List columnInfo;
- private final Object[][] data;
-
- private Page(int rows, List columnInfo, Object[][] data) {
- this.rows = rows;
- this.columnInfo = columnInfo;
- this.data = data;
- }
-
- void resize(int newLength) {
- // resize only when needed
- // the array is kept around so check its length not the logical limit
- if (newLength > data[0].length) {
- for (int i = 0; i < data.length; i++) {
- data[i] = (Object[]) Array.newInstance(data[i].getClass().getComponentType(), newLength);
- }
- }
- rows = newLength;
- }
-
- int rows() {
- return rows;
- }
-
- List columnInfo() {
- return columnInfo;
- }
-
- Object[] column(int index) {
- if (index < 0 || index >= data.length) {
- throw new JdbcException("Invalid column %d (max is %d)", index, data.length - 1);
- }
-
- return data[index];
- }
-
- Object entry(int row, int column) {
- if (row < 0 || row >= rows) {
- throw new JdbcException("Invalid row %d (max is %d)", row, rows - 1);
- }
- return column(column)[row];
- }
-
- static Page of(List columnInfo, int dataSize) {
- Object[][] data = new Object[columnInfo.size()][];
-
- for (int i = 0; i < columnInfo.size(); i++) {
- Class> types = JdbcUtils.classOf(columnInfo.get(i).type);
- if (types == Timestamp.class || types == Date.class || types == Time.class) {
- types = Long.class;
- }
- data[i] = (Object[]) Array.newInstance(types, dataSize);
- }
-
- return new Page(dataSize, columnInfo, data);
- }
-}
\ No newline at end of file
diff --git a/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/JdbcAssert.java b/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/JdbcAssert.java
index 466de095d7f..621a8cb6f39 100644
--- a/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/JdbcAssert.java
+++ b/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/JdbcAssert.java
@@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.sql.jdbc.framework;
+import java.sql.JDBCType;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
@@ -13,7 +14,6 @@ import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
-import static org.elasticsearch.xpack.sql.jdbc.jdbc.JdbcUtils.nameOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -51,9 +51,8 @@ public class JdbcAssert {
int expectedType = expectedMeta.getColumnType(column);
int actualType = actualMeta.getColumnType(column);
- assertEquals(
- "Different column type for column [" + expectedName + "] (" + nameOf(expectedType) + " != " + nameOf(actualType) + ")",
- expectedType, actualType);
+ assertEquals("Different column type for column [" + expectedName + "] (" + JDBCType.valueOf(expectedType) + " != "
+ + JDBCType.valueOf(actualType) + ")", expectedType, actualType);
}
}
diff --git a/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/JdbcHttpServer.java b/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/JdbcHttpServer.java
index 280d28e7bdf..2930ab20996 100644
--- a/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/JdbcHttpServer.java
+++ b/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/JdbcHttpServer.java
@@ -6,7 +6,7 @@
package org.elasticsearch.xpack.sql.jdbc.framework;
import org.elasticsearch.client.Client;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Response;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import org.elasticsearch.xpack.sql.test.server.ProtoHttpServer;
/**
diff --git a/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/SqlProtoHandler.java b/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/SqlProtoHandler.java
index 95dd6ed7b4e..7729c022b3f 100644
--- a/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/SqlProtoHandler.java
+++ b/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/framework/SqlProtoHandler.java
@@ -9,11 +9,12 @@ import com.sun.net.httpserver.HttpExchange;
import org.elasticsearch.client.Client;
import org.elasticsearch.xpack.sql.TestUtils;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.ProtoUtils;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Request;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Response;
-import org.elasticsearch.xpack.sql.plugin.jdbc.server.JdbcServer;
-import org.elasticsearch.xpack.sql.plugin.jdbc.server.JdbcServerProtoUtils;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+import org.elasticsearch.xpack.sql.server.AbstractSqlServer;
+import org.elasticsearch.xpack.sql.server.jdbc.JdbcServer;
import org.elasticsearch.xpack.sql.test.server.ProtoHandler;
import java.io.DataInput;
@@ -26,13 +27,13 @@ class SqlProtoHandler extends ProtoHandler {
private final JdbcServer server;
SqlProtoHandler(Client client) {
- super(client, ProtoUtils::readHeader, JdbcServerProtoUtils::write);
+ super(client, response -> AbstractSqlServer.write(AbstractProto.CURRENT_VERSION, response));
this.server = new JdbcServer(TestUtils.planExecutor(client), clusterName, () -> info.getNode().getName(), info.getVersion(), info.getBuild());
}
@Override
protected void handle(HttpExchange http, DataInput in) throws IOException {
- Request req = ProtoUtils.readRequest(in);
+ Request req = Proto.INSTANCE.readRequest(in);
server.handle(req, wrap(resp -> sendHttpResponse(http, resp), ex -> fail(http, ex)));
}
}
\ No newline at end of file
diff --git a/sql/server/build.gradle b/sql/server/build.gradle
index cdbf3190ee4..c1c9ff3fce6 100644
--- a/sql/server/build.gradle
+++ b/sql/server/build.gradle
@@ -5,18 +5,21 @@ description = 'The server components of SQL for Elasticsearch'
dependencies {
compile project(':x-pack-elasticsearch:sql:jdbc-proto')
compile project(':x-pack-elasticsearch:sql:cli-proto')
+ compile project(':x-pack-elasticsearch:sql:shared-proto')
provided "org.elasticsearch.plugin:aggs-matrix-stats-client:${project.versions.elasticsearch}"
//NOCOMMIT - we should upgrade to the latest 4.5.x if not 4.7
compile 'org.antlr:antlr4-runtime:4.5.1-1'
provided "org.elasticsearch:elasticsearch:${project.versions.elasticsearch}"
-
+
}
dependencyLicenses {
mapping from: /jdbc-proto.*/, to: 'elasticsearch'
mapping from: /cli-proto.*/, to: 'elasticsearch'
+ mapping from: /shared-proto.*/, to: 'elasticsearch'
ignoreSha 'jdbc-proto'
ignoreSha 'cli-proto'
+ ignoreSha 'shared-proto'
}
// NOCOMMIT probably not a good thing to rely on.....
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java
index e4c859ef4ae..87ada4fd2d7 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java
@@ -24,15 +24,15 @@ import org.elasticsearch.script.ScriptService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.sql.execution.PlanExecutor;
-import org.elasticsearch.xpack.sql.plugin.jdbc.action.JdbcAction;
-import org.elasticsearch.xpack.sql.plugin.jdbc.action.TransportJdbcAction;
-import org.elasticsearch.xpack.sql.plugin.jdbc.http.JdbcHttpHandler;
import org.elasticsearch.xpack.sql.plugin.sql.action.SqlAction;
import org.elasticsearch.xpack.sql.plugin.sql.action.TransportSqlAction;
import org.elasticsearch.xpack.sql.plugin.sql.rest.RestSqlAction;
import org.elasticsearch.xpack.sql.server.cli.CliAction;
import org.elasticsearch.xpack.sql.server.cli.CliHttpHandler;
import org.elasticsearch.xpack.sql.server.cli.TransportCliAction;
+import org.elasticsearch.xpack.sql.server.jdbc.JdbcAction;
+import org.elasticsearch.xpack.sql.server.jdbc.JdbcHttpHandler;
+import org.elasticsearch.xpack.sql.server.jdbc.TransportJdbcAction;
import java.util.Arrays;
import java.util.Collection;
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/server/JdbcServerProtoUtils.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/server/JdbcServerProtoUtils.java
deleted file mode 100644
index 2f57d464f88..00000000000
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/server/JdbcServerProtoUtils.java
+++ /dev/null
@@ -1,112 +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.jdbc.server;
-
-import org.elasticsearch.ResourceNotFoundException;
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.bytes.BytesReference;
-import org.elasticsearch.common.io.stream.BytesStreamOutput;
-import org.elasticsearch.xpack.sql.analysis.AnalysisException;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.DataResponse;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.ErrorResponse;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.ExceptionResponse;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.Action;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.SqlExceptionType;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.ProtoUtils;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryInitResponse;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Response;
-import org.elasticsearch.xpack.sql.parser.ParsingException;
-import org.elasticsearch.xpack.sql.session.RowSet;
-import org.elasticsearch.xpack.sql.session.RowSetCursor;
-import org.joda.time.ReadableInstant;
-
-import java.io.DataOutput;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.sql.Types;
-import java.util.concurrent.TimeoutException;
-
-import static org.elasticsearch.xpack.sql.util.StringUtils.EMPTY;
-
-public abstract class JdbcServerProtoUtils {
-
- public static BytesReference write(Response response) throws IOException {
- try (BytesStreamOutput array = new BytesStreamOutput();
- DataOutputStream out = new DataOutputStream(array)) {
- ProtoUtils.write(out, response);
-
- // serialize payload (if present)
- if (response instanceof DataResponse) { // NOCOMMIT why not implement an interface?
- RowSetCursor cursor = (RowSetCursor) ((QueryInitResponse) response).data;
-
- if (cursor != null) {
- JdbcServerProtoUtils.write(out, cursor);
- }
- }
- out.flush();
- return array.bytes();
- }
- }
-
- private static void write(DataOutput out, RowSet rowSet) throws IOException {
- out.writeInt(rowSet.size());
- int[] jdbcTypes = rowSet.schema().types().stream()
- .mapToInt(dt -> dt.sqlType().getVendorTypeNumber())
- .toArray();
-
- // unroll forEach manually to avoid a Consumer + try/catch for each value...
- for (boolean hasRows = rowSet.hasCurrentRow(); hasRows; hasRows = rowSet.advanceRow()) {
- for (int i = 0; i < rowSet.rowSize(); i++) {
- Object value = rowSet.column(i);
- // unpack Joda classes on the server-side to not 'pollute' the common project and thus the client
- if (jdbcTypes[i] == Types.TIMESTAMP && value instanceof ReadableInstant) {
- // NOCOMMIT feels like a hack that'd be better cleaned up another way.
- value = ((ReadableInstant) value).getMillis();
- }
- ProtoUtils.writeValue(out, value, jdbcTypes[i]);
- }
- }
- }
-
- public static Response exception(Throwable cause, Action action) {
- SqlExceptionType sqlExceptionType = sqlExceptionType(cause);
-
- String message = EMPTY;
- String cs = EMPTY;
- if (cause != null) {
- if (Strings.hasText(cause.getMessage())) {
- message = cause.getMessage();
- }
- cs = cause.getClass().getName();
- }
-
- if (sqlExceptionType != null) {
- return new ExceptionResponse(action, message, cs, sqlExceptionType);
- }
- else {
- // TODO: might want to 'massage' this
- StringWriter sw = new StringWriter();
- cause.printStackTrace(new PrintWriter(sw));
- return new ErrorResponse(action, message, cs, sw.toString());
- }
- }
-
- private static SqlExceptionType sqlExceptionType(Throwable cause) {
- if (cause instanceof AnalysisException || cause instanceof ResourceNotFoundException) {
- return SqlExceptionType.DATA;
- }
- if (cause instanceof ParsingException) {
- return SqlExceptionType.SYNTAX;
- }
- if (cause instanceof TimeoutException) {
- return SqlExceptionType.TIMEOUT;
- }
-
- return null;
- }
-}
\ No newline at end of file
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/AbstractSqlServer.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/AbstractSqlServer.java
new file mode 100644
index 00000000000..bf902a76569
--- /dev/null
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/AbstractSqlServer.java
@@ -0,0 +1,86 @@
+/*
+ * 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.server;
+
+import org.elasticsearch.ResourceNotFoundException;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.io.stream.BytesStreamOutput;
+import org.elasticsearch.xpack.sql.analysis.AnalysisException;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto;
+import org.elasticsearch.xpack.sql.parser.ParsingException;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractErrorResponse;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractExceptionResponse;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.TimeoutException;
+
+import static org.elasticsearch.xpack.sql.util.StringUtils.EMPTY;
+
+public abstract class AbstractSqlServer {
+ public final void handle(Request req, ActionListener listener) {
+ try {
+ innerHandle(req, listener);
+ } catch (Exception e) {
+ listener.onResponse(exceptionResponse(req, e));
+ }
+ }
+
+ protected final Response exceptionResponse(Request req, Exception e) {
+ // NOCOMMIT I wonder why we don't just teach the servers to handle ES's normal exception response.....
+ SqlExceptionType exceptionType = sqlExceptionType(e);
+
+ String message = EMPTY;
+ String cs = EMPTY;
+ if (Strings.hasText(e.getMessage())) {
+ message = e.getMessage();
+ }
+ cs = e.getClass().getName();
+
+ if (exceptionType != null) {
+ return buildExceptionResponse(req, message, cs, exceptionType);
+ } else {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ return buildErrorResponse(req, message, cs, sw.toString());
+ }
+ }
+
+ protected abstract void innerHandle(Request req, ActionListener listener);
+ protected abstract AbstractExceptionResponse> buildExceptionResponse(Request request, String message, String cause,
+ SqlExceptionType exceptionType);
+ protected abstract AbstractErrorResponse> buildErrorResponse(Request request, String message, String cause, String stack);
+
+ public static BytesReference write(int clientVersion, Response response) throws IOException {
+ try (BytesStreamOutput array = new BytesStreamOutput();
+ DataOutputStream out = new DataOutputStream(array)) {
+ Proto.INSTANCE.writeResponse(response, clientVersion, out);
+ out.flush();
+ return array.bytes();
+ }
+ }
+
+ private static SqlExceptionType sqlExceptionType(Throwable cause) {
+ if (cause instanceof AnalysisException || cause instanceof ResourceNotFoundException) {
+ return SqlExceptionType.DATA;
+ }
+ if (cause instanceof ParsingException) {
+ return SqlExceptionType.SYNTAX;
+ }
+ if (cause instanceof TimeoutException) {
+ return SqlExceptionType.TIMEOUT;
+ }
+
+ return null;
+ }
+}
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliHttpHandler.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliHttpHandler.java
index 61f476f3cd8..ced49ddba24 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliHttpHandler.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliHttpHandler.java
@@ -15,6 +15,8 @@ import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.xpack.sql.cli.net.protocol.Proto;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto;
+import org.elasticsearch.xpack.sql.server.AbstractSqlServer;
import org.elasticsearch.xpack.sql.util.StringUtils;
import java.io.DataInputStream;
@@ -39,7 +41,7 @@ public class CliHttpHandler extends BaseRestHandler {
}
try (DataInputStream in = new DataInputStream(request.content().streamInput())) {
- CliRequest cliRequest = new CliRequest(Proto.readRequest(in));
+ CliRequest cliRequest = new CliRequest(Proto.INSTANCE.readRequest(in));
return c -> client.executeLocally(CliAction.INSTANCE, cliRequest,
ActionListener.wrap(response -> cliResponse(c, response), ex -> error(c, ex)));
}
@@ -49,7 +51,9 @@ public class CliHttpHandler extends BaseRestHandler {
BytesRestResponse restResponse = null;
try {
- restResponse = new BytesRestResponse(OK, TEXT_CONTENT_TYPE, CliServerProtoUtils.write(response.response()));
+ // NOCOMMIT use a real version
+ restResponse = new BytesRestResponse(OK, TEXT_CONTENT_TYPE,
+ AbstractSqlServer.write(AbstractProto.CURRENT_VERSION, response.response()));
} catch (IOException ex) {
restResponse = new BytesRestResponse(INTERNAL_SERVER_ERROR, TEXT_CONTENT_TYPE, StringUtils.EMPTY);
}
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliRequest.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliRequest.java
index a1a95e3a08b..f06bfb4b4f7 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliRequest.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliRequest.java
@@ -5,12 +5,12 @@
*/
package org.elasticsearch.xpack.sql.server.cli;
-import java.util.Objects;
-
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.CompositeIndicesRequest;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+
+import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliRequestBuilder.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliRequestBuilder.java
index 9fde4bd82a7..88a5665b1aa 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliRequestBuilder.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliRequestBuilder.java
@@ -7,7 +7,7 @@ package org.elasticsearch.xpack.sql.server.cli;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.client.ElasticsearchClient;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
public class CliRequestBuilder extends ActionRequestBuilder {
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliResponse.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliResponse.java
index 8dd4625bf6c..743583ace13 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliResponse.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliResponse.java
@@ -6,13 +6,11 @@
package org.elasticsearch.xpack.sql.server.cli;
import org.elasticsearch.action.ActionResponse;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Response;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import org.elasticsearch.xpack.sql.session.RowSetCursor;
public class CliResponse extends ActionResponse {
-
private Response response;
- private RowSetCursor cursor;
public CliResponse() {}
@@ -22,14 +20,9 @@ public class CliResponse extends ActionResponse {
public CliResponse(Response response, RowSetCursor cursor) {
this.response = response;
- this.cursor = cursor;
}
public Response response() {
return response;
}
-
- public RowSetCursor cursor() {
- return cursor;
- }
}
\ No newline at end of file
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliServer.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliServer.java
index c0feb1a469d..458d266ad50 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliServer.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliServer.java
@@ -10,14 +10,17 @@ import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.xpack.sql.cli.net.protocol.CommandRequest;
import org.elasticsearch.xpack.sql.cli.net.protocol.CommandResponse;
+import org.elasticsearch.xpack.sql.cli.net.protocol.ErrorResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.ExceptionResponse;
import org.elasticsearch.xpack.sql.cli.net.protocol.InfoRequest;
import org.elasticsearch.xpack.sql.cli.net.protocol.InfoResponse;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Request;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Response;
+import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
import org.elasticsearch.xpack.sql.execution.PlanExecutor;
import org.elasticsearch.xpack.sql.execution.search.SearchHitRowSetCursor;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryPageRequest;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+import org.elasticsearch.xpack.sql.server.AbstractSqlServer;
import org.elasticsearch.xpack.sql.util.StringUtils;
import java.util.TimeZone;
@@ -26,7 +29,7 @@ import java.util.function.Supplier;
import static org.elasticsearch.action.ActionListener.wrap;
import static org.elasticsearch.xpack.sql.util.StringUtils.EMPTY;
-public class CliServer {
+public class CliServer extends AbstractSqlServer {
private final PlanExecutor executor;
private final Supplier infoResponse;
@@ -37,23 +40,37 @@ public class CliServer {
this.infoResponse = () -> new InfoResponse(nodeName.get(), clusterName, version.major, version.minor, version.toString(),
build.shortHash(), build.date());
}
-
- public void handle(Request req, ActionListener listener) {
+
+ @Override
+ protected void innerHandle(Request req, ActionListener listener) {
+ RequestType requestType = (RequestType) req.requestType();
try {
- if (req instanceof InfoRequest) {
+ switch (requestType) {
+ case INFO:
listener.onResponse(info((InfoRequest) req));
- }
- else if (req instanceof CommandRequest) {
+ break;
+ case COMMAND:
command((CommandRequest) req, listener);
- }
- else {
- listener.onResponse(new ExceptionResponse(req.requestType(), "Invalid requested", null));
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported action [" + requestType + "]");
}
} catch (Exception ex) {
- listener.onResponse(CliServerProtoUtils.exception(ex, req.requestType()));
+ listener.onResponse(exceptionResponse(req, ex));
}
}
+ @Override
+ protected ErrorResponse buildErrorResponse(Request request, String message, String cause, String stack) {
+ return new ErrorResponse((RequestType) request.requestType(), message, cause, stack);
+ }
+
+ @Override
+ protected ExceptionResponse buildExceptionResponse(Request request, String message, String cause,
+ SqlExceptionType exceptionType) {
+ return new ExceptionResponse((RequestType) request.requestType(), message, cause, exceptionType);
+ }
+
public InfoResponse info(InfoRequest req) {
return infoResponse.get();
}
@@ -70,13 +87,10 @@ public class CliServer {
requestId = StringUtils.nullAsEmpty(((SearchHitRowSetCursor) c).scrollId());
}
+ // NOCOMMIT it looks like this tries to buffer the entire response in memory before returning it which is going to OOM some po
+ // NOCOMMIT also this blocks the current thread while it iterates the cursor
listener.onResponse(new CommandResponse(start, stop, requestId, CliUtils.toString(c)));
},
- ex -> listener.onResponse(CliServerProtoUtils.exception(ex, req.requestType()))));
+ ex -> listener.onResponse(exceptionResponse(req, ex))));
}
-
- public void queryPage(QueryPageRequest req, ActionListener listener) {
- throw new UnsupportedOperationException();
- }
-
}
\ No newline at end of file
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliServerProtoUtils.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliServerProtoUtils.java
deleted file mode 100644
index 2e4b700d41c..00000000000
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/cli/CliServerProtoUtils.java
+++ /dev/null
@@ -1,60 +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.server.cli;
-
-import org.elasticsearch.ResourceNotFoundException;
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.bytes.BytesReference;
-import org.elasticsearch.common.io.stream.BytesStreamOutput;
-import org.elasticsearch.xpack.sql.SqlException;
-import org.elasticsearch.xpack.sql.cli.net.protocol.ErrorResponse;
-import org.elasticsearch.xpack.sql.cli.net.protocol.ExceptionResponse;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Proto;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Response;
-
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-import static org.elasticsearch.xpack.sql.util.StringUtils.EMPTY;
-
-public abstract class CliServerProtoUtils {
-
- public static BytesReference write(Response response) throws IOException {
- try (BytesStreamOutput array = new BytesStreamOutput();
- DataOutputStream out = new DataOutputStream(array)) {
- Proto.writeResponse(response, Proto.CURRENT_VERSION, out);
- out.flush();
- return array.bytes();
- }
- }
-
- public static Response exception(Throwable cause, RequestType requestType) {
- String message = EMPTY;
- String cs = EMPTY;
- if (cause != null) {
- if (Strings.hasText(cause.getMessage())) {
- message = cause.getMessage();
- }
- cs = cause.getClass().getName();
- }
-
- if (expectedException(cause)) {
- return new ExceptionResponse(requestType, message, cs);
- }
- else {
- StringWriter sw = new StringWriter();
- cause.printStackTrace(new PrintWriter(sw));
- return new ErrorResponse(requestType, message, cs, sw.toString());
- }
- }
-
- private static boolean expectedException(Throwable cause) {
- return (cause instanceof SqlException || cause instanceof ResourceNotFoundException);
- }
-}
\ No newline at end of file
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcAction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcAction.java
similarity index 93%
rename from sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcAction.java
rename to sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcAction.java
index 8b7eb8342ca..3d2513f3e21 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcAction.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcAction.java
@@ -3,7 +3,7 @@
* 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.jdbc.action;
+package org.elasticsearch.xpack.sql.server.jdbc;
import org.elasticsearch.action.Action;
import org.elasticsearch.client.ElasticsearchClient;
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/http/JdbcHttpHandler.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcHttpHandler.java
similarity index 83%
rename from sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/http/JdbcHttpHandler.java
rename to sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcHttpHandler.java
index 3527fc32593..74f8c6b9d72 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/http/JdbcHttpHandler.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcHttpHandler.java
@@ -3,7 +3,7 @@
* 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.jdbc.http;
+package org.elasticsearch.xpack.sql.server.jdbc;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.client.node.NodeClient;
@@ -13,11 +13,9 @@ import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.ProtoUtils;
-import org.elasticsearch.xpack.sql.plugin.jdbc.action.JdbcAction;
-import org.elasticsearch.xpack.sql.plugin.jdbc.action.JdbcRequest;
-import org.elasticsearch.xpack.sql.plugin.jdbc.action.JdbcResponse;
-import org.elasticsearch.xpack.sql.plugin.jdbc.server.JdbcServerProtoUtils;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto;
+import org.elasticsearch.xpack.sql.server.AbstractSqlServer;
import org.elasticsearch.xpack.sql.util.StringUtils;
import java.io.DataInputStream;
@@ -44,13 +42,8 @@ public class JdbcHttpHandler extends BaseRestHandler { // NOCOMMIT these are cal
}
try (DataInputStream in = new DataInputStream(request.content().streamInput())) {
- String msg = ProtoUtils.readHeader(in);
- if (msg != null) {
- return badProto(msg);
- }
-
try {
- return c -> client.executeLocally(JdbcAction.INSTANCE, new JdbcRequest(ProtoUtils.readRequest(in)),
+ return c -> client.executeLocally(JdbcAction.INSTANCE, new JdbcRequest(Proto.INSTANCE.readRequest(in)),
wrap(response -> jdbcResponse(c, response), ex -> error(c, ex)));
} catch (Exception ex) {
@@ -67,7 +60,9 @@ public class JdbcHttpHandler extends BaseRestHandler { // NOCOMMIT these are cal
BytesRestResponse restResponse = null;
try {
- restResponse = new BytesRestResponse(OK, TEXT_CONTENT_TYPE, JdbcServerProtoUtils.write(response.response()));
+ // NOCOMMIT use the real version
+ restResponse = new BytesRestResponse(OK, TEXT_CONTENT_TYPE,
+ AbstractSqlServer.write(AbstractProto.CURRENT_VERSION, response.response()));
} catch (IOException ex) {
logger.error("error building jdbc response", ex);
restResponse = new BytesRestResponse(INTERNAL_SERVER_ERROR, TEXT_CONTENT_TYPE, StringUtils.EMPTY);
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcRequest.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcRequest.java
similarity index 93%
rename from sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcRequest.java
rename to sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcRequest.java
index c679af4d102..b20159242f4 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcRequest.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcRequest.java
@@ -3,14 +3,14 @@
* 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.jdbc.action;
-
-import java.util.Objects;
+package org.elasticsearch.xpack.sql.server.jdbc;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.CompositeIndicesRequest;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+
+import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcRequestBuilder.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcRequestBuilder.java
similarity index 87%
rename from sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcRequestBuilder.java
rename to sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcRequestBuilder.java
index 70efc0dc387..c43ec569f96 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcRequestBuilder.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcRequestBuilder.java
@@ -3,11 +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.jdbc.action;
+package org.elasticsearch.xpack.sql.server.jdbc;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.client.ElasticsearchClient;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
public class JdbcRequestBuilder extends ActionRequestBuilder {
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcResponse.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcResponse.java
similarity index 87%
rename from sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcResponse.java
rename to sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcResponse.java
index f6916c82b28..ea772d50c2e 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/JdbcResponse.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcResponse.java
@@ -3,10 +3,10 @@
* 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.jdbc.action;
+package org.elasticsearch.xpack.sql.server.jdbc;
import org.elasticsearch.action.ActionResponse;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Response;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
import org.elasticsearch.xpack.sql.session.RowSetCursor;
public class JdbcResponse extends ActionResponse {
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/server/JdbcServer.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcServer.java
similarity index 52%
rename from sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/server/JdbcServer.java
rename to sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcServer.java
index 66854ffa446..d07ec7b5640 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/server/JdbcServer.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/JdbcServer.java
@@ -3,7 +3,7 @@
* 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.jdbc.server;
+package org.elasticsearch.xpack.sql.server.jdbc;
import org.elasticsearch.Build;
import org.elasticsearch.Version;
@@ -13,6 +13,8 @@ import org.elasticsearch.xpack.sql.analysis.catalog.EsType;
import org.elasticsearch.xpack.sql.execution.PlanExecutor;
import org.elasticsearch.xpack.sql.execution.search.SearchHitRowSetCursor;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.ErrorResponse;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.ExceptionResponse;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.InfoRequest;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.InfoResponse;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaColumnInfo;
@@ -20,14 +22,18 @@ import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaColumnRequest;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaColumnResponse;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaTableRequest;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaTableResponse;
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.Proto.RequestType;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryInitRequest;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryInitResponse;
import org.elasticsearch.xpack.sql.jdbc.net.protocol.QueryPageRequest;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Request;
-import org.elasticsearch.xpack.sql.jdbc.net.protocol.Response;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
+import org.elasticsearch.xpack.sql.protocol.shared.Request;
+import org.elasticsearch.xpack.sql.protocol.shared.Response;
+import org.elasticsearch.xpack.sql.server.AbstractSqlServer;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.util.StringUtils;
+import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -37,10 +43,11 @@ import java.util.regex.Pattern;
import static java.util.stream.Collectors.toList;
import static org.elasticsearch.action.ActionListener.wrap;
-import static org.elasticsearch.xpack.sql.util.StringUtils.EMPTY;
-
-public class JdbcServer {
+import static org.elasticsearch.common.Strings.coalesceToEmpty;
+import static org.elasticsearch.common.Strings.hasText;
+import static org.elasticsearch.common.Strings.tokenizeToStringArray;
+public class JdbcServer extends AbstractSqlServer {
private final PlanExecutor executor;
private final Supplier infoResponse;
@@ -49,58 +56,78 @@ public class JdbcServer {
// Delay building the response until runtime because the node name is not available at startup
this.infoResponse = () -> new InfoResponse(nodeName.get(), clusterName, version.major, version.minor, version.toString(), build.shortHash(), build.date());
}
-
- public void handle(Request req, ActionListener listener) {
- try {
- if (req instanceof InfoRequest) {
- listener.onResponse(info((InfoRequest) req));
- }
- else if (req instanceof MetaTableRequest) {
- listener.onResponse(metaTable((MetaTableRequest) req));
- }
- else if (req instanceof MetaColumnRequest) {
- listener.onResponse(metaColumn((MetaColumnRequest) req));
- }
- else if (req instanceof QueryInitRequest) {
- queryInit((QueryInitRequest) req, listener);
- }
- } catch (Exception ex) {
- listener.onResponse(JdbcServerProtoUtils.exception(ex, req.action));
+
+ @Override
+ protected void innerHandle(Request req, ActionListener listener) {
+ RequestType requestType = (RequestType) req.requestType();
+ switch (requestType) {
+ case INFO:
+ listener.onResponse(info((InfoRequest) req));
+ break;
+ case META_TABLE:
+ listener.onResponse(metaTable((MetaTableRequest) req));
+ break;
+ case META_COLUMN:
+ listener.onResponse(metaColumn((MetaColumnRequest) req));
+ break;
+ case QUERY_INIT:
+ queryInit((QueryInitRequest) req, listener);
+ break;
+ case QUERY_PAGE:
+ // TODO implement me
+ default:
+ throw new IllegalArgumentException("Unsupported action [" + requestType + "]");
}
}
-
+
+ @Override
+ protected ErrorResponse buildErrorResponse(Request request, String message, String cause, String stack) {
+ return new ErrorResponse((RequestType) request.requestType(), message, cause, stack);
+ }
+
+ @Override
+ protected ExceptionResponse buildExceptionResponse(Request request, String message, String cause,
+ SqlExceptionType exceptionType) {
+ return new ExceptionResponse((RequestType) request.requestType(), message, cause, exceptionType);
+ }
public InfoResponse info(InfoRequest req) {
return infoResponse.get();
}
public MetaTableResponse metaTable(MetaTableRequest req) {
- String indexPattern = Strings.hasText(req.index) ? StringUtils.jdbcToEsPattern(req.index) : "*";
+ String[] split = splitToIndexAndType(req.pattern());
+ String index = split[0];
+ String type = split[1];
+ String indexPattern = hasText(index) ? StringUtils.jdbcToEsPattern(index) : "*";
- Collection types = executor.catalog().listTypes(indexPattern, req.type);
+ Collection types = executor.catalog().listTypes(indexPattern, type);
return new MetaTableResponse(types.stream()
.map(t -> t.index() + "." + t.name())
.collect(toList()));
}
public MetaColumnResponse metaColumn(MetaColumnRequest req) {
- String indexPattern = Strings.hasText(req.index) ? StringUtils.jdbcToEsPattern(req.index) : "*";
+ String[] split = splitToIndexAndType(req.tablePattern());
+ String index = split[0];
+ String type = split[1];
+ String indexPattern = Strings.hasText(index) ? StringUtils.jdbcToEsPattern(index) : "*";
- Collection types = executor.catalog().listTypes(indexPattern, req.type);
+ Collection types = executor.catalog().listTypes(indexPattern, type);
- Pattern columnMatcher = Strings.hasText(req.column) ? StringUtils.likeRegex(req.column) : null;
+ Pattern columnMatcher = hasText(req.columnPattern()) ? StringUtils.likeRegex(req.columnPattern()) : null;
List resp = new ArrayList<>();
- for (EsType type : types) {
+ for (EsType esType : types) {
int pos = 0;
- for (Entry entry : type.mapping().entrySet()) {
+ for (Entry entry : esType.mapping().entrySet()) {
pos++;
if (columnMatcher == null || columnMatcher.matcher(entry.getKey()).matches()) {
String name = entry.getKey();
- String table = type.index() + "." + type.name();
- int tp = entry.getValue().sqlType().getVendorTypeNumber().intValue();
+ String table = esType.index() + "." + esType.name();
+ JDBCType tp = entry.getValue().sqlType();
int size = entry.getValue().precision();
- resp.add(new MetaColumnInfo(name, table, tp, size, pos));
+ resp.add(new MetaColumnInfo(table, name, tp, size, pos));
}
}
}
@@ -114,20 +141,33 @@ public class JdbcServer {
executor.sql(req.query, req.timeZone, wrap(c -> {
long stop = System.currentTimeMillis();
- String requestId = EMPTY;
+ String requestId = "";
if (c.hasNextSet() && c instanceof SearchHitRowSetCursor) {
requestId = StringUtils.nullAsEmpty(((SearchHitRowSetCursor) c).scrollId());
}
- List list = c.schema().stream()
- .map(e -> new ColumnInfo(e.name(), e.type().sqlType().getVendorTypeNumber().intValue(), EMPTY, EMPTY, EMPTY, EMPTY))
+ List columnInfo = c.schema().stream()
+ .map(e -> new ColumnInfo(e.name(), e.type().sqlType(), "", "", "", ""))
.collect(toList());
- listener.onResponse(new QueryInitResponse(start, stop, requestId, list, c));
- }, ex -> listener.onResponse(JdbcServerProtoUtils.exception(ex, req.action))));
+ listener.onResponse(new QueryInitResponse(start, stop, requestId, columnInfo, new RowSetCursorResultPage(c)));
+ }, ex -> listener.onResponse(exceptionResponse(req, ex))));
}
public void queryPage(QueryPageRequest req, ActionListener listener) {
throw new UnsupportedOperationException();
}
+
+ static String[] splitToIndexAndType(String pattern) {
+ String[] tokens = tokenizeToStringArray(pattern, ".");
+
+ if (tokens.length == 2) {
+ return tokens;
+ }
+ if (tokens.length != 1) {
+ throw new IllegalArgumentException("bad pattern: [" + pattern + "]");
+ }
+
+ return new String[] {coalesceToEmpty(pattern), ""};
+ }
}
\ No newline at end of file
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/RowSetCursorResultPage.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/RowSetCursorResultPage.java
new file mode 100644
index 00000000000..d2328f542c6
--- /dev/null
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/RowSetCursorResultPage.java
@@ -0,0 +1,46 @@
+/*
+ * 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.server.jdbc;
+
+import org.elasticsearch.xpack.sql.jdbc.net.protocol.ResultPage;
+import org.elasticsearch.xpack.sql.session.RowSet;
+import org.joda.time.ReadableInstant;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.sql.JDBCType;
+
+/**
+ * Adapts {@link RowSet} into a {@link ResultPage} so it can be serialized.
+ * Note that we are careful not to read the {@linkplain RowSet} more then
+ * once.
+ */
+public class RowSetCursorResultPage extends ResultPage {
+ private final RowSet rowSet;
+
+ public RowSetCursorResultPage(RowSet rowSet) {
+ this.rowSet = rowSet;
+ }
+
+ public void write(DataOutput out) throws IOException {
+ int rows = rowSet.size();
+ out.writeInt(rows);
+ if (rows == 0) {
+ return;
+ }
+ do {
+ for (int column = 0; column < rowSet.rowSize(); column++) {
+ JDBCType columnType = rowSet.schema().types().get(column).sqlType();
+ Object value = rowSet.column(column);
+ if (columnType == JDBCType.TIMESTAMP && value instanceof ReadableInstant) {
+ // TODO it feels like there should be a better way to do this
+ value = ((ReadableInstant) value).getMillis();
+ }
+ writeValue(out, value, columnType);
+ }
+ } while (rowSet.advanceRow());
+ }
+}
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/TransportJdbcAction.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/TransportJdbcAction.java
similarity index 95%
rename from sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/TransportJdbcAction.java
rename to sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/TransportJdbcAction.java
index 281f7c42416..e1895c1ce1b 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/plugin/jdbc/action/TransportJdbcAction.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/server/jdbc/TransportJdbcAction.java
@@ -3,7 +3,7 @@
* 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.jdbc.action;
+package org.elasticsearch.xpack.sql.server.jdbc;
import org.elasticsearch.Build;
import org.elasticsearch.Version;
@@ -18,7 +18,6 @@ import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.sql.analysis.catalog.EsCatalog;
import org.elasticsearch.xpack.sql.execution.PlanExecutor;
-import org.elasticsearch.xpack.sql.plugin.jdbc.server.JdbcServer;
import static org.elasticsearch.xpack.sql.util.ActionUtils.chain;
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/ThrowableBiConsumer.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/ThrowableBiConsumer.java
index cf1770d26ec..d1eb2e170ae 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/ThrowableBiConsumer.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/ThrowableBiConsumer.java
@@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.util;
import java.util.function.BiConsumer;
public interface ThrowableBiConsumer extends BiConsumer {
+ // NOCOMMIT replace with CheckedBiConsumer
@Override
default void accept(T t, U u) {
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/ThrowableConsumer.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/ThrowableConsumer.java
index cdd084aa98a..45c4c138b7f 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/ThrowableConsumer.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/ThrowableConsumer.java
@@ -7,7 +7,7 @@ package org.elasticsearch.xpack.sql.util;
import java.util.function.Consumer;
-public interface ThrowableConsumer extends Consumer {
+public interface ThrowableConsumer extends Consumer { // NOCOMMIT replace with CheckedConsumer
@Override
default void accept(T t) {
diff --git a/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/WrappingException.java b/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/WrappingException.java
index f406b602f53..4585219e005 100644
--- a/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/WrappingException.java
+++ b/sql/server/src/main/java/org/elasticsearch/xpack/sql/util/WrappingException.java
@@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.util;
public class WrappingException extends RuntimeException {
+ // NOCOMMIT probably not needed after replacing "ThrowableXXXX"
public WrappingException(String message, Exception cause) {
super(message, cause, false, false);
diff --git a/sql/shared-proto/build.gradle b/sql/shared-proto/build.gradle
new file mode 100644
index 00000000000..ba4dfedb847
--- /dev/null
+++ b/sql/shared-proto/build.gradle
@@ -0,0 +1,13 @@
+description = 'Request and response objects shared by the cli and jdbc.'
+
+dependencies {
+ testCompile project(':x-pack-elasticsearch:sql:test-utils')
+}
+
+forbiddenApisMain {
+ // does not depend on core, so only jdk and http signatures should be checked
+ signaturesURLs = [this.class.getResource('/forbidden/jdk-signatures.txt')]
+}
+
+// Tested in cli-proto and jdbc-proto
+test.enabled = false
diff --git a/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractErrorResponse.java b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractErrorResponse.java
new file mode 100644
index 00000000000..bdadeaaeeae
--- /dev/null
+++ b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractErrorResponse.java
@@ -0,0 +1,72 @@
+/*
+ * 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.protocol.shared;
+
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.RequestType;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Response sent when there is a server side error.
+ */
+public abstract class AbstractErrorResponse extends Response {
+ private final RequestTypeT requestType;
+ public final String message, cause, stack;
+
+ protected AbstractErrorResponse(RequestTypeT requestType, String message, String cause, String stack) {
+ this.requestType = requestType;
+ this.message = message;
+ this.cause = cause;
+ this.stack = stack;
+ }
+
+ protected AbstractErrorResponse(RequestTypeT requestType, DataInput in) throws IOException {
+ this.requestType = requestType;
+ message = in.readUTF();
+ cause = in.readUTF();
+ stack = in.readUTF();
+ }
+
+ @Override
+ protected final void write(int clientVersion, DataOutput out) throws IOException {
+ out.writeUTF(message);
+ out.writeUTF(cause);
+ out.writeUTF(stack);
+ }
+
+ @Override
+ public RequestTypeT requestType() { // NOCOMMIT do I need the T?
+ return requestType;
+ }
+
+ @Override
+ protected final String toStringBody() {
+ return "request=[" + requestType
+ + "] message=[" + message
+ + "] cause=[" + cause
+ + "] stack=[" + stack + "]";
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ AbstractErrorResponse> other = (AbstractErrorResponse>) obj;
+ return Objects.equals(requestType, other.requestType)
+ && Objects.equals(message, other.message)
+ && Objects.equals(cause, other.cause)
+ && Objects.equals(stack, other.stack);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(message, cause, stack);
+ }
+}
diff --git a/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractExceptionResponse.java b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractExceptionResponse.java
new file mode 100644
index 00000000000..2ba098e9cbd
--- /dev/null
+++ b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractExceptionResponse.java
@@ -0,0 +1,94 @@
+/*
+ * 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.protocol.shared;
+
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.SqlExceptionType;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.Objects;
+
+/**
+ * Response sent when there is a client side error.
+ */
+public abstract class AbstractExceptionResponse extends Response {
+ private final RequestTypeT requestType;
+ public final String message, cause;
+ private SqlExceptionType exceptionType;
+
+ protected AbstractExceptionResponse(RequestTypeT requestType, String message, String cause, SqlExceptionType exceptionType) {
+ if (requestType == null) {
+ throw new IllegalArgumentException("[requestType] cannot be null");
+ }
+ if (message == null) {
+ throw new IllegalArgumentException("[message] cannot be null");
+ }
+ if (cause == null) {
+ throw new IllegalArgumentException("[cause] cannot be null");
+ }
+ if (exceptionType == null) {
+ throw new IllegalArgumentException("[exceptionType] cannot be null");
+ }
+ this.requestType = requestType;
+ this.message = message;
+ this.cause = cause;
+ this.exceptionType = exceptionType;
+ }
+
+ protected AbstractExceptionResponse(RequestTypeT requestType, DataInput in) throws IOException {
+ this.requestType = requestType;
+ message = in.readUTF();
+ cause = in.readUTF();
+ exceptionType = SqlExceptionType.read(in);
+ }
+
+ @Override
+ protected final void write(int clientVersion, DataOutput out) throws IOException {
+ out.writeUTF(message);
+ out.writeUTF(cause);
+ exceptionType.write(out);
+ }
+
+ @Override
+ public RequestTypeT requestType() {
+ return requestType;
+ }
+
+ @Override
+ protected final String toStringBody() {
+ return "request=[" + requestType
+ + "] message=[" + message
+ + "] cause=[" + cause
+ + "] type=[" + exceptionType + "]";
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ AbstractExceptionResponse> other = (AbstractExceptionResponse>) obj;
+ return Objects.equals(requestType, other.requestType)
+ && Objects.equals(message, other.message)
+ && Objects.equals(cause, other.cause)
+ && Objects.equals(exceptionType, other.exceptionType);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(requestType, message, cause, exceptionType);
+ }
+
+ /**
+ * Build an exception to throw for this failure.
+ */
+ public SQLException asException() {
+ return exceptionType.asException(message);
+ }
+}
diff --git a/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractInfoRequest.java b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractInfoRequest.java
new file mode 100644
index 00000000000..78579986a98
--- /dev/null
+++ b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractInfoRequest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.protocol.shared;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Request general information about the server.
+ */
+public abstract class AbstractInfoRequest extends Request {
+ public final String jvmVersion, jvmVendor, jvmClassPath, osName, osVersion;
+
+ /**
+ * Build the info request containing information about the current JVM.
+ */
+ protected AbstractInfoRequest() {
+ jvmVersion = System.getProperty("java.version", "");
+ jvmVendor = System.getProperty("java.vendor", "");
+ jvmClassPath = System.getProperty("java.class.path", "");
+ osName = System.getProperty("os.name", "");
+ osVersion = System.getProperty("os.version", "");
+ }
+
+ protected AbstractInfoRequest(String jvmVersion, String jvmVendor, String jvmClassPath, String osName, String osVersion) {
+ this.jvmVersion = jvmVersion;
+ this.jvmVendor = jvmVendor;
+ this.jvmClassPath = jvmClassPath;
+ this.osName = osName;
+ this.osVersion = osVersion;
+ }
+
+ protected AbstractInfoRequest(int clientVersion, DataInput in) throws IOException {
+ jvmVersion = in.readUTF();
+ jvmVendor = in.readUTF();
+ jvmClassPath = in.readUTF();
+ osName = in.readUTF();
+ osVersion = in.readUTF();
+ }
+
+ @Override
+ public final void write(DataOutput out) throws IOException {
+ out.writeUTF(jvmVersion);
+ out.writeUTF(jvmVendor);
+ out.writeUTF(jvmClassPath);
+ out.writeUTF(osName);
+ out.writeUTF(osVersion);
+ }
+
+ @Override
+ protected final String toStringBody() {
+ return "jvm=[version=[" + jvmVersion
+ + "] vendor=[" + jvmVendor
+ + "] classPath=[" + jvmClassPath
+ + "]] os=[name=[" + osName
+ + "] version=[" + osVersion + "]]";
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ AbstractInfoRequest other = (AbstractInfoRequest) obj;
+ return Objects.equals(jvmVersion, other.jvmVersion)
+ && Objects.equals(jvmVendor, other.jvmVendor)
+ && Objects.equals(jvmClassPath, other.jvmClassPath)
+ && Objects.equals(osName, other.osName)
+ && Objects.equals(osVersion, other.osVersion);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(jvmVersion, jvmVendor, jvmClassPath, osName, osVersion);
+ }
+}
diff --git a/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractInfoResponse.java b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractInfoResponse.java
new file mode 100644
index 00000000000..b21ed387b36
--- /dev/null
+++ b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractInfoResponse.java
@@ -0,0 +1,83 @@
+/*
+ * 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.protocol.shared;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * General information about the server.
+ */
+public abstract class AbstractInfoResponse extends Response {
+ public final String node, cluster, versionString, versionHash, versionDate;
+ public final int majorVersion, minorVersion;
+
+ protected AbstractInfoResponse(String nodeName, String clusterName, byte versionMajor, byte versionMinor, String version,
+ String versionHash, String versionDate) {
+ this.node = nodeName;
+ this.cluster = clusterName;
+ this.versionString = version;
+ this.versionHash = versionHash;
+ this.versionDate = versionDate;
+
+ this.majorVersion = versionMajor;
+ this.minorVersion = versionMinor;
+ }
+
+ protected AbstractInfoResponse(Request request, DataInput in) throws IOException {
+ node = in.readUTF();
+ cluster = in.readUTF();
+ majorVersion = in.readByte();
+ minorVersion = in.readByte();
+ versionString = in.readUTF();
+ versionHash = in.readUTF();
+ versionDate = in.readUTF();
+ }
+
+ @Override
+ protected final void write(int clientVersion, DataOutput out) throws IOException {
+ out.writeUTF(node);
+ out.writeUTF(cluster);
+ out.writeByte(majorVersion);
+ out.writeByte(minorVersion);
+ out.writeUTF(versionString);
+ out.writeUTF(versionHash);
+ out.writeUTF(versionDate);
+ }
+
+ @Override
+ protected final String toStringBody() {
+ return "node=[" + node
+ + "] cluster=[" + cluster
+ + "] version=[" + versionString
+ + "]/[major=[" + majorVersion
+ + "] minor=[" + minorVersion
+ + "] hash=[" + versionHash
+ + "] date=[" + versionDate + "]]";
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+ AbstractInfoResponse other = (AbstractInfoResponse) obj;
+ return Objects.equals(node, other.node)
+ && Objects.equals(cluster, other.cluster)
+ && Objects.equals(majorVersion, other.majorVersion)
+ && Objects.equals(minorVersion, other.minorVersion)
+ && Objects.equals(versionString, other.versionString)
+ && Objects.equals(versionHash, other.versionHash)
+ && Objects.equals(versionDate, other.versionDate);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(node, cluster, majorVersion, minorVersion, versionString, versionHash, versionDate);
+ }
+}
\ No newline at end of file
diff --git a/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractProto.java b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractProto.java
new file mode 100644
index 00000000000..bcb83724981
--- /dev/null
+++ b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/AbstractProto.java
@@ -0,0 +1,144 @@
+/*
+ * 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.protocol.shared;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLDataException;
+import java.sql.SQLException;
+import java.sql.SQLRecoverableException;
+import java.sql.SQLSyntaxErrorException;
+import java.sql.SQLTimeoutException;
+import java.util.function.Function;
+
+import javax.sql.rowset.serial.SerialException;
+
+import static java.util.Collections.emptyMap;
+
+/**
+ * Base implementation for the binary protocol for the CLI and JDBC.
+ * All backwards compatibility is done on the server side using the
+ * version number sent in the header.
+ */
+public abstract class AbstractProto {
+ private static final int MAGIC_NUMBER = 0x0C0DEC110;
+ public static final int CURRENT_VERSION = 000_000_001;
+
+ public void writeRequest(Request request, DataOutput out) throws IOException {
+ writeHeader(CURRENT_VERSION, out);
+ request.requestType().write(out);
+ request.write(out);
+ }
+
+ public Request readRequest(DataInput in) throws IOException {
+ int clientVersion = readHeader(in);
+ if (clientVersion > CURRENT_VERSION) {
+ throw new IOException("Unknown client version [" + clientVersion + "]. Always upgrade sql last.");
+ // NOCOMMIT I believe we usually advise upgrading the clients *first* so this might be backwards.....
+ }
+ return readRequestType(in).reader().read(clientVersion, in);
+ }
+
+ public void writeResponse(Response response, int clientVersion, DataOutput out) throws IOException {
+ writeHeader(clientVersion, out);
+ response.responseType().write(out);
+ response.write(clientVersion, out);
+ }
+
+ public Response readResponse(Request request, DataInput in) throws IOException {
+ int version = readHeader(in);
+ if (version != CURRENT_VERSION) {
+ throw new IOException("Response version [" + version + "] does not match client version ["
+ + CURRENT_VERSION + "]. Server is busted.");
+ }
+ // NOCOMMIT why do I need the response type at all? Just a byte for err/exception/normal, then get response type from request.
+ Response response = readResponseType(in).reader().read(request, in);
+ if (response.requestType() != request.requestType()) {
+ throw new IOException("Expected request type to be [" + request.requestType()
+ + "] but was [" + response.requestType() + "]. Server is busted.");
+ }
+ return response;
+ }
+
+ /**
+ * Exception type.
+ */
+ public enum SqlExceptionType {
+ UNKNOWN(SQLException::new),
+ SERIAL(SerialException::new),
+ CLIENT_INFO(message -> new SQLClientInfoException(message, emptyMap())),
+ DATA(SQLDataException::new),
+ SYNTAX(SQLSyntaxErrorException::new),
+ RECOVERABLE(SQLRecoverableException::new),
+ TIMEOUT(SQLTimeoutException::new);
+
+ private final Function toException;
+
+ SqlExceptionType(Function toException) {
+ this.toException = toException;
+ }
+
+ public static SqlExceptionType read(DataInput in) throws IOException {
+ byte b = in.readByte();
+ try {
+ return values()[b];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Unknown request type [" + b + "]", e);
+ }
+ }
+
+ public void write(DataOutput out) throws IOException {
+ out.writeByte(ordinal());
+ }
+
+ SQLException asException(String message) {
+ if (message == null) {
+ throw new IllegalArgumentException("[message] cannot be null");
+ }
+ return toException.apply(message);
+ }
+ }
+
+ protected abstract RequestType readRequestType(DataInput in) throws IOException;
+ protected abstract ResponseType readResponseType(DataInput in) throws IOException;
+ @FunctionalInterface
+ protected interface RequestReader {
+ Request read(int clientVersion, DataInput in) throws IOException;
+ }
+ protected interface RequestType {
+ void write(DataOutput out) throws IOException;
+ RequestReader reader();
+ }
+ @FunctionalInterface
+ protected interface ResponseReader {
+ Response read(Request request, DataInput in) throws IOException;
+ }
+ protected interface ResponseType {
+ void write(DataOutput out) throws IOException;
+ ResponseReader reader();
+ }
+
+ private static void writeHeader(int clientVersion, DataOutput out) throws IOException {
+ out.writeInt(MAGIC_NUMBER);
+ out.writeInt(clientVersion);
+ }
+
+ /**
+ * Read the protocol header.
+ * @return the version
+ * @throws IOException if there is an underlying {@linkplain IOException} or if the protocol is malformed
+ */
+ private static int readHeader(DataInput in) throws IOException {
+ int magic = in.readInt();
+ if (magic != MAGIC_NUMBER) {
+ throw new IOException("Unknown protocol magic number [" + Integer.toHexString(magic) + "]");
+ }
+ int version = in.readInt();
+ return version;
+ }
+}
\ No newline at end of file
diff --git a/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Nullable.java b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Nullable.java
new file mode 100644
index 00000000000..e640798fcb8
--- /dev/null
+++ b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Nullable.java
@@ -0,0 +1,23 @@
+/*
+ * 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.protocol.shared;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The presence of this annotation on a method parameter indicates that
+ * {@code null} is an acceptable value for that parameter. It should not be
+ * used for parameters of primitive types.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
+public @interface Nullable {
+}
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Request.java b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Request.java
similarity index 85%
rename from sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Request.java
rename to sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Request.java
index 21dce66507c..7a1af23c806 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Request.java
+++ b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Request.java
@@ -3,9 +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.cli.net.protocol;
+package org.elasticsearch.xpack.sql.protocol.shared;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.RequestType;
import java.io.DataOutput;
import java.io.IOException;
@@ -20,7 +20,7 @@ public abstract class Request {
* Write this request to the {@link DataOutput}. Implementers should
* be kind and stick this right under the ctor that reads the response.
*/
- abstract void write(DataOutput out) throws IOException;
+ protected abstract void write(DataOutput out) throws IOException;
/**
* Body to go into the {@link #toString()} result.
@@ -45,5 +45,4 @@ public abstract class Request {
*/
@Override
public abstract int hashCode();
-
}
diff --git a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Response.java b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Response.java
similarity index 76%
rename from sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Response.java
rename to sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Response.java
index 8c106f15ad1..ca228e6b9fd 100644
--- a/sql/cli-proto/src/main/java/org/elasticsearch/xpack/sql/cli/net/protocol/Response.java
+++ b/sql/shared-proto/src/main/java/org/elasticsearch/xpack/sql/protocol/shared/Response.java
@@ -3,10 +3,10 @@
* 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.cli.net.protocol;
+package org.elasticsearch.xpack.sql.protocol.shared;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.RequestType;
-import org.elasticsearch.xpack.sql.cli.net.protocol.Proto.ResponseType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.RequestType;
+import org.elasticsearch.xpack.sql.protocol.shared.AbstractProto.ResponseType;
import java.io.DataOutput;
import java.io.IOException;
@@ -23,7 +23,7 @@ public abstract class Response {
* the message. This should be used to send a response compatible
* with the client.
*/
- abstract void write(int clientVersion, DataOutput out) throws IOException;
+ protected abstract void write(int clientVersion, DataOutput out) throws IOException;
/**
* Body to go into the {@link #toString()} result.
@@ -33,12 +33,12 @@ public abstract class Response {
/**
* Type of the request for which this is the response.
*/
- abstract RequestType requestType();
+ public abstract RequestType requestType();
/**
* Type of this response.
*/
- abstract ResponseType responseType();
+ public abstract ResponseType responseType();
/*
* Must properly implement {@linkplain #equals(Object)} for
diff --git a/sql/test-utils/src/main/java/org/elasticsearch/xpack/sql/test/server/ProtoHandler.java b/sql/test-utils/src/main/java/org/elasticsearch/xpack/sql/test/server/ProtoHandler.java
index 73213d58b50..62c172e4a4f 100644
--- a/sql/test-utils/src/main/java/org/elasticsearch/xpack/sql/test/server/ProtoHandler.java
+++ b/sql/test-utils/src/main/java/org/elasticsearch/xpack/sql/test/server/ProtoHandler.java
@@ -28,16 +28,13 @@ public abstract class ProtoHandler implements HttpHandler, AutoCloseable {
private final TimeValue TV = TimeValue.timeValueSeconds(5);
protected final NodeInfo info;
protected final String clusterName;
- private final CheckedFunction headerReader;
private final CheckedFunction toProto;
-
- protected ProtoHandler(Client client, CheckedFunction headerReader,
- CheckedFunction toProto) {
+
+ protected ProtoHandler(Client client, CheckedFunction toProto) {
NodesInfoResponse niResponse = client.admin().cluster().prepareNodesInfo("_local").clear().get(TV);
info = niResponse.getNodes().get(0);
clusterName = niResponse.getClusterName().value();
- this.headerReader = headerReader;
this.toProto = toProto;
}
@@ -52,12 +49,6 @@ public abstract class ProtoHandler implements HttpHandler, AutoCloseable {
}
try (DataInputStream in = new DataInputStream(http.getRequestBody())) {
- String msg = headerReader.apply(in);
- if (msg != null) {
- http.sendResponseHeaders(RestStatus.BAD_REQUEST.getStatus(), -1);
- http.close();
- return;
- }
handle(http, in);
} catch (Exception ex) {
fail(http, ex);