You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by el...@apache.org on 2016/03/07 19:28:09 UTC

[31/59] [partial] calcite git commit: [CALCITE-1078] Detach avatica from the core calcite Maven project

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/protobuf/common.proto
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/protobuf/common.proto b/avatica/core/src/main/protobuf/common.proto
new file mode 100644
index 0000000..bd116c3
--- /dev/null
+++ b/avatica/core/src/main/protobuf/common.proto
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "org.apache.calcite.avatica.proto";
+
+// Details about a connection
+message ConnectionProperties {
+  bool is_dirty = 1;
+  bool auto_commit = 2;
+  bool has_auto_commit = 7; // field is a Boolean, need to discern null and default value
+  bool read_only = 3;
+  bool has_read_only = 8; // field is a Boolean, need to discern null and default value
+  uint32 transaction_isolation = 4;
+  string catalog = 5;
+  string schema = 6;
+}
+
+// Statement handle
+message StatementHandle {
+  string connection_id = 1;
+  uint32 id = 2;
+  Signature signature = 3;
+}
+
+// Results of preparing a statement
+message Signature {
+  repeated ColumnMetaData columns = 1;
+  string sql = 2;
+  repeated AvaticaParameter parameters = 3;
+  CursorFactory cursor_factory = 4;
+  StatementType statementType = 5;
+}
+
+// Has to be consistent with Meta.StatementType
+enum StatementType {
+  SELECT = 0;
+  INSERT = 1;
+  UPDATE = 2;
+  DELETE = 3;
+  UPSERT = 4;
+  MERGE = 5;
+  OTHER_DML = 6;
+  CREATE = 7;
+  DROP = 8;
+  ALTER = 9;
+  OTHER_DDL = 10;
+  CALL = 11;
+}
+
+message ColumnMetaData {
+  uint32 ordinal = 1;
+  bool auto_increment = 2;
+  bool case_sensitive = 3;
+  bool searchable = 4;
+  bool currency = 5;
+  uint32 nullable = 6;
+  bool signed = 7;
+  uint32 display_size = 8;
+  string label = 9;
+  string column_name = 10;
+  string schema_name = 11;
+  uint32 precision = 12;
+  uint32 scale = 13;
+  string table_name = 14;
+  string catalog_name = 15;
+  bool read_only = 16;
+  bool writable = 17;
+  bool definitely_writable = 18;
+  string column_class_name = 19;
+  AvaticaType type = 20;
+}
+
+enum Rep {
+  PRIMITIVE_BOOLEAN = 0;
+  PRIMITIVE_BYTE = 1;
+  PRIMITIVE_CHAR = 2;
+  PRIMITIVE_SHORT = 3;
+  PRIMITIVE_INT = 4;
+  PRIMITIVE_LONG = 5;
+  PRIMITIVE_FLOAT = 6;
+  PRIMITIVE_DOUBLE = 7;
+  BOOLEAN = 8;
+  BYTE = 9;
+  CHARACTER = 10;
+  SHORT = 11;
+  INTEGER = 12;
+  LONG = 13;
+  FLOAT = 14;
+  DOUBLE = 15;
+  BIG_INTEGER = 25;
+  BIG_DECIMAL = 26;
+  JAVA_SQL_TIME = 16;
+  JAVA_SQL_TIMESTAMP = 17;
+  JAVA_SQL_DATE = 18;
+  JAVA_UTIL_DATE = 19;
+  BYTE_STRING = 20;
+  STRING = 21;
+  NUMBER = 22;
+  OBJECT = 23;
+  NULL = 24;
+  ARRAY = 27;
+  STRUCT = 28;
+  MULTISET = 29;
+}
+
+// Base class for a column type
+message AvaticaType {
+  uint32 id = 1;
+  string name = 2;
+  Rep rep = 3;
+
+  repeated ColumnMetaData columns = 4; // Only present when name = STRUCT
+  AvaticaType component = 5; // Only present when name = ARRAY
+}
+
+// Metadata for a parameter
+message AvaticaParameter {
+  bool signed = 1;
+  uint32 precision = 2;
+  uint32 scale = 3;
+  uint32 parameter_type = 4;
+  string type_name = 5;
+  string class_name = 6;
+  string name = 7;
+}
+
+// Information necessary to convert an Iterable into a Calcite Cursor
+message CursorFactory {
+  enum Style {
+    OBJECT = 0;
+    RECORD = 1;
+    RECORD_PROJECTION = 2;
+    ARRAY = 3;
+    LIST = 4;
+    MAP = 5;
+  }
+
+  Style style = 1;
+  string class_name = 2;
+  repeated string field_names = 3;
+}
+
+// A collection of rows
+message Frame {
+  uint64 offset = 1;
+  bool done = 2;
+  repeated Row rows = 3;
+}
+
+// A row is a collection of values
+message Row {
+  repeated ColumnValue value = 1;
+}
+
+// Database property, list of functions the database provides for a certain operation
+message DatabaseProperty {
+  string name = 1;
+  repeated string functions = 2;
+}
+
+// Message which encapsulates another message to support a single RPC endpoint
+message WireMessage {
+  string name = 1;
+  bytes wrapped_message = 2;
+}
+
+// A value might be a TypedValue or an Array of TypedValue's
+message ColumnValue {
+  repeated TypedValue value = 1; // deprecated, use array_value or scalar_value
+  repeated TypedValue array_value = 2;
+  bool has_array_value = 3; // Is an array value set?
+  TypedValue scalar_value = 4;
+}
+
+// Generic wrapper to support any SQL type. Struct-like to work around no polymorphism construct.
+message TypedValue {
+  Rep type = 1; // The actual type that was serialized in the general attribute below
+
+  bool bool_value = 2; // boolean
+  string string_value = 3; // char/varchar
+  sint64 number_value = 4; // var-len encoding lets us shove anything from byte to long
+                           // includes numeric types and date/time types.
+  bytes bytes_values = 5; // binary/varbinary
+  double double_value = 6; // big numbers
+  bool null = 7; // a null object
+}
+
+// The severity of some unexpected outcome to an operation.
+// Protobuf enum values must be unique across all other enums
+enum Severity {
+  UNKNOWN_SEVERITY = 0;
+  FATAL_SEVERITY = 1;
+  ERROR_SEVERITY = 2;
+  WARNING_SEVERITY = 3;
+}
+
+// Enumeration corresponding to DatabaseMetaData operations
+enum MetaDataOperation {
+  GET_ATTRIBUTES = 0;
+  GET_BEST_ROW_IDENTIFIER = 1;
+  GET_CATALOGS = 2;
+  GET_CLIENT_INFO_PROPERTIES = 3;
+  GET_COLUMN_PRIVILEGES = 4;
+  GET_COLUMNS = 5;
+  GET_CROSS_REFERENCE = 6;
+  GET_EXPORTED_KEYS = 7;
+  GET_FUNCTION_COLUMNS = 8;
+  GET_FUNCTIONS = 9;
+  GET_IMPORTED_KEYS = 10;
+  GET_INDEX_INFO = 11;
+  GET_PRIMARY_KEYS = 12;
+  GET_PROCEDURE_COLUMNS = 13;
+  GET_PROCEDURES = 14;
+  GET_PSEUDO_COLUMNS = 15;
+  GET_SCHEMAS = 16;
+  GET_SCHEMAS_WITH_ARGS = 17;
+  GET_SUPER_TABLES = 18;
+  GET_SUPER_TYPES = 19;
+  GET_TABLE_PRIVILEGES = 20;
+  GET_TABLES = 21;
+  GET_TABLE_TYPES = 22;
+  GET_TYPE_INFO = 23;
+  GET_UDTS = 24;
+  GET_VERSION_COLUMNS = 25;
+}
+
+// Represents the breadth of arguments to DatabaseMetaData functions
+message MetaDataOperationArgument {
+  enum ArgumentType {
+    STRING = 0;
+    BOOL = 1;
+    INT = 2;
+    REPEATED_STRING = 3;
+    REPEATED_INT = 4;
+    NULL = 5;
+  }
+    
+  string string_value = 1;
+  bool bool_value = 2;
+  sint32 int_value = 3;
+  repeated string string_array_values = 4;
+  repeated sint32 int_array_values = 5;
+  ArgumentType type = 6;
+}
+
+enum StateType {
+  SQL = 0;
+  METADATA = 1;
+}
+
+message QueryState {
+  StateType type = 1;
+  string sql = 2;
+  MetaDataOperation op = 3;
+  repeated MetaDataOperationArgument args = 4;
+  bool has_args = 5;
+  bool has_sql = 6;
+  bool has_op = 7;
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/protobuf/requests.proto
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/protobuf/requests.proto b/avatica/core/src/main/protobuf/requests.proto
new file mode 100644
index 0000000..31b0941
--- /dev/null
+++ b/avatica/core/src/main/protobuf/requests.proto
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+syntax = "proto3";
+
+option java_package = "org.apache.calcite.avatica.proto";
+
+import "common.proto";
+
+// Request for Meta#getCatalogs()
+message CatalogsRequest {
+  string connection_id = 1;
+}
+
+// Request for Meta#getDatabaseProperties()
+message DatabasePropertyRequest {
+  string connection_id = 1;
+}
+
+// Request for Meta#getSchemas(String, org.apache.calcite.avatica.Meta.Pat)}
+message SchemasRequest {
+  string catalog = 1;
+  string schema_pattern = 2;
+  string connection_id = 3;
+}
+
+// Request for Request for Meta#getTables(String, org.apache.calcite.avatica.Meta.Pat,
+//   org.apache.calcite.avatica.Meta.Pat, java.util.List)
+message TablesRequest {
+  string catalog = 1;
+  string schema_pattern = 2;
+  string table_name_pattern = 3;
+  repeated string type_list = 4;
+  bool has_type_list = 6; // Having an empty type_list is distinct from a null type_list
+  string connection_id = 7;
+}
+
+// Request for Meta#getTableTypes()
+message TableTypesRequest {
+  string connection_id = 1;
+}
+
+// Request for Meta#getColumns(String, org.apache.calcite.avatica.Meta.Pat,
+//   org.apache.calcite.avatica.Meta.Pat, org.apache.calcite.avatica.Meta.Pat).
+message ColumnsRequest {
+  string catalog = 1;
+  string schema_pattern = 2;
+  string table_name_pattern = 3;
+  string column_name_pattern = 4;
+  string connection_id = 5;
+}
+
+// Request for Meta#getTypeInfo()
+message TypeInfoRequest {
+  string connection_id = 1;
+}
+
+// Request for Meta#prepareAndExecute(Meta.StatementHandle, String, long, Meta.PrepareCallback)
+message PrepareAndExecuteRequest {
+  string connection_id = 1;
+  string sql = 2;
+  uint64 max_row_count = 3;
+  uint32 statement_id = 4;
+}
+
+// Request for Meta.prepare(Meta.ConnectionHandle, String, long)
+message PrepareRequest {
+  string connection_id = 1;
+  string sql = 2;
+  uint64 max_row_count = 3;
+}
+
+// Request for Meta#fetch(Meta.StatementHandle, List, long, int)
+message FetchRequest {
+  string connection_id = 1;
+  uint32 statement_id = 2;
+  uint64 offset = 3;
+  uint32 fetch_max_row_count = 4; // Maximum number of rows to be returned in the frame. Negative means no limit.
+}
+
+// Request for Meta#createStatement(Meta.ConnectionHandle)
+message CreateStatementRequest {
+  string connection_id = 1;
+}
+
+// Request for Meta#closeStatement(Meta.StatementHandle)
+message CloseStatementRequest {
+  string connection_id = 1;
+  uint32 statement_id = 2;
+}
+
+// Request for Meta#openConnection(Meta.ConnectionHandle, Map<String, String>)
+message OpenConnectionRequest {
+  string connection_id = 1;
+  map<string, string> info = 2;
+}
+
+// Request for Meta#closeConnection(Meta.ConnectionHandle)
+message CloseConnectionRequest {
+  string connection_id = 1;
+}
+
+message ConnectionSyncRequest {
+  string connection_id = 1;
+  ConnectionProperties conn_props = 2;
+}
+
+// Request for Meta#execute(Meta.ConnectionHandle, list, long)
+message ExecuteRequest {
+  StatementHandle statementHandle = 1;
+  repeated TypedValue parameter_values = 2;
+  uint64 max_row_count = 3;
+  bool has_parameter_values = 4;
+}
+
+
+message SyncResultsRequest {
+  string connection_id = 1;
+  uint32 statement_id = 2;
+  QueryState state = 3;
+  uint64 offset = 4;
+}
+
+// Request to invoke a commit on a Connection
+message CommitRequest {
+  string connection_id = 1;
+}
+
+// Request to invoke rollback on a Connection
+message RollbackRequest {
+  string connection_id = 1;
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/protobuf/responses.proto
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/protobuf/responses.proto b/avatica/core/src/main/protobuf/responses.proto
new file mode 100644
index 0000000..01a62ed
--- /dev/null
+++ b/avatica/core/src/main/protobuf/responses.proto
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+syntax = "proto3";
+
+option java_package = "org.apache.calcite.avatica.proto";
+
+import "common.proto";
+
+// Response that contains a result set.
+message ResultSetResponse {
+  string connection_id = 1;
+  uint32 statement_id = 2;
+  bool own_statement = 3;
+  Signature signature = 4;
+  Frame first_frame = 5;
+  uint64 update_count = 6; // -1 for normal result sets, else this response contains a dummy result set
+                                    // with no signature nor other data.
+  RpcMetadata metadata = 7;
+}
+
+// Response to PrepareAndExecuteRequest
+message ExecuteResponse {
+  repeated ResultSetResponse results = 1;
+  bool missing_statement = 2; // Did the request fail because of no-cached statement
+  RpcMetadata metadata = 3;
+}
+
+// Response to PrepareRequest
+message PrepareResponse {
+  StatementHandle statement = 1;
+  RpcMetadata metadata = 2;
+}
+
+// Response to FetchRequest
+message FetchResponse {
+  Frame frame = 1;
+  bool missing_statement = 2; // Did the request fail because of no-cached statement
+  bool missing_results = 3; // Did the request fail because of a cached-statement w/o ResultSet
+  RpcMetadata metadata = 4;
+}
+
+// Response to CreateStatementRequest
+message CreateStatementResponse {
+  string connection_id = 1;
+  uint32 statement_id = 2;
+  RpcMetadata metadata = 3;
+}
+
+// Response to CloseStatementRequest
+message CloseStatementResponse {
+  RpcMetadata metadata = 1;
+}
+
+// Response to OpenConnectionRequest {
+message OpenConnectionResponse {
+  RpcMetadata metadata = 1;
+}
+
+// Response to CloseConnectionRequest {
+message CloseConnectionResponse {
+  RpcMetadata metadata = 1;
+}
+
+// Response to ConnectionSyncRequest
+message ConnectionSyncResponse {
+  ConnectionProperties conn_props = 1;
+  RpcMetadata metadata = 2;
+}
+
+message DatabasePropertyElement {
+  DatabaseProperty key = 1;
+  TypedValue value = 2;
+  RpcMetadata metadata = 3;
+}
+
+// Response for Meta#getDatabaseProperties()
+message DatabasePropertyResponse {
+  repeated DatabasePropertyElement props = 1;
+  RpcMetadata metadata = 2;
+}
+
+// Send contextual information about some error over the wire from the server.
+message ErrorResponse {
+  repeated string exceptions = 1; // exception stacktraces, many for linked exceptions.
+  bool has_exceptions = 7; // are there stacktraces contained?
+  string error_message = 2; // human readable description
+  Severity severity = 3;
+  uint32 error_code = 4; // numeric identifier for error
+  string sql_state = 5; // five-character standard-defined value
+  RpcMetadata metadata = 6;
+}
+
+message SyncResultsResponse {
+  bool missing_statement = 1; // Server doesn't have the statement with the ID from the request
+  bool more_results = 2; // Should the client fetch() to get more results
+  RpcMetadata metadata = 3;
+}
+
+// Generic metadata for the server to return with each response.
+message RpcMetadata {
+  string server_address = 1; // The host:port of the server
+}
+
+// Response to a commit request
+message CommitResponse {
+
+}
+
+// Response to a rollback request
+message RollbackResponse {
+
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/resources/META-INF/LICENSE
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/resources/META-INF/LICENSE b/avatica/core/src/main/resources/META-INF/LICENSE
new file mode 100644
index 0000000..7764d41
--- /dev/null
+++ b/avatica/core/src/main/resources/META-INF/LICENSE
@@ -0,0 +1,257 @@
+
+                                 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.
+
+
+
+
+
+-----------------------------------------------------------------------
+
+APACHE CALCITE SUBCOMPONENTS:
+
+The Apache Calcite project contains subcomponents with separate copyright
+notices and license terms. Your use of the source code for the these
+subcomponents is subject to the terms and conditions of the following
+licenses.
+
+-----------------------------------------------------------------------
+ 3-clause BSD license
+-----------------------------------------------------------------------
+
+The Apache Calcite Avatica project bundles Protocol Buffers, which is available
+under the following "3-clause BSD" license:
+
+    Copyright 2014, Google Inc. All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, are permitted provided that the following
+    conditions are met:
+
+    Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following disclaimer
+    in the documentation and/or other materials provided with the
+    distribution.
+
+    Neither the name of Google Inc. nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    Code generated by the Protocol Buffer compiler is owned by the owner
+    of the input file used when generating it. This code is not
+    standalone and requires a support library to be linked with it. This
+    support library is itself covered by the above license.

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/resources/META-INF/services/java.sql.Driver
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/resources/META-INF/services/java.sql.Driver b/avatica/core/src/main/resources/META-INF/services/java.sql.Driver
new file mode 100644
index 0000000..beb1ac0
--- /dev/null
+++ b/avatica/core/src/main/resources/META-INF/services/java.sql.Driver
@@ -0,0 +1 @@
+org.apache.calcite.avatica.remote.Driver

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/scripts/generate-protobuf.sh
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/scripts/generate-protobuf.sh b/avatica/core/src/main/scripts/generate-protobuf.sh
new file mode 100755
index 0000000..c4d3abe
--- /dev/null
+++ b/avatica/core/src/main/scripts/generate-protobuf.sh
@@ -0,0 +1,99 @@
+#! /usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you 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.
+
+# This script will regenerate the protobuf code for Avatica. Slightly
+# modified script over one in Accumulo.
+
+# NOTES:
+#   To support this script being called by other modules, only edit the right side.
+#   In other scripts, set the variables that diverge from the defaults below, then call this script.
+#   Leave the BUILD_DIR and FINAL_DIR alone for Maven builds.
+# ========================================================================================================================
+[[ -z $REQUIRED_PROTOC_VERSION ]] && REQUIRED_PROTOC_VERSION='libprotoc 3.0.0'
+[[ -z $BUILD_DIR ]]               && BUILD_DIR='target/proto-tmp'
+[[ -z $FINAL_DIR ]]               && FINAL_DIR='src/main'
+# ========================================================================================================================
+
+fail() {
+  echo "$@"
+  exit 1
+}
+
+# Test to see if we have protoc installed
+VERSION=$(protoc --version 2>/dev/null | grep -F "${REQUIRED_PROTOC_VERSION}" |  wc -l)
+if [[ $VERSION -ne 1 ]] ; then
+  # Nope: bail
+  echo "****************************************************"
+  echo "*** protoc is not available"
+  echo "***   expecting 'protoc --version' to return ${REQUIRED_PROTOC_VERSION}"
+  echo "*** generated code will not be updated"
+  fail "****************************************************"
+fi
+
+# Ensure output directories are created
+PROTOC_ARGS="-I src/main/protobuf --java_out=$BUILD_DIR"
+rm -rf $BUILD_DIR
+mkdir -p $BUILD_DIR
+
+protoc ${PROTOC_ARGS} src/main/protobuf/*.proto || fail unable to generate Java protocol buffer classes
+
+# For all generated protobuf code, suppress all warnings and add the LICENSE header
+s='@SuppressWarnings({"unused", "rawtypes"})'
+find $BUILD_DIR -name '*.java' -print0 | xargs -0 sed -i.orig -e 's/\(public final class \)/'"$s"' \1/'
+
+PREFIX="/*
+"
+LINE_NOTATION=" *"
+SUFFIX="
+ */"
+FILE_SUFFIX=(.java)
+
+for file in "${FILE_SUFFIX[@]}"; do
+  for f in $(find $BUILD_DIR/ -name "*$file"); do
+    cat - "$f" > "${f}-with-license" <<EOF
+${PREFIX}${LINE_NOTATION} Licensed to the Apache Software Foundation (ASF) under one or more
+${LINE_NOTATION} contributor license agreements.  See the NOTICE file distributed with
+${LINE_NOTATION} this work for additional information regarding copyright ownership.
+${LINE_NOTATION} The ASF licenses this file to you under the Apache License, Version 2.0
+${LINE_NOTATION} (the "License"); you may not use this file except in compliance with
+${LINE_NOTATION} the License.  You may obtain a copy of the License at
+${LINE_NOTATION}
+${LINE_NOTATION} http://www.apache.org/licenses/LICENSE-2.0
+${LINE_NOTATION}
+${LINE_NOTATION} Unless required by applicable law or agreed to in writing, software
+${LINE_NOTATION} distributed under the License is distributed on an "AS IS" BASIS,
+${LINE_NOTATION} WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+${LINE_NOTATION} See the License for the specific language governing permissions and
+${LINE_NOTATION} limitations under the License.${SUFFIX}
+EOF
+  done
+done
+
+# For every generated java file, compare it with the version-controlled one, and copy the ones that have changed into place
+SDIR="${BUILD_DIR}/org/apache/calcite/avatica/proto"
+DDIR="${FINAL_DIR}/java/org/apache/calcite/avatica/proto"
+FILE_SUFFIX=(.java)
+mkdir -p "$DDIR"
+for file in "${FILE_SUFFIX[@]}"; do
+  for f in $(find $SDIR -name *$file); do
+    DEST=$DDIR/$(basename "$f")
+    if ! cmp -s "${f}-with-license" "${DEST}" ; then
+      echo cp -f "${f}-with-license" "${DEST}"
+      cp -f "${f}-with-license" "${DEST}" || fail unable to copy files to java workspace
+    fi
+  done
+done

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaConnectionTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaConnectionTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaConnectionTest.java
new file mode 100644
index 0000000..a1414c3
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaConnectionTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Properties;
+
+/**
+ * Tests for AvaticaConnection
+ */
+public class AvaticaConnectionTest {
+
+  @Test
+  public void testNumExecuteRetries() {
+    AvaticaConnection statement = Mockito.mock(AvaticaConnection.class);
+
+    Mockito.when(statement.getNumStatementRetries(Mockito.any(Properties.class)))
+      .thenCallRealMethod();
+
+    // Bad argument should throw an exception
+    try {
+      statement.getNumStatementRetries(null);
+      Assert.fail("Calling getNumStatementRetries with a null object should throw an exception");
+    } catch (NullPointerException e) {
+      // Pass
+    }
+
+    Properties props = new Properties();
+
+    // Verify the default value
+    Assert.assertEquals(Long.valueOf(AvaticaConnection.NUM_EXECUTE_RETRIES_DEFAULT).longValue(),
+        statement.getNumStatementRetries(props));
+
+    // Set a non-default value
+    props.setProperty(AvaticaConnection.NUM_EXECUTE_RETRIES_KEY, "10");
+
+    // Verify that we observe that value
+    Assert.assertEquals(10, statement.getNumStatementRetries(props));
+  }
+
+}
+
+// End AvaticaConnectionTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/FrameTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/FrameTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/FrameTest.java
new file mode 100644
index 0000000..bdd989b
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/FrameTest.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica;
+
+import org.apache.calcite.avatica.Meta.Frame;
+import org.apache.calcite.avatica.proto.Common;
+import org.apache.calcite.avatica.proto.Common.ColumnValue;
+import org.apache.calcite.avatica.proto.Common.TypedValue;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests serialization of {@link Frame}.
+ */
+public class FrameTest {
+
+  private static final TypedValue NUMBER_VALUE = TypedValue.newBuilder().setNumberValue(1)
+      .setType(Common.Rep.LONG).build();
+
+  private void serializeAndTestEquality(Frame frame) {
+    Frame frameCopy = Frame.fromProto(frame.toProto());
+
+    assertEquals(frame.done, frameCopy.done);
+    assertEquals(frame.offset, frameCopy.offset);
+
+    Iterable<Object> origRows = frame.rows;
+    Iterable<Object> copiedRows = frameCopy.rows;
+
+    assertEquals("Expected rows to both be null, or both be non-null",
+        origRows == null, copiedRows == null);
+
+    Iterator<Object> origIter = origRows.iterator();
+    Iterator<Object> copiedIter = copiedRows.iterator();
+    while (origIter.hasNext() && copiedIter.hasNext()) {
+      Object orig = origIter.next();
+      Object copy = copiedIter.next();
+
+      assertEquals(orig == null, copy == null);
+
+      // This is goofy, but it seems like an Array comes from the JDBC implementation but then
+      // the resulting Frame has a List to support the Avatica typed Accessors
+      assertEquals(Object[].class, orig.getClass());
+      assertTrue("Expected List but got " + copy.getClass(), copy instanceof List);
+
+      @SuppressWarnings("unchecked")
+      List<Object> copyList = (List<Object>) copy;
+
+      assertArrayEquals((Object[]) orig, copyList.toArray(new Object[0]));
+    }
+
+    assertEquals(origIter.hasNext(), copiedIter.hasNext());
+  }
+
+  @Test
+  public void testEmpty() {
+    serializeAndTestEquality(Frame.EMPTY);
+  }
+
+  @Test
+  public void testSingleRow() {
+    ArrayList<Object> rows = new ArrayList<>();
+    rows.add(new Object[] {"string", Integer.MAX_VALUE, new Date().getTime()});
+
+    Frame singleRow = new Frame(0, true, rows);
+
+    serializeAndTestEquality(singleRow);
+  }
+
+  @Test
+  public void testMultipleRows() {
+    ArrayList<Object> rows = new ArrayList<>();
+    rows.add(new Object[] {"string", Integer.MAX_VALUE, new Date().getTime()});
+    rows.add(new Object[] {"gnirts", 0, Long.MIN_VALUE});
+    rows.add(new Object[] {"", null, Long.MAX_VALUE});
+
+    Frame singleRow = new Frame(0, true, rows);
+
+    serializeAndTestEquality(singleRow);
+  }
+
+  @Test public void testMalformedColumnValue() {
+    // Invalid ColumnValue: has an array and scalar
+    final ColumnValue bothAttributesColumnValue = ColumnValue.newBuilder().setHasArrayValue(true)
+        .setScalarValue(NUMBER_VALUE).build();
+    // Note omission of setScalarValue(TypedValue).
+    final ColumnValue neitherAttributeColumnValue = ColumnValue.newBuilder().setHasArrayValue(false)
+        .build();
+
+    try {
+      Frame.validateColumnValue(bothAttributesColumnValue);
+      fail("Validating the ColumnValue should have failed as it has an array and scalar");
+    } catch (IllegalArgumentException e) {
+      // Pass
+    }
+
+    try {
+      Frame.validateColumnValue(neitherAttributeColumnValue);
+      fail("Validating the ColumnValue should have failed as it has neither an array nor scalar");
+    } catch (IllegalArgumentException e) {
+      // Pass
+    }
+  }
+
+  @Test public void testColumnValueBackwardsCompatibility() {
+    // 1
+    final ColumnValue oldStyleScalarValue = ColumnValue.newBuilder().addValue(NUMBER_VALUE).build();
+    // [1, 1]
+    final ColumnValue oldStyleArrayValue = ColumnValue.newBuilder().addValue(NUMBER_VALUE)
+        .addValue(NUMBER_VALUE).build();
+
+    assertFalse(Frame.isNewStyleColumn(oldStyleScalarValue));
+    assertFalse(Frame.isNewStyleColumn(oldStyleArrayValue));
+
+    Object scalar = Frame.parseOldStyleColumn(oldStyleScalarValue);
+    assertEquals(1L, scalar);
+
+    Object array = Frame.parseOldStyleColumn(oldStyleArrayValue);
+    assertEquals(Arrays.asList(1L, 1L), array);
+  }
+
+  @Test public void testColumnValueParsing() {
+    // 1
+    final ColumnValue scalarValue = ColumnValue.newBuilder().setScalarValue(NUMBER_VALUE).build();
+    // [1, 1]
+    final ColumnValue arrayValue = ColumnValue.newBuilder().addArrayValue(NUMBER_VALUE)
+        .addArrayValue(NUMBER_VALUE).setHasArrayValue(true).build();
+
+    assertTrue(Frame.isNewStyleColumn(scalarValue));
+    assertTrue(Frame.isNewStyleColumn(arrayValue));
+
+    Object scalar = Frame.parseColumn(scalarValue);
+    assertEquals(1L, scalar);
+
+    Object array = Frame.parseColumn(arrayValue);
+    assertEquals(Arrays.asList(1L, 1L), array);
+  }
+}
+
+// End FrameTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/QueryStateTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/QueryStateTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/QueryStateTest.java
new file mode 100644
index 0000000..d97bfa2
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/QueryStateTest.java
@@ -0,0 +1,513 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica;
+
+import org.apache.calcite.avatica.remote.MetaDataOperation;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests that {@link QueryState} properly retains the necessary state to recreate
+ * a {@link ResultSet}.
+ */
+public class QueryStateTest {
+
+  private Connection conn;
+  private DatabaseMetaData metadata;
+  private Statement statement;
+
+
+  @Before
+  public void setup() throws Exception {
+    conn = Mockito.mock(Connection.class);
+    metadata = Mockito.mock(DatabaseMetaData.class);
+    statement = Mockito.mock(Statement.class);
+
+    Mockito.when(conn.getMetaData()).thenReturn(metadata);
+  }
+
+  @Test
+  public void testMetadataGetAttributes() throws Exception {
+    final String catalog = "catalog";
+    final String schemaPattern = null;
+    final String typeNamePattern = "%";
+    final String attributeNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_ATTRIBUTES, catalog, schemaPattern,
+        typeNamePattern, attributeNamePattern);
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getAttributes(catalog, schemaPattern, typeNamePattern,
+        attributeNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetBestRowIdentifier() throws Exception {
+    final String catalog = "catalog";
+    final String schema = null;
+    final String table = "table";
+    final int scope = 1;
+    final boolean nullable = true;
+
+    QueryState state = new QueryState(MetaDataOperation.GET_BEST_ROW_IDENTIFIER, new Object[] {
+      catalog,
+      schema,
+      table,
+      scope,
+      nullable
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getBestRowIdentifier(catalog, schema, table, scope, nullable);
+  }
+
+  @Test
+  public void testMetadataGetCatalogs() throws Exception {
+    QueryState state = new QueryState(MetaDataOperation.GET_CATALOGS, new Object[0]);
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getCatalogs();
+  }
+
+  @Test
+  public void testMetadataGetColumnPrivileges() throws Exception {
+    final String catalog = null;
+    final String schema = "schema";
+    final String table = "table";
+    final String columnNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_COLUMN_PRIVILEGES, new Object[] {
+      catalog,
+      schema,
+      table,
+      columnNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getColumnPrivileges(catalog, schema, table, columnNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetColumns() throws Exception {
+    final String catalog = null;
+    final String schemaPattern = "%";
+    final String tableNamePattern = "%";
+    final String columnNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_COLUMNS, new Object[] {
+      catalog,
+      schemaPattern,
+      tableNamePattern,
+      columnNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getColumns(catalog, schemaPattern, tableNamePattern,
+        columnNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetCrossReference() throws Exception {
+    final String parentCatalog = null;
+    final String parentSchema = null;
+    final String parentTable = "%";
+    final String foreignCatalog = null;
+    final String foreignSchema = null;
+    final String foreignTable = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_CROSS_REFERENCE, new Object[] {
+      parentCatalog,
+      parentSchema,
+      parentTable,
+      foreignCatalog,
+      foreignSchema,
+      foreignTable
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getCrossReference(parentCatalog, parentSchema, parentTable,
+        foreignCatalog, foreignSchema, foreignTable);
+  }
+
+  @Test
+  public void testMetadataGetExportedKeys() throws Exception {
+    final String catalog = "";
+    final String schema = null;
+    final String table = "mytable";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_EXPORTED_KEYS, new Object[] {
+      catalog,
+      schema,
+      table
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getExportedKeys(catalog, schema, table);
+  }
+
+  @Test
+  public void testMetadataGetFunctionColumns() throws Exception {
+    final String catalog = null;
+    final String schemaPattern = "%";
+    final String functionNamePattern = "%";
+    final String columnNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_FUNCTION_COLUMNS, new Object[] {
+      catalog,
+      schemaPattern,
+      functionNamePattern,
+      columnNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getFunctionColumns(catalog, schemaPattern, functionNamePattern,
+        columnNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetFunctions() throws Exception {
+    final String catalog = null;
+    final String schemaPattern = "%";
+    final String functionNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_FUNCTIONS, new Object[] {
+      catalog,
+      schemaPattern,
+      functionNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getFunctions(catalog, schemaPattern, functionNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetImportedKeys() throws Exception {
+    final String catalog = "";
+    final String schema = null;
+    final String table = "my_table";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_IMPORTED_KEYS, new Object[] {
+      catalog,
+      schema,
+      table
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getImportedKeys(catalog, schema, table);
+  }
+
+  @Test
+  public void testMetadataGetIndexInfo() throws Exception {
+    final String catalog = "";
+    final String schema = null;
+    final String table = "my_table";
+    final boolean unique = true;
+    final boolean approximate = true;
+
+    QueryState state = new QueryState(MetaDataOperation.GET_INDEX_INFO, new Object[] {
+      catalog,
+      schema,
+      table,
+      unique,
+      approximate
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getIndexInfo(catalog, schema, table, unique, approximate);
+  }
+
+  @Test
+  public void testMetadataGetPrimaryKeys() throws Exception {
+    final String catalog = "";
+    final String schema = null;
+    final String table = "my_table";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_PRIMARY_KEYS, new Object[] {
+      catalog,
+      schema,
+      table
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getPrimaryKeys(catalog, schema, table);
+  }
+
+  @Test
+  public void testMetadataGetProcedureColumns() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String procedureNamePattern = "%";
+    final String columnNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_PROCEDURE_COLUMNS, new Object[] {
+      catalog,
+      schemaPattern,
+      procedureNamePattern,
+      columnNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getProcedureColumns(catalog, schemaPattern, procedureNamePattern,
+        columnNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetProcedures() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String procedureNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_PROCEDURES, new Object[] {
+      catalog,
+      schemaPattern,
+      procedureNamePattern,
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getProcedures(catalog, schemaPattern, procedureNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetPseudoColumns() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String tableNamePattern = "%";
+    final String columnNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_PSEUDO_COLUMNS, new Object[] {
+      catalog,
+      schemaPattern,
+      tableNamePattern,
+      columnNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getPseudoColumns(catalog, schemaPattern, tableNamePattern,
+        columnNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetSchemas() throws Exception {
+    QueryState state = new QueryState(MetaDataOperation.GET_SCHEMAS, new Object[0]);
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getSchemas();
+  }
+
+  @Test
+  public void testMetadataGetSchemasWithArgs() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+
+    QueryState state = new QueryState(MetaDataOperation.GET_SCHEMAS_WITH_ARGS, new Object[] {
+      catalog,
+      schemaPattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getSchemas(catalog, schemaPattern);
+  }
+
+  @Test
+  public void testMetadataGetSuperTables() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String tableNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_SUPER_TABLES, new Object[] {
+      catalog,
+      schemaPattern,
+      tableNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getSuperTables(catalog, schemaPattern, tableNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetSuperTypes() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String tableNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_SUPER_TYPES, new Object[] {
+      catalog,
+      schemaPattern,
+      tableNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getSuperTypes(catalog, schemaPattern, tableNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetTablePrivileges() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String tableNamePattern = "%";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_TABLE_PRIVILEGES, new Object[] {
+      catalog,
+      schemaPattern,
+      tableNamePattern
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getTablePrivileges(catalog, schemaPattern, tableNamePattern);
+  }
+
+  @Test
+  public void testMetadataGetTables() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String tableNamePattern = "%";
+    final String[] types = new String[] {"VIEW", "TABLE"};
+
+    QueryState state = new QueryState(MetaDataOperation.GET_TABLES, new Object[] {
+      catalog,
+      schemaPattern,
+      tableNamePattern,
+      types
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getTables(catalog, schemaPattern, tableNamePattern, types);
+  }
+
+  @Test
+  public void testMetadataGetTableTypes() throws Exception {
+    QueryState state = new QueryState(MetaDataOperation.GET_TABLE_TYPES, new Object[0]);
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getTableTypes();
+  }
+
+  @Test
+  public void testMetadataGetTypeInfo() throws Exception {
+    QueryState state = new QueryState(MetaDataOperation.GET_TYPE_INFO, new Object[0]);
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getTypeInfo();
+  }
+
+  @Test
+  public void testMetadataGetUDTs() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String typeNamePattern = "%";
+    final int[] types = new int[] {1, 2};
+
+    QueryState state = new QueryState(MetaDataOperation.GET_UDTS, new Object[] {
+      catalog,
+      schemaPattern,
+      typeNamePattern,
+      types
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getUDTs(catalog, schemaPattern, typeNamePattern, types);
+  }
+
+  @Test
+  public void testMetadataGetVersionColumns() throws Exception {
+    final String catalog = "";
+    final String schemaPattern = null;
+    final String table = "my_table";
+
+    QueryState state = new QueryState(MetaDataOperation.GET_VERSION_COLUMNS, new Object[] {
+      catalog,
+      schemaPattern,
+      table
+    });
+
+    state.invoke(conn, statement);
+
+    Mockito.verify(metadata).getVersionColumns(catalog, schemaPattern, table);
+  }
+
+  @Test
+  public void testSerialization() throws Exception {
+    final String catalog = "catalog";
+    final String schema = null;
+    final String table = "table";
+    final int scope = 1;
+    final boolean nullable = true;
+
+    QueryState state = new QueryState(MetaDataOperation.GET_BEST_ROW_IDENTIFIER, new Object[] {
+      catalog,
+      schema,
+      table,
+      scope,
+      nullable
+    });
+
+    assertEquals(state, QueryState.fromProto(state.toProto()));
+
+    final String schemaPattern = null;
+    final String typeNamePattern = "%";
+    final int[] types = new int[] {1, 2};
+
+    state = new QueryState(MetaDataOperation.GET_UDTS, new Object[] {
+      catalog,
+      schemaPattern,
+      typeNamePattern,
+      types
+    });
+
+    assertEquals(state, QueryState.fromProto(state.toProto()));
+
+    state = new QueryState("SELECT * FROM foo");
+
+    assertEquals(state, QueryState.fromProto(state.toProto()));
+  }
+
+}
+
+// End QueryStateTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java
new file mode 100644
index 0000000..c85312d
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica.metrics;
+
+import org.apache.calcite.avatica.remote.MetricsHelper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test class for {@link MetricsHelper}.
+ */
+public class MetricsHelperTest {
+
+  @Test(expected = NullPointerException.class) public void testNullConcat() {
+    MetricsHelper.concat(null, "foo");
+  }
+
+  @Test public void testConcat() {
+    String suffix = "suffix";
+    String finalName = getClass().getName() + "." + suffix;
+    assertEquals(finalName, MetricsHelper.concat(getClass(), suffix));
+  }
+
+}
+
+// End MetricsHelperTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AbstractHandlerTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AbstractHandlerTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AbstractHandlerTest.java
new file mode 100644
index 0000000..012cccc
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AbstractHandlerTest.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica.remote;
+
+import org.apache.calcite.avatica.AvaticaSeverity;
+import org.apache.calcite.avatica.remote.Handler.HandlerResponse;
+import org.apache.calcite.avatica.remote.Service.ErrorResponse;
+import org.apache.calcite.avatica.remote.Service.Request;
+import org.apache.calcite.avatica.remote.Service.Response;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Objects;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test for common functionality across {@link Handler} implementations.
+ */
+public class AbstractHandlerTest {
+
+  private String exceptionToString(Exception e) {
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    Objects.requireNonNull(e).printStackTrace(pw);
+    return sw.toString();
+  }
+
+  @Test public void testExceptionUnwrappingWithoutContext() {
+    @SuppressWarnings("unchecked")
+    AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class);
+
+    Mockito.when(handler.unwrapException(Mockito.any(Exception.class))).thenCallRealMethod();
+
+    Exception e = new RuntimeException();
+    Response resp = handler.unwrapException(e);
+    assertTrue("Response should be ErrorResponse, but was " + resp.getClass(),
+        resp instanceof ErrorResponse);
+    ErrorResponse errorResp = (ErrorResponse) resp;
+    assertEquals(ErrorResponse.UNKNOWN_ERROR_CODE, errorResp.errorCode);
+    assertEquals(AvaticaSeverity.UNKNOWN, errorResp.severity);
+    assertEquals(Arrays.asList(exceptionToString(e)), errorResp.exceptions);
+
+    e = new AvaticaRuntimeException();
+    resp = handler.unwrapException(e);
+    assertTrue("Response should be ErrorResponse, but was " + resp.getClass(),
+        resp instanceof ErrorResponse);
+    errorResp = (ErrorResponse) resp;
+    assertEquals(ErrorResponse.UNKNOWN_ERROR_CODE, errorResp.errorCode);
+    assertEquals(AvaticaSeverity.UNKNOWN, errorResp.severity);
+    assertEquals(Arrays.asList(exceptionToString(e)), errorResp.exceptions);
+  }
+
+  @Test public void testExceptionUnwrappingWithContext() {
+    @SuppressWarnings("unchecked")
+    AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class);
+
+    Mockito.when(handler.unwrapException(Mockito.any(Exception.class))).thenCallRealMethod();
+
+    final String msg = "Something failed!";
+    AvaticaRuntimeException e = new AvaticaRuntimeException(msg,
+        ErrorResponse.UNKNOWN_ERROR_CODE, ErrorResponse.UNKNOWN_SQL_STATE, AvaticaSeverity.FATAL);
+    Response resp = handler.unwrapException(e);
+    assertTrue("Response should be ErrorResponse, but was " + resp.getClass(),
+        resp instanceof ErrorResponse);
+    ErrorResponse errorResp = (ErrorResponse) resp;
+    assertEquals(ErrorResponse.UNKNOWN_ERROR_CODE, errorResp.errorCode);
+    assertEquals(AvaticaSeverity.FATAL, errorResp.severity);
+    assertEquals(Arrays.asList(exceptionToString(e)), errorResp.exceptions);
+    assertEquals(msg, errorResp.errorMessage);
+  }
+
+  @Test public void testFailedResponseSerialization() throws IOException {
+    @SuppressWarnings("unchecked")
+    final AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class);
+    final Request request = Mockito.mock(Request.class);
+    final Response response = Mockito.mock(Response.class);
+    final IOException exception = new IOException();
+    final ErrorResponse errorResponse = Mockito.mock(ErrorResponse.class);
+    final String serializedErrorResponse = "An ErrorResponse";
+
+    // Accept a serialized request
+    Mockito.when(handler.apply(Mockito.anyString())).thenCallRealMethod();
+    // Deserialize it back into a POJO
+    Mockito.when(handler.decode(Mockito.anyString())).thenReturn(request);
+    // Construct the Response for that Request
+    Mockito.when(request.accept(Mockito.any(Service.class))).thenReturn(response);
+    // Throw an IOException when serializing the Response.
+    Mockito.when(handler.encode(response)).thenThrow(exception);
+    // Convert the IOException into an ErrorResponse
+    Mockito.when(handler.unwrapException(exception)).thenReturn(errorResponse);
+    Mockito.when(handler.encode(errorResponse)).thenReturn(serializedErrorResponse);
+
+    HandlerResponse<String> handlerResp = handler.apply("this is mocked out");
+    assertEquals(500, handlerResp.getStatusCode());
+    assertEquals(serializedErrorResponse, handlerResp.getResponse());
+  }
+
+  @Test public void testFailedErrorResponseSerialization() throws IOException {
+    @SuppressWarnings("unchecked")
+    final AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class);
+    final Request request = Mockito.mock(Request.class);
+    final Response response = Mockito.mock(Response.class);
+    final IOException exception = new IOException();
+    final ErrorResponse errorResponse = Mockito.mock(ErrorResponse.class);
+
+    // Accept a serialized request
+    Mockito.when(handler.apply(Mockito.anyString())).thenCallRealMethod();
+    // Deserialize it back into a POJO
+    Mockito.when(handler.decode(Mockito.anyString())).thenReturn(request);
+    // Construct the Response for that Request
+    Mockito.when(request.accept(Mockito.any(Service.class))).thenReturn(response);
+    // Throw an IOException when serializing the Response.
+    Mockito.when(handler.encode(response)).thenThrow(exception);
+    // Convert the IOException into an ErrorResponse
+    Mockito.when(handler.unwrapException(exception)).thenReturn(errorResponse);
+    // Fail to serialize the ErrorResponse
+    Mockito.when(handler.encode(errorResponse)).thenThrow(exception);
+
+    try {
+      handler.apply("this is mocked out");
+    } catch (RuntimeException e) {
+      assertEquals(exception, e.getCause());
+    }
+  }
+
+  @Test public void testFailedRequestDeserialization() throws IOException {
+    @SuppressWarnings("unchecked")
+    final AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class);
+    final IOException exception = new IOException();
+
+    // Accept a serialized request
+    Mockito.when(handler.apply(Mockito.anyString())).thenCallRealMethod();
+    // Throw an Exception trying to convert it back into a POJO
+    Mockito.when(handler.decode(Mockito.anyString())).thenThrow(exception);
+
+    try {
+      handler.apply("this is mocked out");
+    } catch (RuntimeException e) {
+      assertEquals(exception, e.getCause());
+    }
+  }
+}
+
+// End AbstractHandlerTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java
new file mode 100644
index 0000000..cd64329
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica.remote;
+
+import org.apache.calcite.avatica.BuiltInConnectionProperty;
+import org.apache.calcite.avatica.ConnectionConfig;
+import org.apache.calcite.avatica.ConnectionConfigImpl;
+
+import org.junit.Test;
+
+import java.net.URL;
+import java.util.Properties;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the factory that creates http clients.
+ */
+public class AvaticaHttpClientFactoryTest {
+
+  @Test public void testDefaultHttpClient() throws Exception {
+    Properties props = new Properties();
+    URL url = new URL("http://localhost:8765");
+    ConnectionConfig config = new ConnectionConfigImpl(props);
+    AvaticaHttpClientFactory httpClientFactory = new AvaticaHttpClientFactoryImpl();
+
+    AvaticaHttpClient client = httpClientFactory.getClient(url, config);
+    assertTrue("Client was an instance of " + client.getClass(),
+        client instanceof AvaticaCommonsHttpClientImpl);
+  }
+
+  @Test public void testOverridenHttpClient() throws Exception {
+    Properties props = new Properties();
+    props.setProperty(BuiltInConnectionProperty.HTTP_CLIENT_IMPL.name(),
+        AvaticaHttpClientImpl.class.getName());
+    URL url = new URL("http://localhost:8765");
+    ConnectionConfig config = new ConnectionConfigImpl(props);
+    AvaticaHttpClientFactory httpClientFactory = new AvaticaHttpClientFactoryImpl();
+
+    AvaticaHttpClient client = httpClientFactory.getClient(url, config);
+    assertTrue("Client was an instance of " + client.getClass(),
+        client instanceof AvaticaHttpClientImpl);
+  }
+}
+
+// End AvaticaHttpClientFactoryTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientTest.java
new file mode 100644
index 0000000..33369d2
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica.remote;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertArrayEquals;
+
+/**
+ * Tests for the HTTP transport.
+ */
+public class AvaticaHttpClientTest {
+  private static final String REQUEST =
+      "{\"request\":\"createStatement\",\"connectionId\":\"8f3f28ee-d0bb-4cdb-a4b1-8f6e8476c534\"}";
+  private static final String RESPONSE =
+      "{\"response\":\"createStatement\",\"connectionId\":"
+          + "\"8f3f28ee-d0bb-4cdb-a4b1-8f6e8476c534\",\"statementId\":1608176856}";
+
+  @Test
+  public void testRetryOnUnavailable() throws Exception {
+    // HTTP-503, try again
+    URL url = new URL("http://127.0.0.1:8765");
+    final HttpURLConnection cnxn = Mockito.mock(HttpURLConnection.class);
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    ByteArrayInputStream bais = new ByteArrayInputStream(RESPONSE.getBytes(StandardCharsets.UTF_8));
+
+    // Create the HTTP client
+    AvaticaHttpClientImpl client = new AvaticaHttpClientImpl(url) {
+      @Override HttpURLConnection openConnection() throws IOException {
+        return cnxn;
+      }
+    };
+
+    // HTTP 503 then 200
+    Mockito.when(cnxn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_UNAVAILABLE,
+        HttpURLConnection.HTTP_OK);
+
+    Mockito.when(cnxn.getOutputStream()).thenReturn(baos);
+    Mockito.when(cnxn.getInputStream()).thenReturn(bais);
+
+    byte[] response = client.send(REQUEST.getBytes(StandardCharsets.UTF_8));
+
+    assertArrayEquals(RESPONSE.getBytes(StandardCharsets.UTF_8), response);
+  }
+
+  @Test(expected = RuntimeException.class)
+  public void testServerError() throws Exception {
+    // HTTP 500 should error out
+    URL url = new URL("http://127.0.0.1:8765");
+    final HttpURLConnection cnxn = Mockito.mock(HttpURLConnection.class);
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+    // Create the HTTP client
+    AvaticaHttpClientImpl client = new AvaticaHttpClientImpl(url) {
+      @Override HttpURLConnection openConnection() throws IOException {
+        return cnxn;
+      }
+    };
+
+    // HTTP 500
+    Mockito.when(cnxn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_INTERNAL_ERROR);
+
+    Mockito.when(cnxn.getOutputStream()).thenReturn(baos);
+
+    // Should throw an RTE
+    client.send(REQUEST.getBytes(StandardCharsets.UTF_8));
+  }
+
+}
+
+// End AvaticaHttpClientTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java
new file mode 100644
index 0000000..f66cb26
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica.remote;
+
+import org.apache.calcite.avatica.AvaticaClientRuntimeException;
+import org.apache.calcite.avatica.AvaticaSeverity;
+import org.apache.calcite.avatica.remote.Service.ErrorResponse;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * A test class for ErrorResponse.
+ */
+public class ErrorResponseTest {
+
+  @Test public void testEquality() {
+    final String message = "There was an error";
+    final int code = 23;
+    final String state = "a1b2c";
+    final AvaticaSeverity severity = AvaticaSeverity.ERROR;
+    final List<String> exceptions = Arrays.asList("Server Stacktrace 1", "Server Stacktace 2");
+    final RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
+    assertEquals(new ErrorResponse(message, code, state, severity, exceptions, metadata),
+        new ErrorResponse(message, code, state, severity, exceptions, metadata));
+  }
+
+  @Test public void testToClientRTE() {
+    final String message = "There was an error";
+    final int code = 23;
+    final String state = "a1b2c";
+    final AvaticaSeverity severity = AvaticaSeverity.ERROR;
+    final List<String> exceptions = Arrays.asList("Server Stacktrace 1", "Server Stacktace 2");
+    final RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
+    final ErrorResponse resp = new ErrorResponse(message, code, state, severity, exceptions,
+        metadata);
+    AvaticaClientRuntimeException exception = resp.toException();
+    assertTrue("Expected error message to end with '" + resp.errorMessage + "', but was '"
+        + exception.getMessage() + "'", exception.getMessage().endsWith(resp.errorMessage));
+    assertEquals(resp.errorCode, exception.getErrorCode());
+    assertEquals(resp.severity, exception.getSeverity());
+    assertEquals(resp.sqlState, exception.getSqlState());
+    assertEquals(resp.exceptions, exception.getServerExceptions());
+    assertEquals(resp.rpcMetadata, exception.getRpcMetadata());
+  }
+}
+
+// End ErrorResponseTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/MetaDataOperationTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/MetaDataOperationTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/MetaDataOperationTest.java
new file mode 100644
index 0000000..c64b32c
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/MetaDataOperationTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica.remote;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for {@link MetaDataOperation}
+ */
+public class MetaDataOperationTest {
+
+  @Test
+  public void testProtobufSerialization() {
+    for (MetaDataOperation metadataOp : MetaDataOperation.values()) {
+      assertEquals(metadataOp, MetaDataOperation.fromProto(metadataOp.toProto()));
+    }
+  }
+
+}
+
+// End MetaDataOperationTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
new file mode 100644
index 0000000..afb15c3
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.
+ */
+package org.apache.calcite.avatica.remote;
+
+import org.apache.calcite.avatica.Meta;
+import org.apache.calcite.avatica.Meta.Frame;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
+import org.apache.calcite.avatica.proto.Common;
+import org.apache.calcite.avatica.proto.Common.ColumnValue;
+import org.apache.calcite.avatica.proto.Requests;
+import org.apache.calcite.avatica.proto.Responses;
+import org.apache.calcite.avatica.remote.Handler.HandlerResponse;
+import org.apache.calcite.avatica.remote.Service.FetchRequest;
+import org.apache.calcite.avatica.remote.Service.FetchResponse;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test basic serialization of objects with protocol buffers.
+ */
+public class ProtobufHandlerTest {
+
+  // Mocks
+  private Service service;
+  private ProtobufTranslation translation;
+
+  // Real objects
+  private ProtobufHandler handler;
+
+  @Before
+  public void setupMocks() {
+    // Mocks
+    service = Mockito.mock(Service.class);
+    translation = Mockito.mock(ProtobufTranslation.class);
+
+    // Real objects
+    handler = new ProtobufHandler(service, translation, NoopMetricsSystem.getInstance());
+  }
+
+  @Test
+  public void testFetch() throws Exception {
+    final String connectionId = "cnxn1";
+    final int statementId = 30;
+    final long offset = 10;
+    final int fetchMaxRowCount = 100;
+    final List<Common.TypedValue> values = new ArrayList<>();
+
+    values.add(Common.TypedValue.newBuilder().setType(Common.Rep.BOOLEAN).setBoolValue(true)
+        .build());
+    values.add(Common.TypedValue.newBuilder().setType(Common.Rep.STRING)
+        .setStringValue("my_string").build());
+
+    Requests.FetchRequest protoRequest = Requests.FetchRequest.newBuilder()
+        .setConnectionId(connectionId).setStatementId(statementId)
+        .setOffset(offset).setFetchMaxRowCount(fetchMaxRowCount)
+        .build();
+    byte[] serializedRequest = protoRequest.toByteArray();
+
+    FetchRequest request = new FetchRequest().deserialize(protoRequest);
+
+    List<Object> frameRows = new ArrayList<>();
+    frameRows.add(new Object[] {true, "my_string"});
+
+    Meta.Frame frame = Frame.create(0, true, frameRows);
+    RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
+    FetchResponse response = new FetchResponse(frame, false, false, metadata);
+
+    when(translation.parseRequest(serializedRequest)).thenReturn(request);
+    when(service.apply(request)).thenReturn(response);
+    when(translation.serializeResponse(response))
+        .thenReturn(response.serialize().toByteArray());
+
+    HandlerResponse<byte[]> handlerResponse = handler.apply(serializedRequest);
+    byte[] serializedResponse = handlerResponse.getResponse();
+    assertEquals(200, handlerResponse.getStatusCode());
+
+    Responses.FetchResponse protoResponse = Responses.FetchResponse.parseFrom(serializedResponse);
+
+    Common.Frame protoFrame = protoResponse.getFrame();
+
+    assertEquals(frame.offset, protoFrame.getOffset());
+    assertEquals(frame.done, protoFrame.getDone());
+
+    List<Common.Row> rows = protoFrame.getRowsList();
+    assertEquals(1, rows.size());
+    Common.Row row = rows.get(0);
+    List<Common.ColumnValue> columnValues = row.getValueList();
+    assertEquals(2, columnValues.size());
+
+    Iterator<Common.ColumnValue> iter = columnValues.iterator();
+    assertTrue(iter.hasNext());
+    Common.ColumnValue column = iter.next();
+    assertTrue("The Column should have contained a scalar: " + column,
+        column.hasField(ColumnValue.getDescriptor()
+            .findFieldByNumber(ColumnValue.SCALAR_VALUE_FIELD_NUMBER)));
+
+    Common.TypedValue value = column.getScalarValue();
+    assertEquals(Common.Rep.BOOLEAN, value.getType());
+    assertEquals(true, value.getBoolValue());
+
+    assertTrue(iter.hasNext());
+    column = iter.next();
+    assertTrue("The Column should have contained a scalar: " + column,
+        column.hasField(ColumnValue.getDescriptor()
+            .findFieldByNumber(ColumnValue.SCALAR_VALUE_FIELD_NUMBER)));
+    value = column.getScalarValue();
+    assertEquals(Common.Rep.STRING, value.getType());
+    assertEquals("my_string", value.getStringValue());
+  }
+
+}
+
+// End ProtobufHandlerTest.java