You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/09/17 02:10:48 UTC

[2/8] incubator-calcite git commit: [CALCITE-840] Protocol buffer serialization over HTTP for Avatica Server (Josh Elser)

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cb7c213c/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
index 1171d76..e78aa2e 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
@@ -17,15 +17,24 @@
 package org.apache.calcite.avatica.remote;
 
 import org.apache.calcite.avatica.AvaticaConnection;
+import org.apache.calcite.avatica.ConnectionPropertiesImpl;
 import org.apache.calcite.avatica.Meta;
+import org.apache.calcite.avatica.proto.Common;
+import org.apache.calcite.avatica.proto.Requests;
+import org.apache.calcite.avatica.proto.Responses;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Message;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * API for request-response calls to an Avatica server.
@@ -77,6 +86,8 @@ public interface Service {
       @JsonSubTypes.Type(value = DatabasePropertyRequest.class, name = "databaseProperties") })
   abstract class Request {
     abstract Response accept(Service service);
+    abstract Request deserialize(Message genericMsg);
+    abstract Message serialize();
   }
 
   /** Base class for all service response messages. */
@@ -97,6 +108,8 @@ public interface Service {
       @JsonSubTypes.Type(value = ConnectionSyncResponse.class, name = "connectionSync"),
       @JsonSubTypes.Type(value = DatabasePropertyResponse.class, name = "databaseProperties") })
   abstract class Response {
+    abstract Response deserialize(Message genericMsg);
+    abstract Message serialize();
   }
 
   /** Request for
@@ -105,6 +118,31 @@ public interface Service {
     ResultSetResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override CatalogsRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.CatalogsRequest)) {
+        throw new IllegalArgumentException(
+            "Expected CatalogsRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      // No state to set
+      return new CatalogsRequest();
+    }
+
+    @Override Requests.CatalogsRequest serialize() {
+      return Requests.CatalogsRequest.newBuilder().build();
+    }
+
+    @Override public int hashCode() {
+      return 0;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      return o instanceof CatalogsRequest;
+    }
   }
 
   /** Request for
@@ -117,14 +155,42 @@ public interface Service {
     DatabasePropertyResponse accept(Service service) {
       return service.apply(this);
     }
-  }
 
+    @Override DatabasePropertyRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.DatabasePropertyRequest)) {
+        throw new IllegalArgumentException(
+            "Expected DatabasePropertyRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      return new DatabasePropertyRequest();
+    }
+
+    @Override Requests.DatabasePropertyRequest serialize() {
+      return Requests.DatabasePropertyRequest.newBuilder().build();
+    }
+
+    @Override public int hashCode() {
+      return 0;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      return o instanceof DatabasePropertyRequest;
+    }
+  }
   /** Request for
    * {@link Meta#getSchemas(String, org.apache.calcite.avatica.Meta.Pat)}. */
   class SchemasRequest extends Request {
     public final String catalog;
     public final String schemaPattern;
 
+    SchemasRequest() {
+      catalog = null;
+      schemaPattern = null;
+    }
+
     @JsonCreator
     public SchemasRequest(@JsonProperty("catalog") String catalog,
         @JsonProperty("schemaPattern") String schemaPattern) {
@@ -135,6 +201,80 @@ public interface Service {
     ResultSetResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override SchemasRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.SchemasRequest)) {
+        throw new IllegalArgumentException(
+            "Expected SchemasRequest, but got" + genericMsg.getClass().getName());
+      }
+
+      final Requests.SchemasRequest msg = (Requests.SchemasRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String catalog = null;
+      if (ProtobufService.hasField(msg, desc, Requests.SchemasRequest.CATALOG_FIELD_NUMBER)) {
+        catalog = msg.getCatalog();
+      }
+
+      String schemaPattern = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.SchemasRequest.SCHEMA_PATTERN_FIELD_NUMBER)) {
+        schemaPattern = msg.getSchemaPattern();
+      }
+
+      return new SchemasRequest(catalog, schemaPattern);
+    }
+
+    @Override Requests.SchemasRequest serialize() {
+      Requests.SchemasRequest.Builder builder = Requests.SchemasRequest.newBuilder();
+      if (null != catalog) {
+        builder.setCatalog(catalog);
+      }
+      if (null != schemaPattern) {
+        builder.setSchemaPattern(schemaPattern);
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((catalog == null) ? 0 : catalog.hashCode());
+      result = prime * result + ((schemaPattern == null) ? 0 : schemaPattern.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof SchemasRequest) {
+        SchemasRequest other = (SchemasRequest) o;
+
+        if (null == catalog) {
+          // We're null, other is not
+          if (null != other.catalog) {
+            return false;
+          }
+        } else if (!catalog.equals(other.catalog)) {
+          return false;
+        }
+
+        if (null == schemaPattern) {
+          // We're null, they're not
+          if (null != other.schemaPattern) {
+            return false;
+          }
+        } else if (!catalog.equals(other.catalog)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Request for
@@ -146,6 +286,13 @@ public interface Service {
     public final String tableNamePattern;
     public final List<String> typeList;
 
+    TablesRequest() {
+      catalog = null;
+      schemaPattern = null;
+      tableNamePattern = null;
+      typeList = null;
+    }
+
     @JsonCreator
     public TablesRequest(@JsonProperty("catalog") String catalog,
         @JsonProperty("schemaPattern") String schemaPattern,
@@ -160,14 +307,141 @@ public interface Service {
     @Override Response accept(Service service) {
       return service.apply(this);
     }
+
+    @Override Request deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.TablesRequest)) {
+        throw new IllegalArgumentException(
+            "Expected TablesRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      final Requests.TablesRequest msg = (Requests.TablesRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String catalog = null;
+      if (ProtobufService.hasField(msg, desc, Requests.TablesRequest.CATALOG_FIELD_NUMBER)) {
+        catalog = msg.getCatalog();
+      }
+
+      String schemaPattern = null;
+      if (ProtobufService.hasField(msg, desc, Requests.TablesRequest.SCHEMA_PATTERN_FIELD_NUMBER)) {
+        schemaPattern = msg.getSchemaPattern();
+      }
+
+      String tableNamePattern = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.TablesRequest.TABLE_NAME_PATTERN_FIELD_NUMBER)) {
+        tableNamePattern = msg.getTableNamePattern();
+      }
+
+      return new TablesRequest(catalog, schemaPattern, tableNamePattern, msg.getTypeListList());
+    }
+
+    @Override Requests.TablesRequest serialize() {
+      Requests.TablesRequest.Builder builder = Requests.TablesRequest.newBuilder();
+
+      if (null != catalog) {
+        builder.setCatalog(catalog);
+      }
+      if (null != schemaPattern) {
+        builder.setSchemaPattern(schemaPattern);
+      }
+      if (null != tableNamePattern) {
+        builder.setTableNamePattern(tableNamePattern);
+      }
+      if (null != typeList) {
+        builder.addAllTypeList(typeList);
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((catalog == null) ? 0 : catalog.hashCode());
+      result = prime * result + ((schemaPattern == null) ? 0 : schemaPattern.hashCode());
+      result = prime * result + ((tableNamePattern == null) ? 0 : tableNamePattern.hashCode());
+      result = prime * result + ((typeList == null) ? 0 : typeList.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof TablesRequest) {
+        TablesRequest other = (TablesRequest) o;
+
+        if (null == catalog) {
+          if (null != catalog) {
+            return false;
+          }
+        } else if (!catalog.equals(other.catalog)) {
+          return false;
+        }
+
+        if (null == schemaPattern) {
+          if (null != other.schemaPattern) {
+            return false;
+          }
+        } else if (!schemaPattern.equals(other.schemaPattern)) {
+          return false;
+        }
+
+        if (null == tableNamePattern) {
+          if (null != other.tableNamePattern) {
+            return false;
+          }
+        } else if (!tableNamePattern.equals(other.tableNamePattern)) {
+          return false;
+        }
+
+        if (null == typeList) {
+          if (null != other.typeList) {
+            return false;
+          }
+        } else if (null == other.typeList || !typeList.equals(other.typeList)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
-  /** Request for
-   * {@link Meta#getTableTypes()}. */
+  /**
+   * Request for {@link Meta#getTableTypes()}.
+   */
   class TableTypesRequest extends Request {
     @Override ResultSetResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override TableTypesRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.TableTypesRequest)) {
+        throw new IllegalArgumentException(
+            "Expected TableTypesRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      return new TableTypesRequest();
+    }
+
+    @Override Requests.TableTypesRequest serialize() {
+      return Requests.TableTypesRequest.newBuilder().build();
+    }
+
+    @Override public int hashCode() {
+      return 0;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      return o instanceof TableTypesRequest;
+    }
   }
 
   /** Request for
@@ -179,6 +453,13 @@ public interface Service {
     public final String tableNamePattern;
     public final String columnNamePattern;
 
+    ColumnsRequest() {
+      catalog = null;
+      schemaPattern = null;
+      tableNamePattern = null;
+      columnNamePattern = null;
+    }
+
     @JsonCreator
     public ColumnsRequest(@JsonProperty("catalog") String catalog,
         @JsonProperty("schemaPattern") String schemaPattern,
@@ -193,6 +474,115 @@ public interface Service {
     ResultSetResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override ColumnsRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.ColumnsRequest)) {
+        throw new IllegalArgumentException(
+            "Expected ColumnsRequest, but got" + genericMsg.getClass().getName());
+      }
+
+      final Requests.ColumnsRequest msg = (Requests.ColumnsRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String catalog = null;
+      if (ProtobufService.hasField(msg, desc, Requests.ColumnsRequest.CATALOG_FIELD_NUMBER)) {
+        catalog = msg.getCatalog();
+      }
+
+      String schemaPattern = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.ColumnsRequest.SCHEMA_PATTERN_FIELD_NUMBER)) {
+        schemaPattern = msg.getSchemaPattern();
+      }
+
+      String tableNamePattern = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.ColumnsRequest.TABLE_NAME_PATTERN_FIELD_NUMBER)) {
+        tableNamePattern = msg.getTableNamePattern();
+      }
+
+      String columnNamePattern = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.ColumnsRequest.COLUMN_NAME_PATTERN_FIELD_NUMBER)) {
+        columnNamePattern = msg.getColumnNamePattern();
+      }
+
+      return new ColumnsRequest(catalog, schemaPattern, tableNamePattern, columnNamePattern);
+    }
+
+    @Override Requests.ColumnsRequest serialize() {
+      Requests.ColumnsRequest.Builder builder = Requests.ColumnsRequest.newBuilder();
+
+      if (null != catalog) {
+        builder.setCatalog(catalog);
+      }
+      if (null != schemaPattern) {
+        builder.setSchemaPattern(schemaPattern);
+      }
+      if (null != tableNamePattern) {
+        builder.setTableNamePattern(tableNamePattern);
+      }
+      if (null != columnNamePattern) {
+        builder.setColumnNamePattern(columnNamePattern);
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((catalog == null) ? 0 : catalog.hashCode());
+      result = prime * result + ((columnNamePattern == null) ? 0 : columnNamePattern.hashCode());
+      result = prime * result + ((schemaPattern == null) ? 0 : schemaPattern.hashCode());
+      result = prime * result + ((tableNamePattern == null) ? 0 : tableNamePattern.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof ColumnsRequest) {
+        ColumnsRequest other = (ColumnsRequest) o;
+
+        if (null == catalog) {
+          if (null != other.catalog) {
+            return false;
+          }
+        } else if (!catalog.equals(other.catalog)) {
+          return false;
+        }
+
+        if (null == schemaPattern) {
+          if (null != other.schemaPattern) {
+            return false;
+          }
+        } else if (!schemaPattern.equals(other.schemaPattern)) {
+          return false;
+        }
+
+        if (null == tableNamePattern) {
+          if (null != other.tableNamePattern) {
+            return false;
+          }
+        } else if (!tableNamePattern.equals(other.tableNamePattern)) {
+          return false;
+        }
+
+        if (null == columnNamePattern) {
+          if (null != other.columnNamePattern) {
+            return false;
+          }
+        } else if (!columnNamePattern.equals(other.columnNamePattern)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Request for
@@ -201,6 +591,30 @@ public interface Service {
     @Override ResultSetResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override TypeInfoRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.TypeInfoRequest)) {
+        throw new IllegalArgumentException(
+            "Expected TypeInfoRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      return new TypeInfoRequest();
+    }
+
+    @Override Requests.TypeInfoRequest serialize() {
+      return Requests.TypeInfoRequest.newBuilder().build();
+    }
+
+    @Override public int hashCode() {
+      return 0;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      return o instanceof TypeInfoRequest;
+    }
   }
 
   /** Response that contains a result set.
@@ -223,6 +637,15 @@ public interface Service {
     public final Meta.Frame firstFrame;
     public final long updateCount;
 
+    ResultSetResponse() {
+      connectionId = null;
+      statementId = 0;
+      ownStatement = false;
+      signature = null;
+      firstFrame = null;
+      updateCount = 0;
+    }
+
     @JsonCreator
     public ResultSetResponse(
         @JsonProperty("connectionId") String connectionId,
@@ -238,6 +661,109 @@ public interface Service {
       this.firstFrame = firstFrame;
       this.updateCount = updateCount;
     }
+
+    @Override ResultSetResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.ResultSetResponse)) {
+        throw new IllegalArgumentException(
+            "Expected ResultSetResponse, but got " + genericMsg.getClass().getName());
+      }
+
+      return fromProto((Responses.ResultSetResponse) genericMsg);
+    }
+
+    static ResultSetResponse fromProto(Responses.ResultSetResponse msg) {
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.ResultSetResponse.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      Meta.Signature signature = null;
+      if (ProtobufService.hasField(msg, desc, Responses.ResultSetResponse.SIGNATURE_FIELD_NUMBER)) {
+        signature = Meta.Signature.fromProto(msg.getSignature());
+      }
+
+      Meta.Frame frame = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.ResultSetResponse.FIRST_FRAME_FIELD_NUMBER)) {
+        frame = Meta.Frame.fromProto(msg.getFirstFrame());
+      }
+
+      return new ResultSetResponse(connectionId, msg.getStatementId(), msg.getOwnStatement(),
+          signature, frame, msg.getUpdateCount());
+    }
+
+    @Override Responses.ResultSetResponse serialize() {
+      Responses.ResultSetResponse.Builder builder = Responses.ResultSetResponse.newBuilder();
+
+      builder.setStatementId(statementId).setOwnStatement(ownStatement).setUpdateCount(updateCount);
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+
+      if (null != signature) {
+        builder.setSignature(signature.toProto());
+      }
+
+      if (null != firstFrame) {
+        builder.setFirstFrame(firstFrame.toProto());
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      result = prime * result + ((firstFrame == null) ? 0 : firstFrame.hashCode());
+      result = prime * result + (ownStatement ? 1231 : 1237);
+      result = prime * result + ((signature == null) ? 0 : signature.hashCode());
+      result = prime * result + statementId;
+      result = prime * result + (int) (updateCount ^ (updateCount >>> 32));
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof ResultSetResponse) {
+        ResultSetResponse other = (ResultSetResponse) o;
+
+        if (connectionId == null) {
+          if (other.connectionId != null) {
+            return false;
+          }
+        } else if (!connectionId.equals(other.connectionId)) {
+          return false;
+        }
+
+        if (firstFrame == null) {
+          if (other.firstFrame != null) {
+            return false;
+          }
+        } else if (!firstFrame.equals(other.firstFrame)) {
+          return false;
+        }
+
+        if (signature == null) {
+          if (other.signature != null) {
+            return false;
+          }
+        } else if (!signature.equals(other.signature)) {
+          return false;
+        }
+
+        return ownStatement == other.ownStatement && statementId == other.statementId
+            && updateCount == other.updateCount;
+      }
+
+      return false;
+    }
   }
 
   /** Request for
@@ -248,6 +774,13 @@ public interface Service {
     public final long maxRowCount;
     public final int statementId;
 
+    PrepareAndExecuteRequest() {
+      connectionId = null;
+      sql = null;
+      maxRowCount = 0;
+      statementId = 0;
+    }
+
     @JsonCreator
     public PrepareAndExecuteRequest(
         @JsonProperty("connectionId") String connectionId,
@@ -263,6 +796,85 @@ public interface Service {
     @Override ExecuteResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override PrepareAndExecuteRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.PrepareAndExecuteRequest)) {
+        throw new IllegalArgumentException(
+            "Expected PrepareAndExecuteRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      final Requests.PrepareAndExecuteRequest msg = (Requests.PrepareAndExecuteRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.PrepareAndExecuteRequest.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      String sql = null;
+      if (ProtobufService.hasField(msg, desc, Requests.PrepareAndExecuteRequest.SQL_FIELD_NUMBER)) {
+        sql = msg.getSql();
+      }
+
+      return new PrepareAndExecuteRequest(connectionId, msg.getStatementId(), sql,
+          msg.getMaxRowCount());
+    }
+
+    @Override Requests.PrepareAndExecuteRequest serialize() {
+      Requests.PrepareAndExecuteRequest.Builder builder = Requests.PrepareAndExecuteRequest
+          .newBuilder();
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+      if (null != sql) {
+        builder.setSql(sql);
+      }
+      builder.setStatementId(statementId);
+      builder.setMaxRowCount(maxRowCount);
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      result = prime * result + (int) (maxRowCount ^ (maxRowCount >>> 32));
+      result = prime * result + ((sql == null) ? 0 : sql.hashCode());
+      result = prime * result + statementId;
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof PrepareAndExecuteRequest) {
+        PrepareAndExecuteRequest other = (PrepareAndExecuteRequest) o;
+
+        if (null == connectionId) {
+          if (null != other.connectionId) {
+            return false;
+          }
+        } else if (!connectionId.equals(other.connectionId)) {
+          return false;
+        }
+
+        if (null == sql) {
+          if (null != other.sql) {
+            return false;
+          }
+        } else if (!sql.equals(other.sql)) {
+          return false;
+        }
+
+        return statementId == other.statementId && maxRowCount == other.maxRowCount;
+      }
+
+      return false;
+    }
   }
 
   /** Response to a
@@ -270,11 +882,72 @@ public interface Service {
   class ExecuteResponse extends Response {
     public final List<ResultSetResponse> results;
 
+    ExecuteResponse() {
+      results = null;
+    }
+
     @JsonCreator
     public ExecuteResponse(
         @JsonProperty("resultSets") List<ResultSetResponse> results) {
       this.results = results;
     }
+
+    @Override ExecuteResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.ExecuteResponse)) {
+        throw new IllegalArgumentException(
+            "Expected ExecuteResponse, but got " + genericMsg.getClass().getName());
+      }
+
+      Responses.ExecuteResponse msg = (Responses.ExecuteResponse) genericMsg;
+
+      List<Responses.ResultSetResponse> msgResults = msg.getResultsList();
+      List<ResultSetResponse> copiedResults = new ArrayList<>(msgResults.size());
+
+      for (Responses.ResultSetResponse msgResult : msgResults) {
+        copiedResults.add(ResultSetResponse.fromProto(msgResult));
+      }
+
+      return new ExecuteResponse(copiedResults);
+    }
+
+    @Override Responses.ExecuteResponse serialize() {
+      Responses.ExecuteResponse.Builder builder = Responses.ExecuteResponse.newBuilder();
+
+      for (ResultSetResponse result : results) {
+        builder.addResults(result.serialize());
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      if (null == results) {
+        return 0;
+      }
+
+      return results.hashCode();
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof ExecuteResponse) {
+        ExecuteResponse other = (ExecuteResponse) o;
+
+        if (null == results) {
+          if (null != other.results) {
+            return false;
+          }
+        } else if (!results.equals(other.results)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Request for
@@ -284,6 +957,12 @@ public interface Service {
     public final String sql;
     public final long maxRowCount;
 
+    PrepareRequest() {
+      connectionId = null;
+      sql = null;
+      maxRowCount = 0;
+    }
+
     @JsonCreator
     public PrepareRequest(
         @JsonProperty("connectionId") String connectionId,
@@ -297,6 +976,80 @@ public interface Service {
     @Override PrepareResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override PrepareRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.PrepareRequest)) {
+        throw new IllegalArgumentException(
+            "Expected PrepareRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      final Requests.PrepareRequest msg = (Requests.PrepareRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc, Requests.PrepareRequest.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      String sql = null;
+      if (ProtobufService.hasField(msg, desc, Requests.PrepareRequest.SQL_FIELD_NUMBER)) {
+        sql = msg.getSql();
+      }
+
+      return new PrepareRequest(connectionId, sql, msg.getMaxRowCount());
+    }
+
+    @Override Requests.PrepareRequest serialize() {
+      Requests.PrepareRequest.Builder builder = Requests.PrepareRequest.newBuilder();
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+
+      if (null != sql) {
+        builder.setSql(sql);
+      }
+
+      return builder.setMaxRowCount(maxRowCount).build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      result = prime * result + (int) (maxRowCount ^ (maxRowCount >>> 32));
+      result = prime * result + ((sql == null) ? 0 : sql.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof PrepareRequest) {
+        PrepareRequest other = (PrepareRequest) o;
+
+        if (null == connectionId) {
+          if (null != other.connectionId) {
+            return false;
+          }
+        } else if (!connectionId.equals(other.connectionId)) {
+          return false;
+        }
+
+        if (null == sql) {
+          if (null != other.sql) {
+            return false;
+          }
+        } else if (!sql.equals(other.sql)) {
+          return false;
+        }
+
+        return maxRowCount == other.maxRowCount;
+      }
+
+      return false;
+    }
   }
 
   /** Response from
@@ -304,11 +1057,64 @@ public interface Service {
   class PrepareResponse extends Response {
     public final Meta.StatementHandle statement;
 
+    PrepareResponse() {
+      statement = null;
+    }
+
     @JsonCreator
     public PrepareResponse(
         @JsonProperty("statement") Meta.StatementHandle statement) {
       this.statement = statement;
     }
+
+    @Override PrepareResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.PrepareResponse)) {
+        throw new IllegalArgumentException(
+            "Expected PrepareResponse, but got " + genericMsg.getClass().getName());
+      }
+
+      Responses.PrepareResponse msg = (Responses.PrepareResponse) genericMsg;
+
+      return new PrepareResponse(Meta.StatementHandle.fromProto(msg.getStatement()));
+    }
+
+    @Override Responses.PrepareResponse serialize() {
+      Responses.PrepareResponse.Builder builder = Responses.PrepareResponse.newBuilder();
+
+      if (null != statement) {
+        builder.setStatement(statement.toProto());
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((statement == null) ? 0 : statement.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof PrepareResponse) {
+        PrepareResponse other = (PrepareResponse) o;
+
+        if (statement == null) {
+          if (other.statement != null) {
+            return false;
+          }
+        } else if (!statement.equals(other.statement)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Request for
@@ -324,6 +1130,14 @@ public interface Service {
      * null. */
     public final List<TypedValue> parameterValues;
 
+    FetchRequest() {
+      connectionId = null;
+      statementId = 0;
+      offset = 0;
+      fetchMaxRowCount = 0;
+      parameterValues = null;
+    }
+
     @JsonCreator
     public FetchRequest(
         @JsonProperty("connectionId") String connectionId,
@@ -341,6 +1155,99 @@ public interface Service {
     @Override FetchResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override FetchRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.FetchRequest)) {
+        throw new IllegalArgumentException(
+            "Expected FetchRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      final Requests.FetchRequest msg = (Requests.FetchRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      // Cannot determine if a value was set for a repeated field. Must use an extra boolean
+      // parameter to distinguish an empty list and a null list of ParameterValues.
+      List<TypedValue> values = null;
+      if (msg.getHasParameterValues()) {
+        values = new ArrayList<>(msg.getParameterValuesCount());
+        for (Common.TypedValue valueProto : msg.getParameterValuesList()) {
+          values.add(TypedValue.fromProto(valueProto));
+        }
+      }
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc, Requests.FetchRequest.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      return new FetchRequest(connectionId, msg.getStatementId(), values, msg.getOffset(),
+          msg.getFetchMaxRowCount());
+    }
+
+    @Override Requests.FetchRequest serialize() {
+      Requests.FetchRequest.Builder builder = Requests.FetchRequest.newBuilder();
+
+      if (null != parameterValues) {
+        builder.setHasParameterValues(true);
+        for (TypedValue paramValue : parameterValues) {
+          builder.addParameterValues(paramValue.toProto());
+        }
+      } else {
+        builder.setHasParameterValues(false);
+      }
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+
+      builder.setStatementId(statementId);
+      builder.setOffset(offset);
+      builder.setFetchMaxRowCount(fetchMaxRowCount);
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      result = prime * result + fetchMaxRowCount;
+      result = prime * result + (int) (offset ^ (offset >>> 32));
+      result = prime * result + ((parameterValues == null) ? 0 : parameterValues.hashCode());
+      result = prime * result + statementId;
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof FetchRequest) {
+        FetchRequest other = (FetchRequest) o;
+
+        if (null == connectionId) {
+          if (null != other.connectionId) {
+            return false;
+          }
+        } else {
+          if (!connectionId.equals(other.connectionId)) {
+            return false;
+          }
+        }
+
+        if (null == parameterValues) {
+          if (null != other.parameterValues) {
+            return false;
+          }
+        } else if (!parameterValues.equals(other.parameterValues)) {
+          return false;
+        }
+
+        return offset == other.offset && fetchMaxRowCount == other.fetchMaxRowCount;
+      }
+
+      return false;
+    }
   }
 
   /** Response from
@@ -348,10 +1255,63 @@ public interface Service {
   class FetchResponse extends Response {
     public final Meta.Frame frame;
 
+    FetchResponse() {
+      frame = null;
+    }
+
     @JsonCreator
     public FetchResponse(@JsonProperty("frame") Meta.Frame frame) {
       this.frame = frame;
     }
+
+    @Override FetchResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.FetchResponse)) {
+        throw new IllegalArgumentException(
+            "Expected FetchResponse, but got" + genericMsg.getClass().getName());
+      }
+
+      Responses.FetchResponse msg = (Responses.FetchResponse) genericMsg;
+
+      return new FetchResponse(Meta.Frame.fromProto(msg.getFrame()));
+    }
+
+    @Override Responses.FetchResponse serialize() {
+      Responses.FetchResponse.Builder builder = Responses.FetchResponse.newBuilder();
+
+      if (null != frame) {
+        builder.setFrame(frame.toProto());
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((frame == null) ? 0 : frame.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof FetchResponse) {
+        FetchResponse other = (FetchResponse) o;
+
+        if (frame == null) {
+          if (other.frame != null) {
+            return false;
+          }
+        } else if (!frame.equals(other.frame)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Request for
@@ -359,6 +1319,10 @@ public interface Service {
   class CreateStatementRequest extends Request {
     public final String connectionId;
 
+    CreateStatementRequest() {
+      connectionId = null;
+    }
+
     @JsonCreator
     public CreateStatementRequest(
         @JsonProperty("signature") String connectionId) {
@@ -368,6 +1332,63 @@ public interface Service {
     @Override CreateStatementResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override CreateStatementRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.CreateStatementRequest)) {
+        throw new IllegalArgumentException(
+            "Expected CreateStatementRequest, but got" + genericMsg.getClass().getName());
+      }
+
+      final Requests.CreateStatementRequest msg = (Requests.CreateStatementRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.CreateStatementRequest.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      return new CreateStatementRequest(connectionId);
+    }
+
+    @Override Requests.CreateStatementRequest serialize() {
+      Requests.CreateStatementRequest.Builder builder = Requests.CreateStatementRequest
+          .newBuilder();
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof CreateStatementRequest) {
+        CreateStatementRequest other = (CreateStatementRequest) o;
+
+        if (null == connectionId) {
+          if (null != other.connectionId) {
+            return false;
+          }
+        } else if (!connectionId.equals(other.connectionId)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Response from
@@ -376,6 +1397,11 @@ public interface Service {
     public final String connectionId;
     public final int statementId;
 
+    CreateStatementResponse() {
+      connectionId = null;
+      statementId = 0;
+    }
+
     @JsonCreator
     public CreateStatementResponse(
         @JsonProperty("connectionId") String connectionId,
@@ -383,6 +1409,66 @@ public interface Service {
       this.connectionId = connectionId;
       this.statementId = statementId;
     }
+
+    @Override CreateStatementResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.CreateStatementResponse)) {
+        throw new IllegalArgumentException(
+            "Expected CreateStatementResponse, but got " + genericMsg.getClass().getName());
+      }
+
+      final Responses.CreateStatementResponse msg = (Responses.CreateStatementResponse) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.CreateStatementResponse.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      return new CreateStatementResponse(connectionId, msg.getStatementId());
+    }
+
+    @Override Responses.CreateStatementResponse serialize() {
+      Responses.CreateStatementResponse.Builder builder = Responses.CreateStatementResponse
+          .newBuilder();
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+
+      builder.setStatementId(statementId);
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      result = prime * result + statementId;
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof CreateStatementResponse) {
+        CreateStatementResponse other = (CreateStatementResponse) o;
+
+        if (connectionId == null) {
+          if (other.connectionId != null) {
+            return false;
+          }
+        } else if (!connectionId.equals(other.connectionId)) {
+          return false;
+        }
+
+        return statementId == other.statementId;
+      }
+
+      return false;
+    }
   }
 
   /** Request for
@@ -391,6 +1477,11 @@ public interface Service {
     public final String connectionId;
     public final int statementId;
 
+    CloseStatementRequest() {
+      connectionId = null;
+      statementId = 0;
+    }
+
     @JsonCreator
     public CloseStatementRequest(
         @JsonProperty("connectionId") String connectionId,
@@ -402,6 +1493,63 @@ public interface Service {
     @Override CloseStatementResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override CloseStatementRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.CloseStatementRequest)) {
+        throw new IllegalArgumentException(
+            "Expected CloseStatementRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      final Requests.CloseStatementRequest msg = (Requests.CloseStatementRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.CloseStatementRequest.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      return new CloseStatementRequest(connectionId, msg.getStatementId());
+    }
+
+    @Override Requests.CloseStatementRequest serialize() {
+      Requests.CloseStatementRequest.Builder builder = Requests.CloseStatementRequest.newBuilder();
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+
+      return builder.setStatementId(statementId).build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      result = prime * result + statementId;
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof CloseStatementRequest) {
+        CloseStatementRequest other = (CloseStatementRequest) o;
+
+        if (null == connectionId) {
+          if (null != other.connectionId) {
+            return false;
+          }
+        } else if (!connectionId.equals(other.connectionId)) {
+          return false;
+        }
+
+        return statementId == other.statementId;
+      }
+
+      return false;
+    }
   }
 
   /** Response from
@@ -409,6 +1557,30 @@ public interface Service {
   class CloseStatementResponse extends Response {
     @JsonCreator
     public CloseStatementResponse() {}
+
+    @Override CloseStatementResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.CloseStatementResponse)) {
+        throw new IllegalArgumentException(
+            "Expected CloseStatementResponse, but got " + genericMsg.getClass().getName());
+      }
+
+      return new CloseStatementResponse();
+    }
+
+    @Override Responses.CloseStatementResponse serialize() {
+      return Responses.CloseStatementResponse.newBuilder().build();
+    }
+
+    @Override public int hashCode() {
+      return 0;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      return o instanceof CloseStatementResponse;
+    }
   }
 
   /** Request for
@@ -416,6 +1588,10 @@ public interface Service {
   class CloseConnectionRequest extends Request {
     public final String connectionId;
 
+    CloseConnectionRequest() {
+      connectionId = null;
+    }
+
     @JsonCreator
     public CloseConnectionRequest(
         @JsonProperty("connectionId") String connectionId) {
@@ -425,6 +1601,63 @@ public interface Service {
     @Override CloseConnectionResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override CloseConnectionRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.CloseConnectionRequest)) {
+        throw new IllegalArgumentException(
+            "Expected CloseConnectionRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      final Requests.CloseConnectionRequest msg = (Requests.CloseConnectionRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.CloseConnectionRequest.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      return new CloseConnectionRequest(connectionId);
+    }
+
+    @Override Requests.CloseConnectionRequest serialize() {
+      Requests.CloseConnectionRequest.Builder builder = Requests.CloseConnectionRequest
+          .newBuilder();
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof CloseConnectionRequest) {
+        CloseConnectionRequest other = (CloseConnectionRequest) o;
+
+        if (null == connectionId) {
+          if (null != other.connectionId) {
+            return false;
+          }
+        } else if (!connectionId.equals(other.connectionId)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Response from
@@ -432,6 +1665,30 @@ public interface Service {
   class CloseConnectionResponse extends Response {
     @JsonCreator
     public CloseConnectionResponse() {}
+
+    @Override CloseConnectionResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.CloseConnectionResponse)) {
+        throw new IllegalArgumentException(
+            "Expected CloseConnectionResponse, but got " + genericMsg.getClass().getName());
+      }
+
+      return new CloseConnectionResponse();
+    }
+
+    @Override Responses.CloseConnectionResponse serialize() {
+      return Responses.CloseConnectionResponse.newBuilder().build();
+    }
+
+    @Override public int hashCode() {
+      return 0;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      return o instanceof CloseConnectionResponse;
+    }
   }
 
   /** Request for {@link Meta#connectionSync(Meta.ConnectionHandle, Meta.ConnectionProperties)}. */
@@ -439,6 +1696,11 @@ public interface Service {
     public final String connectionId;
     public final Meta.ConnectionProperties connProps;
 
+    ConnectionSyncRequest() {
+      connectionId = null;
+      connProps = null;
+    }
+
     @JsonCreator
     public ConnectionSyncRequest(
         @JsonProperty("connectionId") String connectionId,
@@ -450,6 +1712,81 @@ public interface Service {
     @Override ConnectionSyncResponse accept(Service service) {
       return service.apply(this);
     }
+
+    @Override ConnectionSyncRequest deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Requests.ConnectionSyncRequest)) {
+        throw new IllegalArgumentException(
+            "Expected ConnectionSyncRequest, but got " + genericMsg.getClass().getName());
+      }
+
+      final Requests.ConnectionSyncRequest msg = (Requests.ConnectionSyncRequest) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
+
+      String connectionId = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.ConnectionSyncRequest.CONNECTION_ID_FIELD_NUMBER)) {
+        connectionId = msg.getConnectionId();
+      }
+
+      Meta.ConnectionProperties connProps = null;
+      if (ProtobufService.hasField(msg, desc,
+          Requests.ConnectionSyncRequest.CONN_PROPS_FIELD_NUMBER)) {
+        connProps = ConnectionPropertiesImpl.fromProto(msg.getConnProps());
+      }
+
+      return new ConnectionSyncRequest(connectionId, connProps);
+    }
+
+    @Override Requests.ConnectionSyncRequest serialize() {
+      Requests.ConnectionSyncRequest.Builder builder = Requests.ConnectionSyncRequest.newBuilder();
+
+      if (null != connectionId) {
+        builder.setConnectionId(connectionId);
+      }
+
+      if (null != connProps) {
+        builder.setConnProps(connProps.toProto());
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connProps == null) ? 0 : connProps.hashCode());
+      result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
+      return result;
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof ConnectionSyncRequest) {
+        ConnectionSyncRequest other = (ConnectionSyncRequest) o;
+
+        if (null == connectionId) {
+          if (null != other.connectionId) {
+            return false;
+          }
+        } else if (!connectionId.equals(other.connectionId)) {
+          return false;
+        }
+
+        if (null == connProps) {
+          if (null != other.connProps) {
+            return false;
+          }
+        } else if (!connProps.equals(other.connProps)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Response for
@@ -457,10 +1794,65 @@ public interface Service {
   class ConnectionSyncResponse extends Response {
     public final Meta.ConnectionProperties connProps;
 
+    ConnectionSyncResponse() {
+      connProps = null;
+    }
+
     @JsonCreator
     public ConnectionSyncResponse(@JsonProperty("connProps") Meta.ConnectionProperties connProps) {
       this.connProps = connProps;
     }
+
+    @Override ConnectionSyncResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.ConnectionSyncResponse)) {
+        throw new IllegalArgumentException(
+            "Expected ConnectionSyncResponse, but got " + genericMsg.getClass().getName());
+      }
+
+      Responses.ConnectionSyncResponse msg = (Responses.ConnectionSyncResponse) genericMsg;
+
+      return new ConnectionSyncResponse(ConnectionPropertiesImpl.fromProto(msg.getConnProps()));
+    }
+
+    @Override Responses.ConnectionSyncResponse serialize() {
+      Responses.ConnectionSyncResponse.Builder builder = Responses.ConnectionSyncResponse
+          .newBuilder();
+
+      if (null != connProps) {
+        builder.setConnProps(connProps.toProto());
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      if (null == connProps) {
+        return 0;
+      }
+
+      return connProps.hashCode();
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof ConnectionSyncResponse) {
+        ConnectionSyncResponse other = (ConnectionSyncResponse) o;
+
+        if (null == connProps) {
+          if (null != other.connProps) {
+            return false;
+          }
+        } else if (!connProps.equals(other.connProps)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 
   /** Response for
@@ -468,10 +1860,132 @@ public interface Service {
   class DatabasePropertyResponse extends Response {
     public final Map<Meta.DatabaseProperty, Object> map;
 
+    DatabasePropertyResponse() {
+      map = null;
+    }
+
     @JsonCreator
     public DatabasePropertyResponse(@JsonProperty("map") Map<Meta.DatabaseProperty, Object> map) {
       this.map = map;
     }
+
+    @Override DatabasePropertyResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.DatabasePropertyResponse)) {
+        throw new IllegalArgumentException(
+            "Expected DatabasePropertyResponse, but got " + genericMsg.getClass().getName());
+      }
+
+      Responses.DatabasePropertyResponse msg = (Responses.DatabasePropertyResponse) genericMsg;
+
+      HashMap<Meta.DatabaseProperty, Object> properties = new HashMap<>();
+      for (Responses.DatabasePropertyElement property : msg.getPropsList()) {
+        final Meta.DatabaseProperty dbProp = Meta.DatabaseProperty.fromProto(property.getKey());
+        final Common.TypedValue value = property.getValue();
+
+        Object obj;
+        switch (dbProp) {
+        // Just need to keep parity with the exposed values on DatabaseProperty
+        case GET_NUMERIC_FUNCTIONS:
+        case GET_STRING_FUNCTIONS:
+        case GET_SYSTEM_FUNCTIONS:
+        case GET_TIME_DATE_FUNCTIONS:
+        case GET_S_Q_L_KEYWORDS:
+          // String
+          if (Common.Rep.STRING != value.getType()) {
+            throw new IllegalArgumentException("Expected STRING, but got " + value.getType());
+          }
+
+          obj = value.getStringValue();
+          break;
+        case GET_DEFAULT_TRANSACTION_ISOLATION:
+          // int
+          if (Common.Rep.INTEGER != value.getType()) {
+            throw new IllegalArgumentException("Expected INTEGER, but got " + value.getType());
+          }
+
+          obj = Integer.valueOf((int) value.getNumberValue());
+          break;
+        default:
+          throw new RuntimeException("Unhandled DatabaseProperty");
+        }
+
+        properties.put(dbProp, obj);
+      }
+
+      return new DatabasePropertyResponse(properties);
+    }
+
+    @Override Responses.DatabasePropertyResponse serialize() {
+      Responses.DatabasePropertyResponse.Builder builder = Responses.DatabasePropertyResponse
+          .newBuilder();
+
+      if (null != map) {
+        for (Entry<Meta.DatabaseProperty, Object> entry : map.entrySet()) {
+          Object obj = entry.getValue();
+
+          Common.TypedValue.Builder valueBuilder = Common.TypedValue.newBuilder();
+          switch (entry.getKey()) {
+          // Just need to keep parity with the exposed values on DatabaseProperty
+          case GET_NUMERIC_FUNCTIONS:
+          case GET_STRING_FUNCTIONS:
+          case GET_SYSTEM_FUNCTIONS:
+          case GET_TIME_DATE_FUNCTIONS:
+          case GET_S_Q_L_KEYWORDS:
+            // String
+            if (!(obj instanceof String)) {
+              throw new RuntimeException("Expected a String, but got " + obj.getClass());
+            }
+
+            valueBuilder.setType(Common.Rep.STRING).setStringValue((String) obj);
+            break;
+          case GET_DEFAULT_TRANSACTION_ISOLATION:
+            // int
+            if (!(obj instanceof Integer)) {
+              throw new RuntimeException("Expected an Integer, but got " + obj.getClass());
+            }
+
+            valueBuilder.setType(Common.Rep.INTEGER).setNumberValue(((Integer) obj).longValue());
+            break;
+          default:
+            throw new RuntimeException("Unhandled DatabaseProperty");
+          }
+
+          builder.addProps(Responses.DatabasePropertyElement.newBuilder()
+              .setKey(entry.getKey().toProto()).setValue(valueBuilder.build()));
+        }
+      }
+
+      return builder.build();
+    }
+
+    @Override public int hashCode() {
+      if (null == map) {
+        return 0;
+      }
+
+      return map.hashCode();
+    }
+
+    @Override public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (o instanceof DatabasePropertyResponse) {
+        DatabasePropertyResponse other = (DatabasePropertyResponse) o;
+
+        if (null == map) {
+          if (null != other.map) {
+            return false;
+          }
+        } else if (!map.equals(other.map)) {
+          return false;
+        }
+
+        return true;
+      }
+
+      return false;
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cb7c213c/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
index d12fc40..5c80816 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.avatica.remote;
 
 import org.apache.calcite.avatica.ColumnMetaData;
+import org.apache.calcite.avatica.proto.Common;
 import org.apache.calcite.avatica.util.ByteString;
 import org.apache.calcite.avatica.util.DateTimeUtils;
 
@@ -329,6 +330,203 @@ public class TypedValue {
     }
     return list;
   }
+
+  public Common.TypedValue toProto() {
+    final Common.TypedValue.Builder builder = Common.TypedValue.newBuilder();
+
+    Common.Rep protoRep = type.toProto();
+    builder.setType(protoRep);
+
+    // Serialize the type into the protobuf
+    switch (protoRep) {
+    case BOOLEAN:
+    case PRIMITIVE_BOOLEAN:
+      builder.setBoolValue((boolean) value);
+      break;
+    case BYTE_STRING:
+    case STRING:
+      builder.setStringValue((String) value);
+      break;
+    case PRIMITIVE_CHAR:
+    case CHARACTER:
+      builder.setStringValue(Character.toString((char) value));
+      break;
+    case BYTE:
+    case PRIMITIVE_BYTE:
+      builder.setNumberValue(Byte.valueOf((byte) value).longValue());
+      break;
+    case DOUBLE:
+    case PRIMITIVE_DOUBLE:
+      builder.setDoubleValue((double) value);
+      break;
+    case FLOAT:
+    case PRIMITIVE_FLOAT:
+      builder.setNumberValue(Float.floatToIntBits((float) value));
+      break;
+    case INTEGER:
+    case PRIMITIVE_INT:
+      builder.setNumberValue(Integer.valueOf((int) value).longValue());
+      break;
+    case PRIMITIVE_SHORT:
+    case SHORT:
+      builder.setNumberValue(Short.valueOf((short) value).longValue());
+      break;
+    case LONG:
+    case PRIMITIVE_LONG:
+      builder.setNumberValue((long) value);
+      break;
+    case JAVA_SQL_DATE:
+    case JAVA_SQL_TIME:
+      // Persisted as integers
+      builder.setNumberValue(Integer.valueOf((int) value).longValue());
+      break;
+    case JAVA_SQL_TIMESTAMP:
+    case JAVA_UTIL_DATE:
+      // Persisted as longs
+      builder.setNumberValue((long) value);
+      break;
+    case BIG_INTEGER:
+      byte[] bytes = ((BigInteger) value).toByteArray();
+      builder.setBytesValues(com.google.protobuf.ByteString.copyFrom(bytes));
+      break;
+    case BIG_DECIMAL:
+      final BigDecimal bigDecimal = (BigDecimal) value;
+      final int scale = bigDecimal.scale();
+      final BigInteger bigInt = bigDecimal.toBigInteger();
+      builder.setBytesValues(com.google.protobuf.ByteString.copyFrom(bigInt.toByteArray()))
+        .setNumberValue(scale);
+      break;
+    case NUMBER:
+      builder.setNumberValue(((Number) value).longValue());
+      break;
+    case OBJECT:
+      if (null == value) {
+        // We can persist a null value through easily
+        builder.setNull(true);
+        break;
+      }
+      // Intentional fall-through to RTE because we can't serialize something we have no type
+      // insight into.
+    case UNRECOGNIZED:
+      // Fail?
+      throw new RuntimeException("Unhandled value: " + protoRep + " " + value.getClass());
+    default:
+      // Fail?
+      throw new RuntimeException("Unknown serialized type: " + protoRep);
+    }
+
+    return builder.build();
+  }
+
+  public static TypedValue fromProto(Common.TypedValue proto) {
+    ColumnMetaData.Rep rep = ColumnMetaData.Rep.fromProto(proto.getType());
+
+    Object value = null;
+
+    // Deserialize the value again
+    switch (proto.getType()) {
+    case BOOLEAN:
+    case PRIMITIVE_BOOLEAN:
+      value = proto.getBoolValue();
+      break;
+    case BYTE_STRING:
+    case STRING:
+      value = proto.getStringValue();
+      break;
+    case PRIMITIVE_CHAR:
+    case CHARACTER:
+      value = proto.getStringValue().charAt(0);
+      break;
+    case BYTE:
+    case PRIMITIVE_BYTE:
+      value = Long.valueOf(proto.getNumberValue()).byteValue();
+      break;
+    case DOUBLE:
+    case PRIMITIVE_DOUBLE:
+      value = proto.getDoubleValue();
+      break;
+    case FLOAT:
+    case PRIMITIVE_FLOAT:
+      value = Float.intBitsToFloat((int) proto.getNumberValue());
+      break;
+    case INTEGER:
+    case PRIMITIVE_INT:
+      value = Long.valueOf(proto.getNumberValue()).intValue();
+      break;
+    case PRIMITIVE_SHORT:
+    case SHORT:
+      value = Long.valueOf(proto.getNumberValue()).shortValue();
+      break;
+    case LONG:
+    case PRIMITIVE_LONG:
+      value = Long.valueOf(proto.getNumberValue());
+      break;
+    case JAVA_SQL_DATE:
+    case JAVA_SQL_TIME:
+      value = Long.valueOf(proto.getNumberValue()).intValue();
+      break;
+    case JAVA_SQL_TIMESTAMP:
+    case JAVA_UTIL_DATE:
+      value = proto.getNumberValue();
+      break;
+    case BIG_INTEGER:
+      value = new BigInteger(proto.getBytesValues().toByteArray());
+      break;
+    case BIG_DECIMAL:
+      BigInteger bigInt = new BigInteger(proto.getBytesValues().toByteArray());
+      value = new BigDecimal(bigInt, (int) proto.getNumberValue());
+      break;
+    case NUMBER:
+      value = Long.valueOf(proto.getNumberValue());
+      break;
+    case OBJECT:
+      if (proto.getNull()) {
+        value = null;
+        break;
+      }
+      // Intentional fall through to RTE. If we sent an object over the wire, it could only
+      // possibly be null (at this point). Anything else has to be an error.
+    case UNRECOGNIZED:
+      // Fail?
+      throw new RuntimeException("Unhandled type: " + proto.getType());
+    default:
+      // Fail?
+      throw new RuntimeException("Unknown type: " + proto.getType());
+    }
+
+    return new TypedValue(rep, value);
+  }
+
+  @Override public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((type == null) ? 0 : type.hashCode());
+    result = prime * result + ((value == null) ? 0 : value.hashCode());
+    return result;
+  }
+
+  @Override public boolean equals(Object o) {
+    if (o == this) {
+      return true;
+    }
+    if (o instanceof TypedValue) {
+      TypedValue other = (TypedValue) o;
+
+      if (type != other.type) {
+        return false;
+      }
+
+      if (null == value) {
+        if (null != other.value) {
+          return false;
+        }
+      }
+
+      return value.equals(other.value);
+    }
+
+    return false;
+  }
 }
 
 // End TypedValue.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cb7c213c/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java b/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
index 9786c7a..6d862d3 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
@@ -765,6 +765,13 @@ public abstract class AbstractCursor implements Cursor {
     }
 
     @Override public byte[] getBytes() {
+      // JSON sends this as a base64-enc string, protobuf can do binary.
+      Object obj = getObject();
+      if (obj instanceof byte[]) {
+        // If we already have bytes, just send them back.
+        return (byte[]) obj;
+      }
+
       final String string = getString();
       if (string == null) {
         return null;

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cb7c213c/avatica/src/main/protobuf/common.proto
----------------------------------------------------------------------
diff --git a/avatica/src/main/protobuf/common.proto b/avatica/src/main/protobuf/common.proto
new file mode 100644
index 0000000..1c81049
--- /dev/null
+++ b/avatica/src/main/protobuf/common.proto
@@ -0,0 +1,174 @@
+/*
+ * 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;
+}
+
+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;
+}
+
+// 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 TypedValue 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;
+}
+
+// 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
+}

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cb7c213c/avatica/src/main/protobuf/requests.proto
----------------------------------------------------------------------
diff --git a/avatica/src/main/protobuf/requests.proto b/avatica/src/main/protobuf/requests.proto
new file mode 100644
index 0000000..1385c93
--- /dev/null
+++ b/avatica/src/main/protobuf/requests.proto
@@ -0,0 +1,112 @@
+/*
+ * 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 {
+
+}
+
+// Request for Meta#getDatabaseProperties()
+message DatabasePropertyRequest {
+
+}
+
+// Request for Meta#getSchemas(String, org.apache.calcite.avatica.Meta.Pat)}
+message SchemasRequest {
+  string catalog = 1;
+  string schema_pattern = 2;
+}
+
+// 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;
+}
+
+// Request for Meta#getTableTypes()
+message TableTypesRequest {
+
+}
+
+// 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;
+}
+
+// Request for Meta#getTypeInfo()
+message TypeInfoRequest {
+
+}
+
+// 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.
+  repeated TypedValue parameter_values = 5; // List of parameter values if statement is to be executed. Else, none.
+  bool has_parameter_values = 6; // Having an empty list of param values is distinct from a null param list
+}
+
+// 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#closeConnection(Meta.ConnectionHandle)
+message CloseConnectionRequest {
+  string connection_id = 1;
+}
+
+message ConnectionSyncRequest {
+  string connection_id = 1;
+  ConnectionProperties conn_props = 2;
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cb7c213c/avatica/src/main/protobuf/responses.proto
----------------------------------------------------------------------
diff --git a/avatica/src/main/protobuf/responses.proto b/avatica/src/main/protobuf/responses.proto
new file mode 100644
index 0000000..1fad9c8
--- /dev/null
+++ b/avatica/src/main/protobuf/responses.proto
@@ -0,0 +1,78 @@
+/*
+ * 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.
+}
+
+// Response to PrepareAndExecuteRequest
+message ExecuteResponse {
+  repeated ResultSetResponse results = 1;
+}
+
+// Response to PrepareRequest
+message PrepareResponse {
+  StatementHandle statement = 1;
+}
+
+// Response to FetchRequest
+message FetchResponse {
+  Frame frame = 1;
+}
+
+// Response to CreateStatementRequest
+message CreateStatementResponse {
+  string connection_id = 1;
+  uint32 statement_id = 2;
+}
+
+// Response to CloseStatementRequest
+message CloseStatementResponse {
+
+}
+
+// Response to CloseConnectionRequest {
+message CloseConnectionResponse {
+
+}
+
+// Response to ConnectionSyncRequest
+message ConnectionSyncResponse {
+  ConnectionProperties conn_props = 1;
+}
+
+message DatabasePropertyElement {
+  DatabaseProperty key = 1;
+  TypedValue value = 2;
+}
+
+// Response for Meta#getDatabaseProperties()
+message DatabasePropertyResponse {
+  repeated DatabasePropertyElement props = 1;
+}

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cb7c213c/avatica/src/main/scripts/generate-protobuf.sh
----------------------------------------------------------------------
diff --git a/avatica/src/main/scripts/generate-protobuf.sh b/avatica/src/main/scripts/generate-protobuf.sh
new file mode 100755
index 0000000..c4d3abe
--- /dev/null
+++ b/avatica/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/incubator-calcite/blob/cb7c213c/avatica/src/test/java/org/apache/calcite/avatica/FrameTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/FrameTest.java b/avatica/src/test/java/org/apache/calcite/avatica/FrameTest.java
new file mode 100644
index 0000000..e4d524c
--- /dev/null
+++ b/avatica/src/test/java/org/apache/calcite/avatica/FrameTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.junit.Test;
+
+import java.util.ArrayList;
+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.assertTrue;
+
+/**
+ * Tests serialization of {@link Frame}.
+ */
+public class FrameTest {
+
+  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);
+  }
+}
+
+// End FrameTest.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cb7c213c/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java b/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
new file mode 100644
index 0000000..ab59b4a
--- /dev/null
+++ b/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.proto.Common;
+import org.apache.calcite.avatica.proto.Requests;
+import org.apache.calcite.avatica.proto.Responses;
+import org.apache.calcite.avatica.remote.Service.FetchRequest;
+import org.apache.calcite.avatica.remote.Service.FetchResponse;
+
+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);
+  }
+
+  @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)
+        .addAllParameterValues(values).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);
+    FetchResponse response = new FetchResponse(frame);
+
+    when(translation.parseRequest(serializedRequest)).thenReturn(request);
+    when(service.apply(request)).thenReturn(response);
+    when(translation.serializeResponse(response))
+        .thenReturn(response.serialize().toByteArray());
+
+    byte[] serializedResponse = handler.apply(serializedRequest);
+
+    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.TypedValue> rowValues = row.getValueList();
+    assertEquals(2, rowValues.size());
+
+    Iterator<Common.TypedValue> iter = rowValues.iterator();
+    assertTrue(iter.hasNext());
+    Common.TypedValue value = iter.next();
+    assertEquals(Common.Rep.BOOLEAN, value.getType());
+    assertEquals(true, value.getBoolValue());
+
+    assertTrue(iter.hasNext());
+    value = iter.next();
+    assertEquals(Common.Rep.STRING, value.getType());
+    assertEquals("my_string", value.getStringValue());
+  }
+
+}
+
+// End ProtobufHandlerTest.java