content: /*
   * 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.AvaticaConnection;
  import org.apache.calcite.avatica.AvaticaSeverity;
  import org.apache.calcite.avatica.AvaticaUtils;
  import org.apache.calcite.avatica.BuiltInConnectionProperty;
  import org.apache.calcite.avatica.ConnectionPropertiesImpl;
  import org.apache.calcite.avatica.Meta;
  import org.apache.calcite.avatica.QueryState;
  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.JsonIgnore;
  import com.fasterxml.jackson.annotation.JsonProperty;
  import com.fasterxml.jackson.annotation.JsonSubTypes;
  import com.fasterxml.jackson.annotation.JsonTypeInfo;
  import com.google.protobuf.ByteString;
  import com.google.protobuf.Descriptors.FieldDescriptor;
  import com.google.protobuf.Message;
  import com.google.protobuf.UnsafeByteOperations;
  
  import java.io.PrintWriter;
  import java.io.StringWriter;
  import java.sql.SQLException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Objects;
  import java.util.Properties;
  
  import static java.nio.charset.StandardCharsets.UTF_8;
  
  /**
   * API for request-response calls to an Avatica server.
   */
  public interface Service {
    ResultSetResponse apply(CatalogsRequest request);
    ResultSetResponse apply(SchemasRequest request);
    ResultSetResponse apply(TablesRequest request);
    ResultSetResponse apply(TableTypesRequest request);
    ResultSetResponse apply(TypeInfoRequest request);
    ResultSetResponse apply(ColumnsRequest request);
    PrepareResponse apply(PrepareRequest request);
    ExecuteResponse apply(ExecuteRequest request);
    ExecuteResponse apply(PrepareAndExecuteRequest request);
    SyncResultsResponse apply(SyncResultsRequest request);
    FetchResponse apply(FetchRequest request);
    CreateStatementResponse apply(CreateStatementRequest request);
    CloseStatementResponse apply(CloseStatementRequest request);
    OpenConnectionResponse apply(OpenConnectionRequest request);
    CloseConnectionResponse apply(CloseConnectionRequest request);
    ConnectionSyncResponse apply(ConnectionSyncRequest request);
    DatabasePropertyResponse apply(DatabasePropertyRequest request);
    CommitResponse apply(CommitRequest request);
    RollbackResponse apply(RollbackRequest request);
    ExecuteBatchResponse apply(PrepareAndExecuteBatchRequest request);
    ExecuteBatchResponse apply(ExecuteBatchRequest request);
  
    /**
     * Sets server-level metadata for RPCs. This includes information that is static across all RPCs.
     *
     * @param metadata The server-level metadata.
     */
    void setRpcMetadata(RpcMetadataResponse metadata);
  
    /** Factory that creates a {@code Service}. */
    interface Factory {
      Service create(AvaticaConnection connection);
    }
  
    /** Base class for request and response. */
    abstract class Base {
      static final int PRIME = 31;
  
      protected static int p(int result, Object o) {
        return PRIME * result + ((o == null) ? 0 : o.hashCode());
      }
  
      protected static int p(int result, boolean v) {
        return PRIME * result + (v ? 1231 : 1237);
      }
  
      protected static int p(int result, int v) {
        return PRIME * result + v;
      }
  
      protected static int p(int result, long v) {
        return PRIME * result + (int) (v ^ (v >>> 32));
      }
    }
  
    /** Base class for all service request messages. */
    @JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "request",
        defaultImpl = SchemasRequest.class)
    @JsonSubTypes({
        @JsonSubTypes.Type(value = CatalogsRequest.class, name = "getCatalogs"),
        @JsonSubTypes.Type(value = SchemasRequest.class, name = "getSchemas"),
        @JsonSubTypes.Type(value = TablesRequest.class, name = "getTables"),
        @JsonSubTypes.Type(value = TableTypesRequest.class, name = "getTableTypes"),
        @JsonSubTypes.Type(value = TypeInfoRequest.class, name = "getTypeInfo"),
        @JsonSubTypes.Type(value = ColumnsRequest.class, name = "getColumns"),
        @JsonSubTypes.Type(value = ExecuteRequest.class, name = "execute"),
        @JsonSubTypes.Type(value = PrepareRequest.class, name = "prepare"),
        @JsonSubTypes.Type(value = PrepareAndExecuteRequest.class,
            name = "prepareAndExecute"),
        @JsonSubTypes.Type(value = FetchRequest.class, name = "fetch"),
        @JsonSubTypes.Type(value = CreateStatementRequest.class,
            name = "createStatement"),
        @JsonSubTypes.Type(value = CloseStatementRequest.class,
            name = "closeStatement"),
        @JsonSubTypes.Type(value = OpenConnectionRequest.class,
            name = "openConnection"),
        @JsonSubTypes.Type(value = CloseConnectionRequest.class,
            name = "closeConnection"),
        @JsonSubTypes.Type(value = ConnectionSyncRequest.class, name = "connectionSync"),
        @JsonSubTypes.Type(value = DatabasePropertyRequest.class, name = "databaseProperties"),
        @JsonSubTypes.Type(value = SyncResultsRequest.class, name = "syncResults"),
        @JsonSubTypes.Type(value = CommitRequest.class, name = "commit"),
        @JsonSubTypes.Type(value = RollbackRequest.class, name = "rollback"),
        @JsonSubTypes.Type(value = PrepareAndExecuteBatchRequest.class,
            name = "prepareAndExecuteBatch"),
        @JsonSubTypes.Type(value = ExecuteBatchRequest.class, name = "executeBatch") })
    abstract class Request extends Base {
      abstract Response accept(Service service);
      abstract Request deserialize(Message genericMsg);
      abstract Message serialize();
    }
  
    /** Base class for all service response messages. */
    @JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "response")
    @JsonSubTypes({
        @JsonSubTypes.Type(value = OpenConnectionResponse.class, name = "openConnection"),
        @JsonSubTypes.Type(value = ResultSetResponse.class, name = "resultSet"),
        @JsonSubTypes.Type(value = PrepareResponse.class, name = "prepare"),
        @JsonSubTypes.Type(value = FetchResponse.class, name = "fetch"),
        @JsonSubTypes.Type(value = CreateStatementResponse.class,
            name = "createStatement"),
        @JsonSubTypes.Type(value = CloseStatementResponse.class,
            name = "closeStatement"),
        @JsonSubTypes.Type(value = CloseConnectionResponse.class,
            name = "closeConnection"),
        @JsonSubTypes.Type(value = ConnectionSyncResponse.class, name = "connectionSync"),
        @JsonSubTypes.Type(value = DatabasePropertyResponse.class, name = "databaseProperties"),
        @JsonSubTypes.Type(value = ExecuteResponse.class, name = "executeResults"),
        @JsonSubTypes.Type(value = ErrorResponse.class, name = "error"),
        @JsonSubTypes.Type(value = SyncResultsResponse.class, name = "syncResults"),
        @JsonSubTypes.Type(value = RpcMetadataResponse.class, name = "rpcMetadata"),
        @JsonSubTypes.Type(value = CommitResponse.class, name = "commit"),
        @JsonSubTypes.Type(value = RollbackResponse.class, name = "rollback"),
        @JsonSubTypes.Type(value = ExecuteBatchResponse.class, name = "executeBatch") })
    abstract class Response extends Base {
      abstract Response deserialize(Message genericMsg);
      abstract Message serialize();
    }
  
    /** Request for
     * {@link org.apache.calcite.avatica.Meta#getCatalogs(Meta.ConnectionHandle)}. */
    class CatalogsRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.CatalogsRequest.
          getDescriptor().findFieldByNumber(Requests.CatalogsRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
  
      public CatalogsRequest() {
        connectionId = null;
      }
  
      @JsonCreator
      public CatalogsRequest(@JsonProperty("connectionId") String connectionId) {
        this.connectionId = connectionId;
      }
  
      ResultSetResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override CatalogsRequest deserialize(Message genericMsg) {
        final Requests.CatalogsRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.CatalogsRequest.class);
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        return new CatalogsRequest(connectionId);
      }
  
      @Override Requests.CatalogsRequest serialize() {
        Requests.CatalogsRequest.Builder builder = Requests.CatalogsRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof CatalogsRequest
            && Objects.equals(connectionId, ((CatalogsRequest) o).connectionId);
      }
    }
  
    /** Request for
     * {@link org.apache.calcite.avatica.Meta#getDatabaseProperties(Meta.ConnectionHandle)}. */
    class DatabasePropertyRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR =
          Requests.DatabasePropertyRequest.getDescriptor()
              .findFieldByNumber(Requests.DatabasePropertyRequest.CONNECTION_ID_FIELD_NUMBER);
  
      public final String connectionId;
  
      public DatabasePropertyRequest() {
        connectionId = null;
      }
  
      @JsonCreator
      public DatabasePropertyRequest(@JsonProperty("connectionId") String connectionId) {
        this.connectionId = connectionId;
      }
  
      DatabasePropertyResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override DatabasePropertyRequest deserialize(Message genericMsg) {
        final Requests.DatabasePropertyRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.DatabasePropertyRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        return new DatabasePropertyRequest(connectionId);
      }
  
      @Override Requests.DatabasePropertyRequest serialize() {
        Requests.DatabasePropertyRequest.Builder builder =
            Requests.DatabasePropertyRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof DatabasePropertyRequest
            && Objects.equals(connectionId, ((DatabasePropertyRequest) o).connectionId);
      }
    }
  
    /** Request for
     * {@link Meta#getSchemas(Meta.ConnectionHandle, String, Meta.Pat)}. */
    class SchemasRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.SchemasRequest.
          getDescriptor().findFieldByNumber(Requests.SchemasRequest.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor CATALOG_DESCRIPTOR = Requests.SchemasRequest.
          getDescriptor().findFieldByNumber(Requests.SchemasRequest.CATALOG_FIELD_NUMBER);
      private static final FieldDescriptor SCHEMA_PATTERN_DESCRIPTOR = Requests.SchemasRequest.
          getDescriptor().findFieldByNumber(Requests.SchemasRequest.SCHEMA_PATTERN_FIELD_NUMBER);
  
      public final String connectionId;
      public final String catalog;
      public final String schemaPattern;
  
      SchemasRequest() {
        connectionId = null;
        catalog = null;
        schemaPattern = null;
      }
  
      @JsonCreator
      public SchemasRequest(@JsonProperty("connectionId") String connectionId,
          @JsonProperty("catalog") String catalog,
          @JsonProperty("schemaPattern") String schemaPattern) {
        this.connectionId = connectionId;
        this.catalog = catalog;
        this.schemaPattern = schemaPattern;
      }
  
      ResultSetResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override SchemasRequest deserialize(Message genericMsg) {
        final Requests.SchemasRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.SchemasRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        //hasField() for String fields simply tells you if that field is ""
        String catalog = null;
        if (msg.hasField(CATALOG_DESCRIPTOR)) {
          catalog = msg.getCatalog();
        } else if (msg.getHasCatalog()) {
          catalog = "";
        }
  
        String schemaPattern = null;
        if (msg.hasField(SCHEMA_PATTERN_DESCRIPTOR)) {
          schemaPattern = msg.getSchemaPattern();
        } else if (msg.getHasSchemaPattern()) {
          schemaPattern = "";
        }
  
        return new SchemasRequest(connectionId, catalog, schemaPattern);
      }
  
      @Override Requests.SchemasRequest serialize() {
        Requests.SchemasRequest.Builder builder = Requests.SchemasRequest.newBuilder();
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
        if (null != catalog) {
          builder.setCatalog(catalog);
          builder.setHasCatalog(true);
        }
  
        if (null != schemaPattern) {
          builder.setSchemaPattern(schemaPattern);
          builder.setHasSchemaPattern(true);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, catalog);
        result = p(result, schemaPattern);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof SchemasRequest
            && Objects.equals(connectionId, ((SchemasRequest) o).connectionId)
            && Objects.equals(catalog, ((SchemasRequest) o).catalog)
            && Objects.equals(schemaPattern, ((SchemasRequest) o).schemaPattern);
      }
    }
  
    /** Request for
     * {@link Meta#getTables(Meta.ConnectionHandle, String, org.apache.calcite.avatica.Meta.Pat, org.apache.calcite.avatica.Meta.Pat, java.util.List)}
     */
    class TablesRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.TablesRequest.
          getDescriptor().findFieldByNumber(Requests.TablesRequest.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor CATALOG_DESCRIPTOR = Requests.TablesRequest.
          getDescriptor().findFieldByNumber(Requests.TablesRequest.CATALOG_FIELD_NUMBER);
      private static final FieldDescriptor SCHEMA_PATTERN_DESCRIPTOR = Requests.TablesRequest.
          getDescriptor().findFieldByNumber(Requests.TablesRequest.SCHEMA_PATTERN_FIELD_NUMBER);
      private static final FieldDescriptor TABLE_NAME_PATTERN_DESCRIPTOR = Requests.TablesRequest.
          getDescriptor().findFieldByNumber(Requests.TablesRequest.TABLE_NAME_PATTERN_FIELD_NUMBER);
  
      public final String connectionId;
      public final String catalog;
      public final String schemaPattern;
      public final String tableNamePattern;
      public final List<String> typeList;
  
      TablesRequest() {
        connectionId = null;
        catalog = null;
        schemaPattern = null;
        tableNamePattern = null;
        typeList = null;
      }
  
      @JsonCreator
      public TablesRequest(@JsonProperty("connectionId") String connectionId,
          @JsonProperty("catalog") String catalog,
          @JsonProperty("schemaPattern") String schemaPattern,
          @JsonProperty("tableNamePattern") String tableNamePattern,
          @JsonProperty("typeList") List<String> typeList) {
        this.connectionId = connectionId;
        this.catalog = catalog;
        this.schemaPattern = schemaPattern;
        this.tableNamePattern = tableNamePattern;
        this.typeList = typeList;
      }
  
      @Override Response accept(Service service) {
        return service.apply(this);
      }
  
      @Override Request deserialize(Message genericMsg) {
        final Requests.TablesRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.TablesRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        String catalog = null;
        if (msg.hasField(CATALOG_DESCRIPTOR)) {
          catalog = msg.getCatalog();
        } else if (msg.getHasCatalog()) {
          catalog = "";
        }
  
        String schemaPattern = null;
        if (msg.hasField(SCHEMA_PATTERN_DESCRIPTOR)) {
          schemaPattern = msg.getSchemaPattern();
        } else if (msg.getHasSchemaPattern()) {
          schemaPattern = "";
        }
  
        String tableNamePattern = null;
        if (msg.hasField(TABLE_NAME_PATTERN_DESCRIPTOR)) {
          tableNamePattern = msg.getTableNamePattern();
        } else if (msg.getHasTableNamePattern()) {
          tableNamePattern = "";
        }
  
        // Cannot determine if a value was set for a repeated field. Must use an extra boolean
        // parameter to distinguish an empty and null typeList.
        List<String> typeList = null;
        if (msg.getHasTypeList()) {
          typeList = msg.getTypeListList();
        }
  
        return new TablesRequest(connectionId, catalog, schemaPattern, tableNamePattern, typeList);
      }
  
      @Override Requests.TablesRequest serialize() {
        Requests.TablesRequest.Builder builder = Requests.TablesRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
        if (null != catalog) {
          builder.setCatalog(catalog);
          builder.setHasCatalog(true);
        }
        if (null != schemaPattern) {
          builder.setSchemaPattern(schemaPattern);
          builder.setHasSchemaPattern(true);
        }
        if (null != tableNamePattern) {
          builder.setTableNamePattern(tableNamePattern);
          builder.setHasTableNamePattern(true);
        }
        if (null != typeList) {
          builder.setHasTypeList(true);
          builder.addAllTypeList(typeList);
        } else {
          builder.setHasTypeList(false);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, catalog);
        result = p(result, schemaPattern);
        result = p(result, tableNamePattern);
        result = p(result, typeList);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof TablesRequest
            && Objects.equals(connectionId, ((TablesRequest) o).connectionId)
            && Objects.equals(catalog, ((TablesRequest) o).catalog)
            && Objects.equals(schemaPattern, ((TablesRequest) o).schemaPattern)
            && Objects.equals(tableNamePattern, ((TablesRequest) o).tableNamePattern)
            && Objects.equals(typeList, ((TablesRequest) o).typeList);
      }
    }
  
    /**
     * Request for {@link Meta#getTableTypes(Meta.ConnectionHandle)}.
     */
    class TableTypesRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.TableTypesRequest.
          getDescriptor().findFieldByNumber(Requests.TableTypesRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
  
      public TableTypesRequest() {
        this.connectionId = null;
      }
  
      @JsonCreator
      public TableTypesRequest(@JsonProperty("connectionId") String connectionId) {
        this.connectionId = connectionId;
      }
  
      @Override ResultSetResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override TableTypesRequest deserialize(Message genericMsg) {
        final Requests.TableTypesRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.TableTypesRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        return new TableTypesRequest(connectionId);
      }
  
      @Override Requests.TableTypesRequest serialize() {
        Requests.TableTypesRequest.Builder builder = Requests.TableTypesRequest.newBuilder();
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof TableTypesRequest
            && Objects.equals(connectionId, ((TableTypesRequest) o).connectionId);
      }
    }
  
    /** Request for
     * {@link Meta#getColumns(Meta.ConnectionHandle, String, org.apache.calcite.avatica.Meta.Pat, org.apache.calcite.avatica.Meta.Pat, org.apache.calcite.avatica.Meta.Pat)}.
     */
    class ColumnsRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.ColumnsRequest.
          getDescriptor().findFieldByNumber(Requests.ColumnsRequest.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor CATALOG_DESCRIPTOR = Requests.ColumnsRequest.
          getDescriptor().findFieldByNumber(Requests.ColumnsRequest.CATALOG_FIELD_NUMBER);
      private static final FieldDescriptor SCHEMA_PATTERN_DESCRIPTOR = Requests.ColumnsRequest.
          getDescriptor().findFieldByNumber(Requests.ColumnsRequest.SCHEMA_PATTERN_FIELD_NUMBER);
      private static final FieldDescriptor TABLE_NAME_PATTERN_DESCRIPTOR = Requests.ColumnsRequest.
          getDescriptor().findFieldByNumber(Requests.ColumnsRequest.TABLE_NAME_PATTERN_FIELD_NUMBER);
      private static final FieldDescriptor COLUMN_NAME_PATTERN_DESCRIPTOR = Requests.ColumnsRequest.
          getDescriptor().findFieldByNumber(Requests.ColumnsRequest.COLUMN_NAME_PATTERN_FIELD_NUMBER);
  
      public final String connectionId;
      public final String catalog;
      public final String schemaPattern;
      public final String tableNamePattern;
      public final String columnNamePattern;
  
      ColumnsRequest() {
        connectionId = null;
        catalog = null;
        schemaPattern = null;
        tableNamePattern = null;
        columnNamePattern = null;
      }
  
      @JsonCreator
      public ColumnsRequest(@JsonProperty("connectionId") String connectionId,
          @JsonProperty("catalog") String catalog,
          @JsonProperty("schemaPattern") String schemaPattern,
          @JsonProperty("tableNamePattern") String tableNamePattern,
          @JsonProperty("columnNamePattern") String columnNamePattern) {
        this.connectionId = connectionId;
        this.catalog = catalog;
        this.schemaPattern = schemaPattern;
        this.tableNamePattern = tableNamePattern;
        this.columnNamePattern = columnNamePattern;
      }
  
      ResultSetResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override ColumnsRequest deserialize(Message genericMsg) {
        final Requests.ColumnsRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.ColumnsRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        String catalog = null;
        if (msg.hasField(CATALOG_DESCRIPTOR)) {
          catalog = msg.getCatalog();
        } else if (msg.getHasCatalog()) {
          catalog = "";
        }
  
        String schemaPattern = null;
        if (msg.hasField(SCHEMA_PATTERN_DESCRIPTOR)) {
          schemaPattern = msg.getSchemaPattern();
        } else if (msg.getHasSchemaPattern()) {
          schemaPattern = "";
        }
  
        String tableNamePattern = null;
        if (msg.hasField(TABLE_NAME_PATTERN_DESCRIPTOR)) {
          tableNamePattern = msg.getTableNamePattern();
        } else if (msg.getHasTableNamePattern()) {
          tableNamePattern = "";
        }
  
        String columnNamePattern = null;
        if (msg.hasField(COLUMN_NAME_PATTERN_DESCRIPTOR)) {
          columnNamePattern = msg.getColumnNamePattern();
        } else if (msg.getHasColumnNamePattern()) {
          columnNamePattern = "";
        }
  
        return new ColumnsRequest(connectionId, catalog, schemaPattern, tableNamePattern,
            columnNamePattern);
      }
  
      @Override Requests.ColumnsRequest serialize() {
        Requests.ColumnsRequest.Builder builder = Requests.ColumnsRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
        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() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, catalog);
        result = p(result, columnNamePattern);
        result = p(result, schemaPattern);
        result = p(result, tableNamePattern);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof ColumnsRequest
            && Objects.equals(connectionId, ((ColumnsRequest) o).connectionId)
            && Objects.equals(catalog, ((ColumnsRequest) o).catalog)
            && Objects.equals(schemaPattern, ((ColumnsRequest) o).schemaPattern)
            && Objects.equals(tableNamePattern, ((ColumnsRequest) o).tableNamePattern)
            && Objects.equals(columnNamePattern, ((ColumnsRequest) o).columnNamePattern);
      }
    }
  
    /** Request for
     * {@link Meta#getTypeInfo(Meta.ConnectionHandle)}. */
    class TypeInfoRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.TypeInfoRequest.
          getDescriptor().findFieldByNumber(Requests.TypeInfoRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
  
      public TypeInfoRequest() {
        connectionId = null;
      }
  
      @JsonCreator
      public TypeInfoRequest(@JsonProperty("connectionId") String connectionId) {
        this.connectionId = connectionId;
      }
  
      @Override ResultSetResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override TypeInfoRequest deserialize(Message genericMsg) {
        final Requests.TypeInfoRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.TypeInfoRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        return new TypeInfoRequest(connectionId);
      }
  
      @Override Requests.TypeInfoRequest serialize() {
        Requests.TypeInfoRequest.Builder builder = Requests.TypeInfoRequest.newBuilder();
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof TypeInfoRequest
            && Objects.equals(connectionId, ((TypeInfoRequest) o).connectionId);
      }
    }
  
    /** Response that contains a result set.
     *
     * <p>Regular result sets have {@code updateCount} -1;
     * any other value means a dummy result set that is just a count, and has
     * no signature and no other data.
     *
     * <p>Several types of request, including
     * {@link org.apache.calcite.avatica.Meta#getCatalogs(Meta.ConnectionHandle)} and
     * {@link org.apache.calcite.avatica.Meta#getSchemas(Meta.ConnectionHandle, String, org.apache.calcite.avatica.Meta.Pat)}
     * {@link Meta#getTables(Meta.ConnectionHandle, String, Meta.Pat, Meta.Pat, List)}
     * {@link Meta#getTableTypes(Meta.ConnectionHandle)}
     * return this response. */
    class ResultSetResponse extends Response {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Responses.ResultSetResponse.
          getDescriptor().findFieldByNumber(Responses.ResultSetResponse.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor SIGNATURE_DESCRIPTOR = Responses.ResultSetResponse.
          getDescriptor().findFieldByNumber(Responses.ResultSetResponse.SIGNATURE_FIELD_NUMBER);
      private static final FieldDescriptor FIRST_FRAME_DESCRIPTOR = Responses.ResultSetResponse.
          getDescriptor().findFieldByNumber(Responses.ResultSetResponse.FIRST_FRAME_FIELD_NUMBER);
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.ResultSetResponse.
          getDescriptor().findFieldByNumber(Responses.ResultSetResponse.METADATA_FIELD_NUMBER);
  
      public final String connectionId;
      public final int statementId;
      public final boolean ownStatement;
      public final Meta.Signature signature;
      public final Meta.Frame firstFrame;
      public final long updateCount;
      public final RpcMetadataResponse rpcMetadata;
  
      ResultSetResponse() {
        connectionId = null;
        statementId = 0;
        ownStatement = false;
        signature = null;
        firstFrame = null;
        updateCount = 0;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public ResultSetResponse(
          @JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId,
          @JsonProperty("ownStatement") boolean ownStatement,
          @JsonProperty("signature") Meta.Signature signature,
          @JsonProperty("firstFrame") Meta.Frame firstFrame,
          @JsonProperty("updateCount") long updateCount,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.connectionId = connectionId;
        this.statementId = statementId;
        this.ownStatement = ownStatement;
        this.signature = signature;
        this.firstFrame = firstFrame;
        this.updateCount = updateCount;
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override ResultSetResponse deserialize(Message genericMsg) {
        final Responses.ResultSetResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.ResultSetResponse.class);
  
        return fromProto(msg);
      }
  
      static ResultSetResponse fromProto(Responses.ResultSetResponse msg) {
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        Meta.Signature signature = null;
        if (msg.hasField(SIGNATURE_DESCRIPTOR)) {
          signature = Meta.Signature.fromProto(msg.getSignature());
        }
  
        Meta.Frame frame = null;
        if (msg.hasField(FIRST_FRAME_DESCRIPTOR)) {
          frame = Meta.Frame.fromProto(msg.getFirstFrame());
        }
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new ResultSetResponse(connectionId, msg.getStatementId(), msg.getOwnStatement(),
            signature, frame, msg.getUpdateCount(), metadata);
      }
  
      @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());
        }
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, firstFrame);
        result = p(result, ownStatement);
        result = p(result, signature);
        result = p(result, statementId);
        result = p(result, updateCount);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof ResultSetResponse
            && statementId == ((ResultSetResponse) o).statementId
            && ownStatement == ((ResultSetResponse) o).ownStatement
            && updateCount == ((ResultSetResponse) o).updateCount
            && Objects.equals(connectionId, ((ResultSetResponse) o).connectionId)
            && Objects.equals(firstFrame, ((ResultSetResponse) o).firstFrame)
            && Objects.equals(signature, ((ResultSetResponse) o).signature)
            && Objects.equals(rpcMetadata, ((ResultSetResponse) o).rpcMetadata);
      }
    }
  
    /** Request for
     * {@link Meta#prepareAndExecute(Meta.StatementHandle, String, long, int, Meta.PrepareCallback)}.
     */
    class PrepareAndExecuteRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.
          PrepareAndExecuteRequest.getDescriptor().findFieldByNumber(
              Requests.PrepareAndExecuteRequest.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor SQL_DESCRIPTOR = Requests.
          PrepareAndExecuteRequest.getDescriptor().findFieldByNumber(
              Requests.PrepareAndExecuteRequest.SQL_FIELD_NUMBER);
      private static final FieldDescriptor MAX_ROWS_TOTAL_DESCRIPTOR = Requests.
          PrepareAndExecuteRequest.getDescriptor().findFieldByNumber(
              Requests.PrepareAndExecuteRequest.MAX_ROWS_TOTAL_FIELD_NUMBER);
      private static final FieldDescriptor FIRST_FRAME_MAX_SIZE_DESCRIPTOR = Requests.
          PrepareAndExecuteRequest.getDescriptor().findFieldByNumber(
              Requests.PrepareAndExecuteRequest.FIRST_FRAME_MAX_SIZE_FIELD_NUMBER);
  
      public final String connectionId;
      public final String sql;
      public final long maxRowCount;
      public final int maxRowsInFirstFrame;
      public final int statementId;
  
      PrepareAndExecuteRequest() {
        connectionId = null;
        sql = null;
        maxRowCount = 0;
        maxRowsInFirstFrame = 0;
        statementId = 0;
      }
  
      public PrepareAndExecuteRequest(String connectionId, int statementId, String sql,
          long maxRowCount) {
        this(connectionId, statementId, sql, maxRowCount, AvaticaUtils.toSaturatedInt(maxRowCount));
      }
  
      @JsonCreator
      public PrepareAndExecuteRequest(
          @JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId,
          @JsonProperty("sql") String sql,
          @JsonProperty("maxRowsTotal") long maxRowCount,
          @JsonProperty("maxRowsInFirstFrame") int maxRowsInFirstFrame) {
        this.connectionId = connectionId;
        this.statementId = statementId;
        this.sql = sql;
        this.maxRowCount = maxRowCount;
        this.maxRowsInFirstFrame = maxRowsInFirstFrame;
      }
  
      @Override ExecuteResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override PrepareAndExecuteRequest deserialize(Message genericMsg) {
        final Requests.PrepareAndExecuteRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.PrepareAndExecuteRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        String sql = null;
        if (msg.hasField(SQL_DESCRIPTOR)) {
          sql = msg.getSql();
        }
  
        // Use the old attribute, unless the new is set
        long maxRowsTotal = msg.getMaxRowCount();
        if (msg.hasField(MAX_ROWS_TOTAL_DESCRIPTOR)) {
          maxRowsTotal = msg.getMaxRowsTotal();
        }
  
        // Use maxRowCount (cast to an integer) if firstFrameMaxSize isn't set
        int maxRowsInFirstFrame = (int) maxRowsTotal;
        if (msg.hasField(FIRST_FRAME_MAX_SIZE_DESCRIPTOR)) {
          maxRowsInFirstFrame = msg.getFirstFrameMaxSize();
        }
  
        return new PrepareAndExecuteRequest(connectionId, msg.getStatementId(), sql,
            maxRowsTotal, maxRowsInFirstFrame);
      }
  
      @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);
        // Set both attributes for backwards compat
        builder.setMaxRowCount(maxRowCount).setMaxRowsTotal(maxRowCount);
        builder.setFirstFrameMaxSize(maxRowsInFirstFrame);
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, maxRowCount);
        result = p(result, maxRowsInFirstFrame);
        result = p(result, sql);
        result = p(result, statementId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof PrepareAndExecuteRequest
            && statementId == ((PrepareAndExecuteRequest) o).statementId
            && maxRowCount == ((PrepareAndExecuteRequest) o).maxRowCount
            && maxRowsInFirstFrame == ((PrepareAndExecuteRequest) o).maxRowsInFirstFrame
            && Objects.equals(connectionId, ((PrepareAndExecuteRequest) o).connectionId)
            && Objects.equals(sql, ((PrepareAndExecuteRequest) o).sql);
      }
    }
  
    /** Request for
     * {@link org.apache.calcite.avatica.Meta#execute}. */
    class ExecuteRequest extends Request {
      private static final FieldDescriptor STATEMENT_HANDLE_DESCRIPTOR = Requests.ExecuteRequest.
          getDescriptor().findFieldByNumber(Requests.ExecuteRequest.STATEMENTHANDLE_FIELD_NUMBER);
      private static final FieldDescriptor DEPRECATED_FIRST_FRAME_MAX_SIZE_DESCRIPTOR =
          Requests.ExecuteRequest.getDescriptor().findFieldByNumber(
              Requests.ExecuteRequest.DEPRECATED_FIRST_FRAME_MAX_SIZE_FIELD_NUMBER);
      private static final FieldDescriptor FIRST_FRAME_MAX_SIZE_DESCRIPTOR = Requests.ExecuteRequest.
          getDescriptor().findFieldByNumber(
              Requests.ExecuteRequest.FIRST_FRAME_MAX_SIZE_FIELD_NUMBER);
      public final Meta.StatementHandle statementHandle;
      public final List<TypedValue> parameterValues;
      public final int maxRowCount;
  
      ExecuteRequest() {
        statementHandle = null;
        parameterValues = null;
        maxRowCount = 0;
      }
  
      @JsonCreator
      public ExecuteRequest(
          @JsonProperty("statementHandle") Meta.StatementHandle statementHandle,
          @JsonProperty("parameterValues") List<TypedValue> parameterValues,
          @JsonProperty("maxRowCount") int maxRowCount) {
        this.statementHandle = statementHandle;
        this.parameterValues = parameterValues;
        this.maxRowCount = maxRowCount;
      }
  
      @Override ExecuteResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override ExecuteRequest deserialize(Message genericMsg) {
        final Requests.ExecuteRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.ExecuteRequest.class);
  
        Meta.StatementHandle statementHandle = null;
        if (msg.hasField(STATEMENT_HANDLE_DESCRIPTOR)) {
          statementHandle = Meta.StatementHandle.fromProto(msg.getStatementHandle());
        }
  
        List<TypedValue> values = null;
        if (msg.getHasParameterValues()) {
          values = new ArrayList<>(msg.getParameterValuesCount());
          for (Common.TypedValue valueProto : msg.getParameterValuesList()) {
            if (TypedValue.PROTO_IMPLICIT_NULL.equals(valueProto)) {
              values.add(null);
            } else {
              values.add(TypedValue.fromProto(valueProto));
            }
          }
        }
  
        // Default int value
        int maxFrameSize = 0;
        if (msg.hasField(FIRST_FRAME_MAX_SIZE_DESCRIPTOR)) {
          maxFrameSize = msg.getFirstFrameMaxSize();
        } else if (msg.hasField(DEPRECATED_FIRST_FRAME_MAX_SIZE_DESCRIPTOR)) {
          // Truncate the long as an int
          maxFrameSize = (int) msg.getDeprecatedFirstFrameMaxSize();
        }
  
        return new ExecuteRequest(statementHandle, values, maxFrameSize);
      }
  
      @Override Requests.ExecuteRequest serialize() {
        Requests.ExecuteRequest.Builder builder = Requests.ExecuteRequest.newBuilder();
  
        if (null != statementHandle) {
          builder.setStatementHandle(statementHandle.toProto());
        }
  
        if (null != parameterValues) {
          builder.setHasParameterValues(true);
          for (TypedValue paramValue : parameterValues) {
            if (paramValue == null) {
              builder.addParameterValues(TypedValue.PROTO_IMPLICIT_NULL);
            } else {
              builder.addParameterValues(paramValue.toProto());
            }
          }
        } else {
          builder.setHasParameterValues(false);
        }
  
        // Set the old and new field
        builder.setDeprecatedFirstFrameMaxSize(maxRowCount);
        builder.setFirstFrameMaxSize(maxRowCount);
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, statementHandle);
        result = p(result, parameterValues);
        result = p(result, maxRowCount);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof ExecuteRequest
            && maxRowCount == ((ExecuteRequest) o).maxRowCount
            && Objects.equals(statementHandle, ((ExecuteRequest) o).statementHandle)
            && Objects.equals(parameterValues, ((ExecuteRequest) o).parameterValues);
      }
    }
  
    /** Response to a
     * {@link org.apache.calcite.avatica.remote.Service.PrepareAndExecuteRequest}. */
    class ExecuteResponse extends Response {
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.ExecuteResponse.
          getDescriptor().findFieldByNumber(Responses.ExecuteResponse.METADATA_FIELD_NUMBER);
      public final List<ResultSetResponse> results;
      public boolean missingStatement = false;
      public final RpcMetadataResponse rpcMetadata;
  
      ExecuteResponse() {
        results = null;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public ExecuteResponse(@JsonProperty("resultSets") List<ResultSetResponse> results,
          @JsonProperty("missingStatement") boolean missingStatement,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.results = results;
        this.missingStatement = missingStatement;
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override ExecuteResponse deserialize(Message genericMsg) {
        final Responses.ExecuteResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.ExecuteResponse.class);
  
        List<Responses.ResultSetResponse> msgResults = msg.getResultsList();
        List<ResultSetResponse> copiedResults = new ArrayList<>(msgResults.size());
  
        for (Responses.ResultSetResponse msgResult : msgResults) {
          copiedResults.add(ResultSetResponse.fromProto(msgResult));
        }
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new ExecuteResponse(copiedResults, msg.getMissingStatement(), metadata);
      }
  
      @Override Responses.ExecuteResponse serialize() {
        Responses.ExecuteResponse.Builder builder = Responses.ExecuteResponse.newBuilder();
  
        if (null != results) {
          for (ResultSetResponse result : results) {
            builder.addResults(result.serialize());
          }
        }
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.setMissingStatement(missingStatement).build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, results);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof ExecuteResponse
            && Objects.equals(results, ((ExecuteResponse) o).results)
            && Objects.equals(rpcMetadata, ((ExecuteResponse) o).rpcMetadata);
      }
    }
  
    /** Request for
     * {@link Meta#prepare(Meta.ConnectionHandle, String, long)}. */
    class PrepareRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.PrepareRequest.
          getDescriptor().findFieldByNumber(Requests.PrepareRequest.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor SQL_DESCRIPTOR = Requests.PrepareRequest.
          getDescriptor().findFieldByNumber(Requests.PrepareRequest.SQL_FIELD_NUMBER);
      private static final FieldDescriptor MAX_ROWS_TOTAL_DESCRIPTOR = Requests.PrepareRequest.
          getDescriptor().findFieldByNumber(Requests.PrepareRequest.MAX_ROWS_TOTAL_FIELD_NUMBER);
  
      public final String connectionId;
      public final String sql;
      public final long maxRowCount;
  
      PrepareRequest() {
        connectionId = null;
        sql = null;
        maxRowCount = 0;
      }
  
      @JsonCreator
      public PrepareRequest(
          @JsonProperty("connectionId") String connectionId,
          @JsonProperty("sql") String sql,
          @JsonProperty("maxRowCount") long maxRowCount) {
        this.connectionId = connectionId;
        this.sql = sql;
        this.maxRowCount = maxRowCount;
      }
  
      @Override PrepareResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override PrepareRequest deserialize(Message genericMsg) {
        final Requests.PrepareRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.PrepareRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        String sql = null;
        if (msg.hasField(SQL_DESCRIPTOR)) {
          sql = msg.getSql();
        }
  
        // Use the old field unless the new field is provided
        long totalRowsForStatement = msg.getMaxRowCount();
        if (msg.hasField(MAX_ROWS_TOTAL_DESCRIPTOR)) {
          totalRowsForStatement = msg.getMaxRowsTotal();
        }
  
        return new PrepareRequest(connectionId, sql, totalRowsForStatement);
      }
  
      @Override Requests.PrepareRequest serialize() {
        Requests.PrepareRequest.Builder builder = Requests.PrepareRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        if (null != sql) {
          builder.setSql(sql);
        }
  
        // Set both field for backwards compatibility
        return builder.setMaxRowCount(maxRowCount).setMaxRowsTotal(maxRowCount).build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, maxRowCount);
        result = p(result, sql);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof PrepareRequest
            && maxRowCount == ((PrepareRequest) o).maxRowCount
            && Objects.equals(connectionId, ((PrepareRequest) o).connectionId)
            && Objects.equals(sql, ((PrepareRequest) o).sql);
      }
    }
  
    /** Response from
     * {@link org.apache.calcite.avatica.remote.Service.PrepareRequest}. */
    class PrepareResponse extends Response {
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.PrepareResponse.
          getDescriptor().findFieldByNumber(Responses.PrepareResponse.METADATA_FIELD_NUMBER);
      public final Meta.StatementHandle statement;
      public final RpcMetadataResponse rpcMetadata;
  
      PrepareResponse() {
        statement = null;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public PrepareResponse(
          @JsonProperty("statement") Meta.StatementHandle statement,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.statement = statement;
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override PrepareResponse deserialize(Message genericMsg) {
        final Responses.PrepareResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.PrepareResponse.class);
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new PrepareResponse(Meta.StatementHandle.fromProto(msg.getStatement()), metadata);
      }
  
      @Override Responses.PrepareResponse serialize() {
        Responses.PrepareResponse.Builder builder = Responses.PrepareResponse.newBuilder();
  
        if (null != statement) {
          builder.setStatement(statement.toProto());
        }
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, statement);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof PrepareResponse
            && Objects.equals(statement, ((PrepareResponse) o).statement)
            && Objects.equals(rpcMetadata, ((PrepareResponse) o).rpcMetadata);
      }
    }
  
    /** Request for
     * {@link Meta#fetch}. */
    class FetchRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.FetchRequest.
          getDescriptor().findFieldByNumber(Requests.FetchRequest.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor FRAME_MAX_SIZE_DESCRIPTOR = Requests.FetchRequest.
          getDescriptor().findFieldByNumber(Requests.FetchRequest.FRAME_MAX_SIZE_FIELD_NUMBER);
  
      public final String connectionId;
      public final int statementId;
      public final long offset;
      /** Maximum number of rows to be returned in the frame. Negative means no
       * limit. */
      public final int fetchMaxRowCount;
  
      FetchRequest() {
        connectionId = null;
        statementId = 0;
        offset = 0;
        fetchMaxRowCount = 0;
      }
  
      @JsonCreator
      public FetchRequest(
          @JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId,
          @JsonProperty("offset") long offset,
          @JsonProperty("fetchMaxRowCount") int fetchMaxRowCount) {
        this.connectionId = connectionId;
        this.statementId = statementId;
        this.offset = offset;
        this.fetchMaxRowCount = fetchMaxRowCount;
      }
  
      @Override FetchResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override FetchRequest deserialize(Message genericMsg) {
        final Requests.FetchRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.FetchRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        int fetchMaxRowCount = msg.getFetchMaxRowCount();
        if (msg.hasField(FRAME_MAX_SIZE_DESCRIPTOR)) {
          fetchMaxRowCount = msg.getFrameMaxSize();
        }
  
        return new FetchRequest(connectionId, msg.getStatementId(), msg.getOffset(),
            fetchMaxRowCount);
      }
  
      @Override Requests.FetchRequest serialize() {
        Requests.FetchRequest.Builder builder = Requests.FetchRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        builder.setStatementId(statementId);
        builder.setOffset(offset);
        // Both fields for backwards compat
        builder.setFetchMaxRowCount(fetchMaxRowCount).setFrameMaxSize(fetchMaxRowCount);
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, fetchMaxRowCount);
        result = p(result, offset);
        result = p(result, statementId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof FetchRequest
            && statementId == ((FetchRequest) o).statementId
            && offset == ((FetchRequest) o).offset
            && fetchMaxRowCount == ((FetchRequest) o).fetchMaxRowCount
            && Objects.equals(connectionId, ((FetchRequest) o).connectionId);
      }
    }
  
    /** Response from
     * {@link org.apache.calcite.avatica.remote.Service.FetchRequest}. */
    class FetchResponse extends Response {
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.FetchResponse.
          getDescriptor().findFieldByNumber(Responses.FetchResponse.METADATA_FIELD_NUMBER);
      public final Meta.Frame frame;
      public boolean missingStatement = false;
      public boolean missingResults = false;
      public final RpcMetadataResponse rpcMetadata;
  
      FetchResponse() {
        frame = null;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public FetchResponse(@JsonProperty("frame") Meta.Frame frame,
          @JsonProperty("missingStatement") boolean missingStatement,
          @JsonProperty("missingResults") boolean missingResults,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.frame = frame;
        this.missingStatement = missingStatement;
        this.missingResults = missingResults;
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override FetchResponse deserialize(Message genericMsg) {
        final Responses.FetchResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.FetchResponse.class);
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new FetchResponse(Meta.Frame.fromProto(msg.getFrame()), msg.getMissingStatement(),
            msg.getMissingResults(), metadata);
      }
  
      @Override Responses.FetchResponse serialize() {
        Responses.FetchResponse.Builder builder = Responses.FetchResponse.newBuilder();
  
        if (null != frame) {
          builder.setFrame(frame.toProto());
        }
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.setMissingStatement(missingStatement)
            .setMissingResults(missingResults).build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, frame);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof FetchResponse
            && Objects.equals(frame, ((FetchResponse) o).frame)
            && Objects.equals(rpcMetadata, ((FetchResponse) o).rpcMetadata)
            && missingStatement == ((FetchResponse) o).missingStatement;
      }
    }
  
    /** Request for
     * {@link org.apache.calcite.avatica.Meta#createStatement(org.apache.calcite.avatica.Meta.ConnectionHandle)}. */
    class CreateStatementRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.CreateStatementRequest.
          getDescriptor().findFieldByNumber(
              Requests.CreateStatementRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
  
      CreateStatementRequest() {
        connectionId = null;
      }
  
      @JsonCreator
      public CreateStatementRequest(
          @JsonProperty("signature") String connectionId) {
        this.connectionId = connectionId;
      }
  
      @Override CreateStatementResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override CreateStatementRequest deserialize(Message genericMsg) {
        final Requests.CreateStatementRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.CreateStatementRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          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() {
        int result = 1;
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof CreateStatementRequest
            && Objects.equals(connectionId, ((CreateStatementRequest) o).connectionId);
      }
    }
  
    /** Response from
     * {@link org.apache.calcite.avatica.remote.Service.CreateStatementRequest}. */
    class CreateStatementResponse extends Response {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Responses.
          CreateStatementResponse.getDescriptor().findFieldByNumber(
              Responses.CreateStatementResponse.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.
          CreateStatementResponse.getDescriptor().findFieldByNumber(
              Responses.CreateStatementResponse.METADATA_FIELD_NUMBER);
      public final String connectionId;
      public final int statementId;
      public final RpcMetadataResponse rpcMetadata;
  
      CreateStatementResponse() {
        connectionId = null;
        statementId = 0;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public CreateStatementResponse(
          @JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.connectionId = connectionId;
        this.statementId = statementId;
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override CreateStatementResponse deserialize(Message genericMsg) {
        final Responses.CreateStatementResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.CreateStatementResponse.class);
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new CreateStatementResponse(connectionId, msg.getStatementId(), metadata);
      }
  
      @Override Responses.CreateStatementResponse serialize() {
        Responses.CreateStatementResponse.Builder builder = Responses.CreateStatementResponse
            .newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        builder.setStatementId(statementId);
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, statementId);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof CreateStatementResponse
            && statementId == ((CreateStatementResponse) o).statementId
            && Objects.equals(connectionId, ((CreateStatementResponse) o).connectionId)
            && Objects.equals(rpcMetadata, ((CreateStatementResponse) o).rpcMetadata);
      }
    }
  
    /** Request for
     * {@link org.apache.calcite.avatica.Meta#closeStatement(org.apache.calcite.avatica.Meta.StatementHandle)}. */
    class CloseStatementRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.CloseStatementRequest.
          getDescriptor().findFieldByNumber(
              Requests.CloseStatementRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
      public final int statementId;
  
      CloseStatementRequest() {
        connectionId = null;
        statementId = 0;
      }
  
      @JsonCreator
      public CloseStatementRequest(
          @JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId) {
        this.connectionId = connectionId;
        this.statementId = statementId;
      }
  
      @Override CloseStatementResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override CloseStatementRequest deserialize(Message genericMsg) {
        final Requests.CloseStatementRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.CloseStatementRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          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() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, statementId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof CloseStatementRequest
            && statementId == ((CloseStatementRequest) o).statementId
            && Objects.equals(connectionId, ((CloseStatementRequest) o).connectionId);
      }
    }
  
    /** Response from
     * {@link org.apache.calcite.avatica.remote.Service.CloseStatementRequest}. */
    class CloseStatementResponse extends Response {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Responses.
          CloseStatementResponse.getDescriptor().findFieldByNumber(
              Responses.CloseStatementResponse.METADATA_FIELD_NUMBER);
  
      public final RpcMetadataResponse rpcMetadata;
  
      public CloseStatementResponse() {
        rpcMetadata = null;
      }
  
      @JsonCreator
      public CloseStatementResponse(@JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override CloseStatementResponse deserialize(Message genericMsg) {
        final Responses.CloseStatementResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.CloseStatementResponse.class);
        RpcMetadataResponse metadata = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new CloseStatementResponse(metadata);
      }
  
      @Override Responses.CloseStatementResponse serialize() {
        Responses.CloseStatementResponse.Builder builder =
            Responses.CloseStatementResponse.newBuilder();
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof CloseStatementResponse
            && Objects.equals(rpcMetadata, ((CloseStatementResponse) o).rpcMetadata);
      }
    }
  
    /** Request for
     * {@link Meta#openConnection}. */
    class OpenConnectionRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.OpenConnectionRequest
          .getDescriptor().findFieldByNumber(
              Requests.OpenConnectionRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
      public final Map<String, String> info;
  
      public OpenConnectionRequest() {
        connectionId = null;
        info = null;
      }
  
      @JsonCreator
      public OpenConnectionRequest(@JsonProperty("connectionId") String connectionId,
          @JsonProperty("info") Map<String, String> info) {
        this.connectionId = connectionId;
        this.info = info;
      }
  
      @Override OpenConnectionResponse accept(Service service) {
        return service.apply(this);
      }
  
      /**
       * Serializes the necessary properties into a Map.
       *
       * @param props The properties to serialize.
       * @return A representation of the Properties as a Map.
       */
      public static Map<String, String> serializeProperties(Properties props) {
        Map<String, String> infoAsString = new HashMap<>();
        for (Map.Entry<Object, Object> entry : props.entrySet()) {
          // Determine if this is a property we want to forward to the server
          if (!BuiltInConnectionProperty.isLocalProperty(entry.getKey())) {
            infoAsString.put(entry.getKey().toString(), entry.getValue().toString());
          }
        }
        return infoAsString;
      }
  
      @Override Request deserialize(Message genericMsg) {
        final Requests.OpenConnectionRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.OpenConnectionRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        Map<String, String> info = msg.getInfoMap();
        if (info.isEmpty()) {
          info = null;
        }
  
        return new OpenConnectionRequest(connectionId, info);
      }
  
      @Override Message serialize() {
        Requests.OpenConnectionRequest.Builder builder = Requests.OpenConnectionRequest.newBuilder();
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
        if (null != info) {
          builder.putAllInfo(info);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, info);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof OpenConnectionRequest
            && Objects.equals(connectionId, ((OpenConnectionRequest) o).connectionId)
            && Objects.equals(info, ((OpenConnectionRequest) o).info);
      }
    }
  
    /** Response from
     * {@link org.apache.calcite.avatica.remote.Service.OpenConnectionRequest}. */
    class OpenConnectionResponse extends Response {
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.OpenConnectionResponse
          .getDescriptor().findFieldByNumber(
              Responses.OpenConnectionResponse.METADATA_FIELD_NUMBER);
      public final RpcMetadataResponse rpcMetadata;
  
      public OpenConnectionResponse() {
        rpcMetadata = null;
      }
  
      @JsonCreator
      public OpenConnectionResponse(@JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override OpenConnectionResponse deserialize(Message genericMsg) {
        final Responses.OpenConnectionResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.OpenConnectionResponse.class);
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new OpenConnectionResponse(metadata);
      }
  
      @Override Responses.OpenConnectionResponse serialize() {
        Responses.OpenConnectionResponse.Builder builder =
            Responses.OpenConnectionResponse.newBuilder();
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof OpenConnectionResponse
            && Objects.equals(rpcMetadata, ((OpenConnectionResponse) o).rpcMetadata);
      }
    }
  
    /** Request for
     * {@link Meta#closeConnection(org.apache.calcite.avatica.Meta.ConnectionHandle)}. */
    class CloseConnectionRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.CloseConnectionRequest
          .getDescriptor().findFieldByNumber(
              Requests.CloseConnectionRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
  
      CloseConnectionRequest() {
        connectionId = null;
      }
  
      @JsonCreator
      public CloseConnectionRequest(
          @JsonProperty("connectionId") String connectionId) {
        this.connectionId = connectionId;
      }
  
      @Override CloseConnectionResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override CloseConnectionRequest deserialize(Message genericMsg) {
        final Requests.CloseConnectionRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.CloseConnectionRequest.class);
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          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() {
        int result = 1;
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof CloseConnectionRequest
            && Objects.equals(connectionId, ((CloseConnectionRequest) o).connectionId);
      }
    }
  
    /** Response from
     * {@link org.apache.calcite.avatica.remote.Service.CloseConnectionRequest}. */
    class CloseConnectionResponse extends Response {
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.CloseConnectionResponse
          .getDescriptor().findFieldByNumber(
              Responses.CloseConnectionResponse.METADATA_FIELD_NUMBER);
  
      public final RpcMetadataResponse rpcMetadata;
  
      public CloseConnectionResponse() {
        rpcMetadata = null;
      }
  
      @JsonCreator
      public CloseConnectionResponse(@JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override CloseConnectionResponse deserialize(Message genericMsg) {
        final Responses.CloseConnectionResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.CloseConnectionResponse.class);
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new CloseConnectionResponse(metadata);
      }
  
      @Override Responses.CloseConnectionResponse serialize() {
        Responses.CloseConnectionResponse.Builder builder =
            Responses.CloseConnectionResponse.newBuilder();
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof CloseConnectionResponse
            && Objects.equals(rpcMetadata, ((CloseConnectionResponse) o).rpcMetadata);
      }
    }
  
    /** Request for {@link Meta#connectionSync(Meta.ConnectionHandle, Meta.ConnectionProperties)}. */
    class ConnectionSyncRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.ConnectionSyncRequest
          .getDescriptor().findFieldByNumber(
              Requests.ConnectionSyncRequest.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor CONN_PROPS_DESCRIPTOR = Requests.ConnectionSyncRequest
          .getDescriptor().findFieldByNumber(Requests.ConnectionSyncRequest.CONN_PROPS_FIELD_NUMBER);
  
      public final String connectionId;
      public final Meta.ConnectionProperties connProps;
  
      ConnectionSyncRequest() {
        connectionId = null;
        connProps = null;
      }
  
      @JsonCreator
      public ConnectionSyncRequest(
          @JsonProperty("connectionId") String connectionId,
          @JsonProperty("connProps") Meta.ConnectionProperties connProps) {
        this.connectionId = connectionId;
        this.connProps = connProps;
      }
  
      @Override ConnectionSyncResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override ConnectionSyncRequest deserialize(Message genericMsg) {
        final Requests.ConnectionSyncRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.ConnectionSyncRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        Meta.ConnectionProperties connProps = null;
        if (msg.hasField(CONN_PROPS_DESCRIPTOR)) {
          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() {
        int result = 1;
        result = p(result, connProps);
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof ConnectionSyncRequest
            && Objects.equals(connectionId, ((ConnectionSyncRequest) o).connectionId)
            && Objects.equals(connProps, ((ConnectionSyncRequest) o).connProps);
      }
    }
  
    /** Response for
     * {@link Meta#connectionSync(Meta.ConnectionHandle, Meta.ConnectionProperties)}. */
    class ConnectionSyncResponse extends Response {
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.ConnectionSyncResponse
          .getDescriptor().findFieldByNumber(Responses.ConnectionSyncResponse.METADATA_FIELD_NUMBER);
      public final Meta.ConnectionProperties connProps;
      public final RpcMetadataResponse rpcMetadata;
  
      ConnectionSyncResponse() {
        connProps = null;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public ConnectionSyncResponse(@JsonProperty("connProps") Meta.ConnectionProperties connProps,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.connProps = connProps;
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override ConnectionSyncResponse deserialize(Message genericMsg) {
        final Responses.ConnectionSyncResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.ConnectionSyncResponse.class);
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new ConnectionSyncResponse(ConnectionPropertiesImpl.fromProto(msg.getConnProps()),
            metadata);
      }
  
      @Override Responses.ConnectionSyncResponse serialize() {
        Responses.ConnectionSyncResponse.Builder builder = Responses.ConnectionSyncResponse
            .newBuilder();
  
        if (null != connProps) {
          builder.setConnProps(connProps.toProto());
        }
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connProps);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof ConnectionSyncResponse
            && Objects.equals(connProps, ((ConnectionSyncResponse) o).connProps)
            && Objects.equals(rpcMetadata, ((ConnectionSyncResponse) o).rpcMetadata);
      }
    }
  
    /** Response for
     * {@link Meta#getDatabaseProperties(Meta.ConnectionHandle)}. */
    class DatabasePropertyResponse extends Response {
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.DatabasePropertyResponse
          .getDescriptor().findFieldByNumber(
              Responses.DatabasePropertyResponse.METADATA_FIELD_NUMBER);
      public final Map<Meta.DatabaseProperty, Object> map;
      public final RpcMetadataResponse rpcMetadata;
  
      DatabasePropertyResponse() {
        map = null;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public DatabasePropertyResponse(@JsonProperty("map") Map<Meta.DatabaseProperty, Object> map,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.map = map;
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override DatabasePropertyResponse deserialize(Message genericMsg) {
        final Responses.DatabasePropertyResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.DatabasePropertyResponse.class);
        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 = (int) value.getNumberValue();
            break;
          default:
            switch (value.getType()) {
            case INTEGER:
              obj = Long.valueOf(value.getNumberValue()).intValue();
              break;
            case STRING:
              obj = value.getStringValue();
              break;
            default:
              throw new IllegalArgumentException("Unhandled value type, " + value.getType());
            }
  
            break;
          }
  
          properties.put(dbProp, obj);
        }
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new DatabasePropertyResponse(properties, metadata);
      }
  
      @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:
              if (obj instanceof Integer) {
                valueBuilder.setType(Common.Rep.INTEGER).setNumberValue((Integer) obj);
              } else {
                String value;
                if (obj instanceof String) {
                  value = (String) obj;
                } else {
                  value = obj.toString();
                }
                valueBuilder.setType(Common.Rep.STRING).setStringValue(value);
              }
  
              break;
            }
  
            builder.addProps(Responses.DatabasePropertyElement.newBuilder()
                .setKey(entry.getKey().toProto()).setValue(valueBuilder.build()));
          }
        }
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, map);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return o == this
            || o instanceof DatabasePropertyResponse
            && Objects.equals(map, ((DatabasePropertyResponse) o).map)
            && Objects.equals(rpcMetadata, ((DatabasePropertyResponse) o).rpcMetadata);
      }
    }
  
    /**
     * Response for any request that the server failed to successfully perform.
     * It is used internally by the transport layers to format errors for
     * transport over the wire. Thus, {@link Service#apply} will never return
     * an ErrorResponse.
     */
    class ErrorResponse extends Response {
      private static final FieldDescriptor ERROR_MESSAGE_DESCRIPTOR = Responses.ErrorResponse
          .getDescriptor().findFieldByNumber(
              Responses.ErrorResponse.ERROR_MESSAGE_FIELD_NUMBER);
      private static final FieldDescriptor SQL_DESCRIPTOR = Responses.ErrorResponse
          .getDescriptor().findFieldByNumber(
              Responses.ErrorResponse.SQL_STATE_FIELD_NUMBER);
      private static final FieldDescriptor SEVERITY_DESCRIPTOR = Responses.ErrorResponse
          .getDescriptor().findFieldByNumber(
              Responses.ErrorResponse.SEVERITY_FIELD_NUMBER);
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.ErrorResponse
          .getDescriptor().findFieldByNumber(
              Responses.ErrorResponse.METADATA_FIELD_NUMBER);
  
      public static final int UNKNOWN_ERROR_CODE = -1;
      public static final int MISSING_CONNECTION_ERROR_CODE = 1;
      public static final int UNAUTHORIZED_ERROR_CODE = 2;
  
      public static final String UNKNOWN_SQL_STATE = "00000";
      public static final String UNAUTHORIZED_SQL_STATE = "00002";
  
      public final List<String> exceptions;
      public final String errorMessage;
      public final int errorCode;
      public final String sqlState;
      public final AvaticaSeverity severity;
      public final RpcMetadataResponse rpcMetadata;
  
      ErrorResponse() {
        exceptions = Collections.singletonList("Unhandled exception");
        errorMessage = "Unknown message";
        errorCode = -1;
        sqlState = UNKNOWN_SQL_STATE;
        severity = AvaticaSeverity.UNKNOWN;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public ErrorResponse(@JsonProperty("exceptions") List<String> exceptions,
          @JsonProperty("errorMessage") String errorMessage,
          @JsonProperty("errorCode") int errorCode,
          @JsonProperty("sqlState") String sqlState,
          @JsonProperty("severity") AvaticaSeverity severity,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.exceptions = exceptions;
        this.errorMessage = errorMessage;
        this.errorCode = errorCode;
        this.sqlState = sqlState;
        this.severity = severity;
        this.rpcMetadata = rpcMetadata;
      }
  
      protected ErrorResponse(Exception e, String errorMessage, int code, String sqlState,
          AvaticaSeverity severity, RpcMetadataResponse rpcMetadata) {
        this(errorMessage, code, sqlState, severity, toStackTraces(e), rpcMetadata);
      }
  
      protected ErrorResponse(String errorMessage, int code, String sqlState,
          AvaticaSeverity severity, List<String> exceptions, RpcMetadataResponse rpcMetadata) {
        this.exceptions = exceptions;
        this.errorMessage = errorMessage;
        this.errorCode = code;
        this.sqlState = sqlState;
        this.severity = severity;
        this.rpcMetadata = rpcMetadata;
      }
  
      static List<String> toStackTraces(Exception e) {
        List<String> stackTraces = new ArrayList<>();
        stackTraces.add(toString(e));
        if (e instanceof SQLException) {
          SQLException next = ((SQLException) e).getNextException();
          while (null != next) {
            stackTraces.add(toString(next));
            next = next.getNextException();
          }
        }
        return stackTraces;
      }
  
      static String toString(Exception e) {
        //noinspection ThrowableResultOfMethodCallIgnored
        Objects.requireNonNull(e);
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        return sw.toString();
      }
  
      @Override ErrorResponse deserialize(Message genericMsg) {
        final Responses.ErrorResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.ErrorResponse.class);
        List<String> exceptions = null;
        if (msg.getHasExceptions()) {
          exceptions = msg.getExceptionsList();
        }
  
        String errorMessage = null;
        if (msg.hasField(ERROR_MESSAGE_DESCRIPTOR)) {
          errorMessage = msg.getErrorMessage();
        }
  
        String sqlState = null;
        if (msg.hasField(SQL_DESCRIPTOR)) {
          sqlState = msg.getSqlState();
        }
  
        AvaticaSeverity severity = null;
        if (msg.hasField(SEVERITY_DESCRIPTOR)) {
          severity = AvaticaSeverity.fromProto(msg.getSeverity());
        }
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new ErrorResponse(exceptions, errorMessage, msg.getErrorCode(), sqlState, severity,
            metadata);
      }
  
      // Public so the Jetty handler implementations can use it
      @Override public Responses.ErrorResponse serialize() {
        Responses.ErrorResponse.Builder builder = Responses.ErrorResponse.newBuilder();
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        if (null != exceptions) {
          builder.setHasExceptions(true);
          builder.addAllExceptions(exceptions);
        } else {
          builder.setHasExceptions(false);
        }
  
        if (null != errorMessage) {
          builder.setErrorMessage(errorMessage);
        }
  
        if (null != sqlState) {
          builder.setSqlState(sqlState);
        }
  
        if (null != severity) {
          builder.setSeverity(severity.toProto());
        }
  
        return builder.setErrorCode(errorCode).build();
      }
  
      @Override public String toString() {
        StringBuilder sb = new StringBuilder(32);
        sb.append("ErrorResponse[errorCode=").append(errorCode)
            .append(", sqlState=").append(sqlState)
            .append(", severity=").append(severity)
            .append(", errorMessage=").append(errorMessage)
            .append(", exceptions=").append(exceptions);
        return sb.toString();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, exceptions);
        result = p(result, errorMessage);
        result = p(result, errorCode);
        result = p(result, sqlState);
        result = p(result, severity);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof ErrorResponse
            && errorCode == ((ErrorResponse) o).errorCode
            && severity == ((ErrorResponse) o).severity
            && Objects.equals(exceptions, ((ErrorResponse) o).exceptions)
            && Objects.equals(errorMessage, ((ErrorResponse) o).errorMessage)
            && Objects.equals(sqlState, ((ErrorResponse) o).sqlState)
            && Objects.equals(rpcMetadata, ((ErrorResponse) o).rpcMetadata);
      }
  
      public AvaticaClientRuntimeException toException() {
        return new AvaticaClientRuntimeException("Remote driver error: " + errorMessage, errorCode,
            sqlState, severity, exceptions, rpcMetadata);
      }
    }
  
    /**
     * Request for {@link Service#apply(SyncResultsRequest)}
     */
    class SyncResultsRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.SyncResultsRequest
          .getDescriptor().findFieldByNumber(Requests.SyncResultsRequest.CONNECTION_ID_FIELD_NUMBER);
      private static final FieldDescriptor STATEMENT_ID_DESCRIPTOR = Requests.SyncResultsRequest
          .getDescriptor().findFieldByNumber(Requests.SyncResultsRequest.STATEMENT_ID_FIELD_NUMBER);
      private static final FieldDescriptor STATE_DESCRIPTOR = Requests.SyncResultsRequest
          .getDescriptor().findFieldByNumber(Requests.SyncResultsRequest.STATE_FIELD_NUMBER);
      private static final FieldDescriptor OFFSET_DESCRIPTOR = Requests.SyncResultsRequest
          .getDescriptor().findFieldByNumber(Requests.SyncResultsRequest.OFFSET_FIELD_NUMBER);
      public final String connectionId;
      public final int statementId;
      public final QueryState state;
      public final long offset;
  
      SyncResultsRequest() {
        this.connectionId = null;
        this.statementId = 0;
        this.state = null;
        this.offset = 0L;
      }
  
      public SyncResultsRequest(@JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId, @JsonProperty("state") QueryState state,
          @JsonProperty("offset") long offset) {
        this.connectionId = connectionId;
        this.statementId = statementId;
        this.state = state;
        this.offset = offset;
      }
  
      SyncResultsResponse accept(Service service) {
        return service.apply(this);
      }
  
      Request deserialize(Message genericMsg) {
        final Requests.SyncResultsRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.SyncResultsRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        int statementId = 0;
        if (msg.hasField(STATEMENT_ID_DESCRIPTOR)) {
          statementId = msg.getStatementId();
        }
  
        Common.QueryState state = null;
        if (msg.hasField(STATE_DESCRIPTOR)) {
          state = msg.getState();
        }
  
        long offset = 0;
        if (msg.hasField(OFFSET_DESCRIPTOR)) {
          offset = msg.getOffset();
        }
  
        return new SyncResultsRequest(connectionId, statementId,
            null == state ? null : QueryState.fromProto(msg.getState()), offset);
      }
  
      Requests.SyncResultsRequest serialize() {
        Requests.SyncResultsRequest.Builder builder = Requests.SyncResultsRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        if (null != state) {
          builder.setState(state.toProto());
        }
  
        builder.setStatementId(statementId);
        builder.setOffset(offset);
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, offset);
        result = p(result, state);
        result = p(result, statementId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof SyncResultsRequest
            && statementId == ((SyncResultsRequest) o).statementId
            && offset == ((SyncResultsRequest) o).offset
            && Objects.equals(connectionId, ((SyncResultsRequest) o).connectionId)
            && Objects.equals(state, ((SyncResultsRequest) o).state);
      }
    }
  
    /**
     * Response for {@link Service#apply(SyncResultsRequest)}.
     */
    class SyncResultsResponse extends Response {
      private static final FieldDescriptor METADATA_DESCRIPTOR = Responses.SyncResultsResponse
          .getDescriptor().findFieldByNumber(Responses.SyncResultsResponse.METADATA_FIELD_NUMBER);
      public boolean missingStatement = false;
      public final boolean moreResults;
      public final RpcMetadataResponse rpcMetadata;
  
      SyncResultsResponse() {
        this.moreResults = false;
        this.rpcMetadata = null;
      }
  
      public SyncResultsResponse(@JsonProperty("moreResults") boolean moreResults,
          @JsonProperty("missingStatement") boolean missingStatement,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.moreResults = moreResults;
        this.missingStatement = missingStatement;
        this.rpcMetadata = rpcMetadata;
      }
  
      SyncResultsResponse deserialize(Message genericMsg) {
        final Responses.SyncResultsResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.SyncResultsResponse.class);
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new SyncResultsResponse(msg.getMoreResults(), msg.getMissingStatement(), metadata);
      }
  
      Responses.SyncResultsResponse serialize() {
        Responses.SyncResultsResponse.Builder builder = Responses.SyncResultsResponse.newBuilder();
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.setMoreResults(moreResults).setMissingStatement(missingStatement).build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, missingStatement);
        result = p(result, moreResults);
        result = p(result, rpcMetadata);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof SyncResultsResponse
            && missingStatement == ((SyncResultsResponse) o).missingStatement
            && moreResults == ((SyncResultsResponse) o).moreResults
            && Objects.equals(rpcMetadata, ((SyncResultsResponse) o).rpcMetadata);
      }
    }
  
    /**
     * Response that includes information about the server that handled an RPC.
     *
     * <p>This isn't really a "response", but we want to be able to be able to convert it to protobuf
     * and back again, so ignore that there isn't an explicit endpoint for it.
     */
    class RpcMetadataResponse extends Response {
      private static final FieldDescriptor SERVER_ADDRESS_DESCRIPTOR = Responses.RpcMetadata
          .getDescriptor().findFieldByNumber(Responses.RpcMetadata.SERVER_ADDRESS_FIELD_NUMBER);
      public final String serverAddress;
      private final ByteString serverAddressAsBytes;
  
      public RpcMetadataResponse() {
        this.serverAddress = null;
        this.serverAddressAsBytes = null;
      }
  
      public RpcMetadataResponse(@JsonProperty("serverAddress") String serverAddress) {
        this.serverAddress = serverAddress;
        this.serverAddressAsBytes = UnsafeByteOperations.unsafeWrap(serverAddress.getBytes(UTF_8));
      }
  
      @Override RpcMetadataResponse deserialize(Message genericMsg) {
        final Responses.RpcMetadata msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.RpcMetadata.class);
  
        return fromProto(msg);
      }
  
      @Override Responses.RpcMetadata serialize() {
        return Responses.RpcMetadata.newBuilder().setServerAddressBytes(serverAddressAsBytes).build();
      }
  
      static RpcMetadataResponse fromProto(Responses.RpcMetadata msg) {
        String serverAddress = null;
        if (msg.hasField(SERVER_ADDRESS_DESCRIPTOR)) {
          serverAddress = msg.getServerAddress();
        }
  
        return new RpcMetadataResponse(serverAddress);
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, serverAddress);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof RpcMetadataResponse
            && Objects.equals(serverAddress, ((RpcMetadataResponse) o).serverAddress);
      }
    }
  
    /**
     * An RPC request to invoke a commit on a Connection.
     */
    class CommitRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.CommitRequest
          .getDescriptor().findFieldByNumber(Requests.CommitRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
  
      CommitRequest() {
        this.connectionId = null;
      }
  
      public CommitRequest(@JsonProperty("connectionId") String connectionId) {
        this.connectionId = connectionId;
      }
  
      @Override CommitResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override CommitRequest deserialize(Message genericMsg) {
        final Requests.CommitRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.CommitRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        return new CommitRequest(connectionId);
      }
  
      @Override Requests.CommitRequest serialize() {
        Requests.CommitRequest.Builder builder = Requests.CommitRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof CommitRequest
            && Objects.equals(connectionId, ((CommitRequest) o).connectionId);
      }
    }
  
    /**
     * An RPC response from invoking commit on a Connection.
     */
    class CommitResponse extends Response {
      private static final CommitResponse INSTANCE = new CommitResponse();
      private static final Responses.CommitResponse PB_INSTANCE =
          Responses.CommitResponse.getDefaultInstance();
  
      CommitResponse() {}
  
      @Override CommitResponse deserialize(Message genericMsg) {
        // Checks the type of genericMsg
        ProtobufService.castProtobufMessage(genericMsg, Responses.CommitResponse.class);
  
        return INSTANCE;
      }
  
      @Override Responses.CommitResponse serialize() {
        return PB_INSTANCE;
      }
  
      @Override public int hashCode() {
        return 1;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof CommitResponse;
      }
    }
  
    /**
     * An RPC request to invoke a rollback on a Connection.
     */
    class RollbackRequest extends Request {
      private static final FieldDescriptor CONNECTION_ID_DESCRIPTOR = Requests.RollbackRequest
          .getDescriptor().findFieldByNumber(Requests.RollbackRequest.CONNECTION_ID_FIELD_NUMBER);
      public final String connectionId;
  
      RollbackRequest() {
        this.connectionId = null;
      }
  
      public RollbackRequest(@JsonProperty("connectionId") String connectionId) {
        this.connectionId = connectionId;
      }
  
      @Override RollbackResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override RollbackRequest deserialize(Message genericMsg) {
        final Requests.RollbackRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.RollbackRequest.class);
  
        String connectionId = null;
        if (msg.hasField(CONNECTION_ID_DESCRIPTOR)) {
          connectionId = msg.getConnectionId();
        }
  
        return new RollbackRequest(connectionId);
      }
  
      @Override Requests.RollbackRequest serialize() {
        Requests.RollbackRequest.Builder builder = Requests.RollbackRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        return builder.build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof RollbackRequest
            && Objects.equals(connectionId, ((RollbackRequest) o).connectionId);
      }
    }
  
    /**
     * An RPC response from invoking rollback on a Connection.
     */
    class RollbackResponse extends Response {
      private static final RollbackResponse INSTANCE = new RollbackResponse();
      private static final Responses.RollbackResponse PB_INSTANCE =
          Responses.RollbackResponse.getDefaultInstance();
  
      RollbackResponse() {}
  
      @Override RollbackResponse deserialize(Message genericMsg) {
        // Check that genericMsg is the expected type
        ProtobufService.castProtobufMessage(genericMsg, Responses.RollbackResponse.class);
        return INSTANCE;
      }
  
      @Override Responses.RollbackResponse serialize() {
        return PB_INSTANCE;
      }
  
      @Override public int hashCode() {
        return 1;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof RollbackResponse;
      }
    }
  
    /**
     * Request to prepare a statement and execute a series of batch commands in one call.
     */
    class PrepareAndExecuteBatchRequest extends Request {
      public final String connectionId;
      public final List<String> sqlCommands;
      public final int statementId;
  
      PrepareAndExecuteBatchRequest() {
        connectionId = null;
        statementId = 0;
        sqlCommands = null;
      }
  
      @JsonCreator
      public PrepareAndExecuteBatchRequest(@JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId, @JsonProperty("sqlCommands") List<String>
          sqlCommands) {
        this.connectionId = connectionId;
        this.sqlCommands = sqlCommands;
        this.statementId = statementId;
      }
  
      @Override public ExecuteBatchResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override public Requests.PrepareAndExecuteBatchRequest serialize() {
        Requests.PrepareAndExecuteBatchRequest.Builder builder =
            Requests.PrepareAndExecuteBatchRequest.newBuilder();
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        if (null != sqlCommands) {
          builder.addAllSqlCommands(sqlCommands);
        }
  
        return builder.setStatementId(statementId).build();
      }
  
      @Override public PrepareAndExecuteBatchRequest deserialize(Message genericMsg) {
        final Requests.PrepareAndExecuteBatchRequest msg =
            ProtobufService.castProtobufMessage(genericMsg,
                Requests.PrepareAndExecuteBatchRequest.class);
  
        List<String> sqlCommands = new ArrayList<>(msg.getSqlCommandsList());
  
        return new PrepareAndExecuteBatchRequest(msg.getConnectionId(), msg.getStatementId(),
            sqlCommands);
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, statementId);
        result = p(result, sqlCommands);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof PrepareAndExecuteBatchRequest
            && Objects.equals(connectionId, ((PrepareAndExecuteBatchRequest) o).connectionId)
            && statementId == ((PrepareAndExecuteBatchRequest) o).statementId
            && Objects.equals(sqlCommands, ((PrepareAndExecuteBatchRequest) o).sqlCommands);
      }
    }
  
    /**
     * Request object to execute a batch of commands.
     */
    class ExecuteBatchRequest extends Request {
      public final String connectionId;
      public final int statementId;
      // Each update in a batch has a list of TypedValue's
      public final List<List<TypedValue>> parameterValues;
      // Avoid deserializing every parameter list from pb to pojo
      @JsonIgnore
      private List<Requests.UpdateBatch> protoParameterValues = null;
  
      ExecuteBatchRequest() {
        this.connectionId = null;
        this.statementId = 0;
        this.parameterValues = null;
      }
  
      @JsonCreator
      public ExecuteBatchRequest(@JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId,
          @JsonProperty("parameterValues") List<List<TypedValue>> parameterValues) {
        this.connectionId = connectionId;
        this.statementId = statementId;
        this.parameterValues = parameterValues;
      }
  
      ExecuteBatchRequest(String connectionId, int statementId) {
        this.connectionId = connectionId;
        this.statementId = statementId;
        this.parameterValues = null;
      }
  
      /**
       * Does this instance contain protobuf update batches.
       * @return True if <code>protoUpdateBatches</code> is non-null.
       */
      public boolean hasProtoUpdateBatches() {
        return null != protoParameterValues;
      }
  
      /**
       * @return The protobuf update batches.
       */
      // JsonIgnore on the getter, otherwise Jackson will try to serialize it
      @JsonIgnore
      public List<Requests.UpdateBatch> getProtoUpdateBatches() {
        return protoParameterValues;
      }
  
      @Override public ExecuteBatchResponse accept(Service service) {
        return service.apply(this);
      }
  
      @Override ExecuteBatchRequest deserialize(Message genericMsg) {
        Requests.ExecuteBatchRequest msg = ProtobufService.castProtobufMessage(genericMsg,
            Requests.ExecuteBatchRequest.class);
  
        List<Requests.UpdateBatch> updateBatches = msg.getUpdatesList();
  
        ExecuteBatchRequest pojo =
            new ExecuteBatchRequest(msg.getConnectionId(), msg.getStatementId());
        pojo.protoParameterValues = updateBatches;
        return pojo;
      }
  
      @Override Requests.ExecuteBatchRequest serialize() {
        Requests.ExecuteBatchRequest.Builder builder = Requests.ExecuteBatchRequest.newBuilder();
  
        if (hasProtoUpdateBatches()) {
          builder.addAllUpdates(protoParameterValues);
        } else if (null != parameterValues) {
          for (List<TypedValue> updateBatch : parameterValues) {
            Requests.UpdateBatch.Builder batchBuilder = Requests.UpdateBatch.newBuilder();
            for (TypedValue update : updateBatch) {
              batchBuilder.addParameterValues(update.toProto());
            }
            builder.addUpdates(batchBuilder.build());
          }
        }
  
        if (null != connectionId) {
          builder.setConnectionId(connectionId);
        }
  
        return builder.setStatementId(statementId).build();
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, statementId);
        result = p(result, parameterValues);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof ExecuteBatchRequest
            && Objects.equals(connectionId, ((ExecuteBatchRequest) o).connectionId)
            && statementId == ((ExecuteBatchRequest) o).statementId
            && Objects.equals(protoParameterValues, ((ExecuteBatchRequest) o).protoParameterValues)
            && Objects.equals(parameterValues, ((ExecuteBatchRequest) o).parameterValues);
      }
    }
  
    /**
     * Response object for executing a batch of commands.
     */
    class ExecuteBatchResponse extends Response {
      private static final FieldDescriptor RPC_METADATA_DESCRIPTOR = Responses.ExecuteBatchResponse
          .getDescriptor().findFieldByNumber(Responses.ExecuteBatchResponse.METADATA_FIELD_NUMBER);
  
      public final String connectionId;
      public final int statementId;
      public final long[] updateCounts;
      public final boolean missingStatement;
      public final RpcMetadataResponse rpcMetadata;
  
      ExecuteBatchResponse() {
        connectionId = null;
        statementId = 0;
        updateCounts = null;
        missingStatement = false;
        rpcMetadata = null;
      }
  
      @JsonCreator
      public ExecuteBatchResponse(@JsonProperty("connectionId") String connectionId,
          @JsonProperty("statementId") int statementId,
          @JsonProperty("updateCounts") long[] updateCounts,
          @JsonProperty("missingStatement") boolean missingStatement,
          @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
        this.connectionId = connectionId;
        this.statementId = statementId;
        this.updateCounts = updateCounts;
        this.missingStatement = missingStatement;
        this.rpcMetadata = rpcMetadata;
      }
  
      @Override public int hashCode() {
        int result = 1;
        result = p(result, connectionId);
        result = p(result, statementId);
        result = p(result, updateCounts);
        result = p(result, missingStatement);
        return result;
      }
  
      @Override public boolean equals(Object o) {
        return this == o
            || o instanceof ExecuteBatchResponse
            && Arrays.equals(updateCounts, ((ExecuteBatchResponse) o).updateCounts)
            && Objects.equals(connectionId, ((ExecuteBatchResponse) o).connectionId)
            && statementId == ((ExecuteBatchResponse) o).statementId
            && missingStatement == ((ExecuteBatchResponse) o).missingStatement;
      }
  
      @Override ExecuteBatchResponse deserialize(Message genericMsg) {
        Responses.ExecuteBatchResponse msg = ProtobufService.castProtobufMessage(genericMsg,
            Responses.ExecuteBatchResponse.class);
  
        long[] updateCounts = new long[msg.getUpdateCountsCount()];
        int i = 0;
        for (Long updateCount : msg.getUpdateCountsList()) {
          updateCounts[i++] = updateCount;
        }
  
        RpcMetadataResponse metadata = null;
        if (msg.hasField(RPC_METADATA_DESCRIPTOR)) {
          metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
        }
  
        return new ExecuteBatchResponse(msg.getConnectionId(), msg.getStatementId(), updateCounts,
            msg.getMissingStatement(), metadata);
      }
  
      @Override Responses.ExecuteBatchResponse serialize() {
        Responses.ExecuteBatchResponse.Builder builder = Responses.ExecuteBatchResponse.newBuilder();
  
        if (null != updateCounts) {
          for (int i = 0; i < updateCounts.length; i++) {
            builder.addUpdateCounts(updateCounts[i]);
          }
        }
  
        if (null != rpcMetadata) {
          builder.setMetadata(rpcMetadata.serialize());
        }
  
        return builder.setConnectionId(connectionId).setStatementId(statementId).build();
      }
    }
  }
  
  // End Service.java
  