calcite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From els...@apache.org
Subject [1/3] calcite git commit: [CALCITE-989] Add server's address in each response
Date Wed, 02 Dec 2015 18:06:07 GMT
Repository: calcite
Updated Branches:
  refs/heads/master 81b487684 -> 3be816f45


http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractHandler.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractHandler.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractHandler.java
index a8b76d8..9565598 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractHandler.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractHandler.java
@@ -21,6 +21,7 @@ import org.apache.calcite.avatica.NoSuchConnectionException;
 import org.apache.calcite.avatica.remote.Service.ErrorResponse;
 import org.apache.calcite.avatica.remote.Service.Request;
 import org.apache.calcite.avatica.remote.Service.Response;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
 
 import java.io.IOException;
 
@@ -33,6 +34,7 @@ import java.io.IOException;
 public abstract class AbstractHandler<T> implements Handler<T> {
   private static final String NULL_EXCEPTION_MESSAGE = "(null exception message)";
   protected final Service service;
+  private RpcMetadataResponse metadata = null;
 
   public AbstractHandler(Service service) {
     this.service = service;
@@ -78,7 +80,7 @@ public abstract class AbstractHandler<T> implements Handler<T> {
       errorMsg = getCausalChain(e);
     }
 
-    return new ErrorResponse(e, errorMsg, errorCode, sqlState, severity);
+    return new ErrorResponse(e, errorMsg, errorCode, sqlState, severity, metadata);
   }
 
   /**
@@ -143,6 +145,11 @@ public abstract class AbstractHandler<T> implements Handler<T> {
     }
     return sb.toString();
   }
+
+  @Override
+  public void setRpcMetadata(RpcMetadataResponse metadata) {
+    this.metadata = metadata;
+  }
 }
 
 // End AbstractHandler.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractService.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractService.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractService.java
index f15e0e7..ecb4adb 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractService.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/AbstractService.java
@@ -29,6 +29,8 @@ import java.util.List;
  */
 public abstract class AbstractService implements Service {
 
+  private RpcMetadataResponse rpcMetadata = null;
+
   /**
    * Represents the serialization of the data over a transport.
    */
@@ -102,7 +104,7 @@ public abstract class AbstractService implements Service {
     if (statement == response.statement) {
       return response;
     }
-    return new PrepareResponse(statement);
+    return new PrepareResponse(statement, rpcMetadata);
   }
 
   Meta.StatementHandle finagle(Meta.StatementHandle h) {
@@ -126,7 +128,7 @@ public abstract class AbstractService implements Service {
       return r;
     }
     return new ResultSetResponse(r.connectionId, r.statementId, r.ownStatement,
-        signature, r.firstFrame, r.updateCount);
+        signature, r.firstFrame, r.updateCount, rpcMetadata);
   }
 
   ExecuteResponse finagle(ExecuteResponse r) {
@@ -145,7 +147,13 @@ public abstract class AbstractService implements Service {
     if (changeCount == 0) {
       return r;
     }
-    return new ExecuteResponse(results, r.missingStatement);
+    return new ExecuteResponse(results, r.missingStatement, rpcMetadata);
+  }
+
+  @Override
+  public void setRpcMetadata(RpcMetadataResponse metadata) {
+    // OK if this is null
+    this.rpcMetadata = metadata;
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java
index f2cfeb6..e3ad07f 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.avatica.remote;
 
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
+
 import java.util.Objects;
 
 /**
@@ -53,6 +55,13 @@ public interface Handler<T> {
   }
 
   HandlerResponse<T> apply(T request);
+
+  /**
+   * Sets some general server information to return to the client in all responses.
+   *
+   * @param metadata Server-wide information
+   */
+  void setRpcMetadata(RpcMetadataResponse metadata);
 }
 
 // End Handler.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
index ff75af5..c4a5893 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
@@ -24,6 +24,7 @@ import org.apache.calcite.avatica.NoSuchStatementException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Implementation of {@link Service} that talks to a local {@link Meta}.
@@ -31,10 +32,16 @@ import java.util.List;
 public class LocalService implements Service {
   final Meta meta;
 
+  private RpcMetadataResponse serverLevelRpcMetadata;
+
   public LocalService(Meta meta) {
     this.meta = meta;
   }
 
+  @Override public void setRpcMetadata(RpcMetadataResponse serverLevelRpcMetadata) {
+    this.serverLevelRpcMetadata = Objects.requireNonNull(serverLevelRpcMetadata);
+  }
+
   private static <E> List<E> list(Iterable<E> iterable) {
     if (iterable instanceof List) {
       return (List<E>) iterable;
@@ -51,7 +58,7 @@ public class LocalService implements Service {
     if (resultSet.updateCount != -1) {
       return new ResultSetResponse(resultSet.connectionId,
           resultSet.statementId, resultSet.ownStatement, null, null,
-          resultSet.updateCount);
+          resultSet.updateCount, serverLevelRpcMetadata);
     }
 
     Meta.Signature signature = resultSet.signature;
@@ -98,7 +105,7 @@ public class LocalService implements Service {
     }
 
     return new ResultSetResponse(resultSet.connectionId, resultSet.statementId,
-        resultSet.ownStatement, signature, frame, updateCount);
+        resultSet.ownStatement, signature, frame, updateCount, serverLevelRpcMetadata);
   }
 
   private List<List<Object>> list2(Meta.MetaResultSet resultSet) {
@@ -169,7 +176,7 @@ public class LocalService implements Service {
         new Meta.ConnectionHandle(request.connectionId);
     final Meta.StatementHandle h =
         meta.prepare(ch, request.sql, request.maxRowCount);
-    return new PrepareResponse(h);
+    return new PrepareResponse(h, serverLevelRpcMetadata);
   }
 
   public ExecuteResponse apply(PrepareAndExecuteRequest request) {
@@ -197,10 +204,10 @@ public class LocalService implements Service {
       for (Meta.MetaResultSet metaResultSet : executeResult.resultSets) {
         results.add(toResponse(metaResultSet));
       }
-      return new ExecuteResponse(results, false);
+      return new ExecuteResponse(results, false, serverLevelRpcMetadata);
     } catch (NoSuchStatementException e) {
       // The Statement doesn't exist anymore, bubble up this information
-      return new ExecuteResponse(null, true);
+      return new ExecuteResponse(null, true, serverLevelRpcMetadata);
     }
   }
 
@@ -212,12 +219,12 @@ public class LocalService implements Service {
           meta.fetch(h,
               request.offset,
               request.fetchMaxRowCount);
-      return new FetchResponse(frame, false, false);
+      return new FetchResponse(frame, false, false, serverLevelRpcMetadata);
     } catch (NullPointerException | NoSuchStatementException e) {
       // The Statement doesn't exist anymore, bubble up this information
-      return new FetchResponse(null, true, true);
+      return new FetchResponse(null, true, true, serverLevelRpcMetadata);
     } catch (MissingResultsException e) {
-      return new FetchResponse(null, false, true);
+      return new FetchResponse(null, false, true, serverLevelRpcMetadata);
     }
   }
 
@@ -230,9 +237,9 @@ public class LocalService implements Service {
       for (Meta.MetaResultSet metaResultSet : executeResult.resultSets) {
         results.add(toResponse(metaResultSet));
       }
-      return new ExecuteResponse(results, false);
+      return new ExecuteResponse(results, false, serverLevelRpcMetadata);
     } catch (NoSuchStatementException e) {
-      return new ExecuteResponse(null, true);
+      return new ExecuteResponse(null, true, serverLevelRpcMetadata);
     }
   }
 
@@ -240,28 +247,28 @@ public class LocalService implements Service {
     final Meta.ConnectionHandle ch =
         new Meta.ConnectionHandle(request.connectionId);
     final Meta.StatementHandle h = meta.createStatement(ch);
-    return new CreateStatementResponse(h.connectionId, h.id);
+    return new CreateStatementResponse(h.connectionId, h.id, serverLevelRpcMetadata);
   }
 
   public CloseStatementResponse apply(CloseStatementRequest request) {
     final Meta.StatementHandle h = new Meta.StatementHandle(
         request.connectionId, request.statementId, null);
     meta.closeStatement(h);
-    return new CloseStatementResponse();
+    return new CloseStatementResponse(serverLevelRpcMetadata);
   }
 
   public OpenConnectionResponse apply(OpenConnectionRequest request) {
     final Meta.ConnectionHandle ch =
         new Meta.ConnectionHandle(request.connectionId);
     meta.openConnection(ch, request.info);
-    return new OpenConnectionResponse();
+    return new OpenConnectionResponse(serverLevelRpcMetadata);
   }
 
   public CloseConnectionResponse apply(CloseConnectionRequest request) {
     final Meta.ConnectionHandle ch =
         new Meta.ConnectionHandle(request.connectionId);
     meta.closeConnection(ch);
-    return new CloseConnectionResponse();
+    return new CloseConnectionResponse(serverLevelRpcMetadata);
   }
 
   public ConnectionSyncResponse apply(ConnectionSyncRequest request) {
@@ -269,13 +276,13 @@ public class LocalService implements Service {
         new Meta.ConnectionHandle(request.connectionId);
     final Meta.ConnectionProperties connProps =
         meta.connectionSync(ch, request.connProps);
-    return new ConnectionSyncResponse(connProps);
+    return new ConnectionSyncResponse(connProps, serverLevelRpcMetadata);
   }
 
   public DatabasePropertyResponse apply(DatabasePropertyRequest request) {
     final Meta.ConnectionHandle ch =
         new Meta.ConnectionHandle(request.connectionId);
-    return new DatabasePropertyResponse(meta.getDatabaseProperties(ch));
+    return new DatabasePropertyResponse(meta.getDatabaseProperties(ch), serverLevelRpcMetadata);
   }
 
   public SyncResultsResponse apply(SyncResultsRequest request) {
@@ -284,10 +291,11 @@ public class LocalService implements Service {
     SyncResultsResponse response;
     try {
       // Set success on the cached statement
-      response = new SyncResultsResponse(meta.syncResults(h, request.state, request.offset), false);
+      response = new SyncResultsResponse(meta.syncResults(h, request.state, request.offset), false,
+          serverLevelRpcMetadata);
     } catch (NoSuchStatementException e) {
       // Tried to sync results on a statement which wasn't cached
-      response = new SyncResultsResponse(false, true);
+      response = new SyncResultsResponse(false, true, serverLevelRpcMetadata);
     }
 
     return response;

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/main/java/org/apache/calcite/avatica/remote/MockProtobufService.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/MockProtobufService.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/MockProtobufService.java
index b04980b..2fcb19a 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/MockProtobufService.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/MockProtobufService.java
@@ -55,15 +55,15 @@ public class MockProtobufService extends ProtobufService {
     mappings.put(
         new SchemasRequest(connectionId, null, null),
         // ownStatement=false just to avoid the extra close statement call.
-        new ResultSetResponse(null, 1, false, null, Meta.Frame.EMPTY, -1));
+        new ResultSetResponse(null, 1, false, null, Meta.Frame.EMPTY, -1, null));
 
     // Get the tables, no tables exist
     mappings.put(new TablesRequest(connectionId, null, null, null, Collections.<String>emptyList()),
         // ownStatement=false just to avoid the extra close statement call.
-        new ResultSetResponse(null, 150, false, null, Meta.Frame.EMPTY, -1));
+        new ResultSetResponse(null, 150, false, null, Meta.Frame.EMPTY, -1, null));
 
     // Create a statement, get back an id
-    mappings.put(new CreateStatementRequest("0"), new CreateStatementResponse("0", 1));
+    mappings.put(new CreateStatementRequest("0"), new CreateStatementResponse("0", 1, null));
 
     // Prepare and execute a query. Values and schema are returned
     mappings.put(
@@ -77,7 +77,7 @@ public class MockProtobufService extends ProtobufService {
                 null, null, Meta.CursorFactory.ARRAY, Meta.StatementType.SELECT),
             Meta.Frame.create(0, true,
                 Arrays.<Object>asList(new Object[] {1, "a"},
-                    new Object[] {null, "b"}, new Object[] {3, "c"})), -1));
+                    new Object[] {null, "b"}, new Object[] {3, "c"})), -1, null));
 
     // Prepare a query. Schema for results are returned, but no values
     mappings.put(
@@ -90,7 +90,7 @@ public class MockProtobufService extends ProtobufService {
                     MetaImpl.columnMetaData("C2", 1, String.class)),
                 null, Collections.<AvaticaParameter>emptyList(),
                 Meta.CursorFactory.ARRAY, Meta.StatementType.SELECT),
-            null, -1));
+            null, -1, null));
 
     mappings.put(
         new ColumnsRequest(connectionId, null, null, "my_table", null),
@@ -101,7 +101,7 @@ public class MockProtobufService extends ProtobufService {
                     MetaImpl.columnMetaData("ORDINAL_POSITION", 1, Long.class)), null,
                 Collections.<AvaticaParameter>emptyList(), Meta.CursorFactory.ARRAY, null),
             Meta.Frame.create(0, true,
-                Arrays.<Object>asList(new Object[] {new Object[]{"my_table", 10}})), -1));
+                Arrays.<Object>asList(new Object[] {new Object[]{"my_table", 10}})), -1, null));
 
     return Collections.unmodifiableMap(mappings);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufTranslationImpl.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufTranslationImpl.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufTranslationImpl.java
index 9c68beb..85d3a2b 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufTranslationImpl.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufTranslationImpl.java
@@ -45,9 +45,11 @@ import org.apache.calcite.avatica.proto.Responses.FetchResponse;
 import org.apache.calcite.avatica.proto.Responses.OpenConnectionResponse;
 import org.apache.calcite.avatica.proto.Responses.PrepareResponse;
 import org.apache.calcite.avatica.proto.Responses.ResultSetResponse;
+import org.apache.calcite.avatica.proto.Responses.RpcMetadata;
 import org.apache.calcite.avatica.proto.Responses.SyncResultsResponse;
 import org.apache.calcite.avatica.remote.Service.Request;
 import org.apache.calcite.avatica.remote.Service.Response;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
 
 import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
@@ -144,6 +146,8 @@ public class ProtobufTranslationImpl implements ProtobufTranslation {
         new ResponseTranslator(ErrorResponse.parser(), new Service.ErrorResponse()));
     respParsers.put(SyncResultsResponse.class.getName(),
         new ResponseTranslator(SyncResultsResponse.parser(), new Service.SyncResultsResponse()));
+    respParsers.put(RpcMetadata.class.getName(),
+        new ResponseTranslator(RpcMetadata.parser(), new RpcMetadataResponse()));
 
     RESPONSE_PARSERS = Collections.unmodifiableMap(respParsers);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
index 473e96c..db87dff 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
@@ -69,6 +69,13 @@ public interface Service {
   ConnectionSyncResponse apply(ConnectionSyncRequest request);
   DatabasePropertyResponse apply(DatabasePropertyRequest 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);
@@ -128,7 +135,8 @@ public interface Service {
       @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 = SyncResultsResponse.class, name = "syncResults"),
+      @JsonSubTypes.Type(value = RpcMetadataResponse.class, name = "rpcMetadata") })
   abstract class Response {
     abstract Response deserialize(Message genericMsg);
     abstract Message serialize();
@@ -888,6 +896,7 @@ public interface Service {
     public final Meta.Signature signature;
     public final Meta.Frame firstFrame;
     public final long updateCount;
+    public final RpcMetadataResponse rpcMetadata;
 
     ResultSetResponse() {
       connectionId = null;
@@ -896,6 +905,7 @@ public interface Service {
       signature = null;
       firstFrame = null;
       updateCount = 0;
+      rpcMetadata = null;
     }
 
     @JsonCreator
@@ -905,13 +915,15 @@ public interface Service {
         @JsonProperty("ownStatement") boolean ownStatement,
         @JsonProperty("signature") Meta.Signature signature,
         @JsonProperty("firstFrame") Meta.Frame firstFrame,
-        @JsonProperty("updateCount") long updateCount) {
+        @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) {
@@ -943,8 +955,13 @@ public interface Service {
         frame = Meta.Frame.fromProto(msg.getFirstFrame());
       }
 
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc, Responses.ResultSetResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
       return new ResultSetResponse(connectionId, msg.getStatementId(), msg.getOwnStatement(),
-          signature, frame, msg.getUpdateCount());
+          signature, frame, msg.getUpdateCount(), metadata);
     }
 
     @Override Responses.ResultSetResponse serialize() {
@@ -964,6 +981,10 @@ public interface Service {
         builder.setFirstFrame(firstFrame.toProto());
       }
 
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
       return builder.build();
     }
 
@@ -976,6 +997,7 @@ public interface Service {
       result = prime * result + ((signature == null) ? 0 : signature.hashCode());
       result = prime * result + statementId;
       result = prime * result + (int) (updateCount ^ (updateCount >>> 32));
+      result = prime * result + ((rpcMetadata == null) ? 0 : rpcMetadata.hashCode());
       return result;
     }
 
@@ -1010,6 +1032,14 @@ public interface Service {
           return false;
         }
 
+        if (null == rpcMetadata) {
+          if (null != rpcMetadata) {
+            return false;
+          }
+        } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+          return false;
+        }
+
         return ownStatement == other.ownStatement && statementId == other.statementId
             && updateCount == other.updateCount;
       }
@@ -1250,16 +1280,20 @@ public interface Service {
   class ExecuteResponse extends Response {
     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("missingStatement") boolean missingStatement,
+        @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
       this.results = results;
       this.missingStatement = missingStatement;
+      this.rpcMetadata = rpcMetadata;
     }
 
     @Override ExecuteResponse deserialize(Message genericMsg) {
@@ -1269,6 +1303,7 @@ public interface Service {
       }
 
       Responses.ExecuteResponse msg = (Responses.ExecuteResponse) genericMsg;
+      final Descriptor desc = msg.getDescriptorForType();
 
       List<Responses.ResultSetResponse> msgResults = msg.getResultsList();
       List<ResultSetResponse> copiedResults = new ArrayList<>(msgResults.size());
@@ -1277,7 +1312,12 @@ public interface Service {
         copiedResults.add(ResultSetResponse.fromProto(msgResult));
       }
 
-      return new ExecuteResponse(copiedResults, msg.getMissingStatement());
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc, Responses.ExecuteResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
+      return new ExecuteResponse(copiedResults, msg.getMissingStatement(), metadata);
     }
 
     @Override Responses.ExecuteResponse serialize() {
@@ -1287,15 +1327,19 @@ public interface Service {
         builder.addResults(result.serialize());
       }
 
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
       return builder.setMissingStatement(missingStatement).build();
     }
 
     @Override public int hashCode() {
-      if (null == results) {
-        return 0;
-      }
-
-      return results.hashCode();
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((results == null) ? 0 : results.hashCode());
+      result = prime * result + ((rpcMetadata == null) ? 0 : rpcMetadata.hashCode());
+      return result;
     }
 
     @Override public boolean equals(Object o) {
@@ -1313,6 +1357,14 @@ public interface Service {
           return false;
         }
 
+        if (null == rpcMetadata) {
+          if (null != other.rpcMetadata) {
+            return false;
+          }
+        } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+          return false;
+        }
+
         return true;
       }
 
@@ -1426,15 +1478,19 @@ public interface Service {
    * {@link org.apache.calcite.avatica.remote.Service.PrepareRequest}. */
   class PrepareResponse extends Response {
     public final Meta.StatementHandle statement;
+    public final RpcMetadataResponse rpcMetadata;
 
     PrepareResponse() {
       statement = null;
+      rpcMetadata = null;
     }
 
     @JsonCreator
     public PrepareResponse(
-        @JsonProperty("statement") Meta.StatementHandle statement) {
+        @JsonProperty("statement") Meta.StatementHandle statement,
+        @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
       this.statement = statement;
+      this.rpcMetadata = rpcMetadata;
     }
 
     @Override PrepareResponse deserialize(Message genericMsg) {
@@ -1444,8 +1500,14 @@ public interface Service {
       }
 
       Responses.PrepareResponse msg = (Responses.PrepareResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
+
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc, Responses.PrepareResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
 
-      return new PrepareResponse(Meta.StatementHandle.fromProto(msg.getStatement()));
+      return new PrepareResponse(Meta.StatementHandle.fromProto(msg.getStatement()), metadata);
     }
 
     @Override Responses.PrepareResponse serialize() {
@@ -1455,6 +1517,10 @@ public interface Service {
         builder.setStatement(statement.toProto());
       }
 
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
       return builder.build();
     }
 
@@ -1462,6 +1528,7 @@ public interface Service {
       final int prime = 31;
       int result = 1;
       result = prime * result + ((statement == null) ? 0 : statement.hashCode());
+      result = prime * result + ((null == rpcMetadata) ? 0 : rpcMetadata.hashCode());
       return result;
     }
 
@@ -1480,6 +1547,14 @@ public interface Service {
           return false;
         }
 
+        if (null == rpcMetadata) {
+          if (null != other.rpcMetadata) {
+            return false;
+          }
+        } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+          return false;
+        }
+
         return true;
       }
 
@@ -1592,18 +1667,22 @@ public interface Service {
     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("missingResults") boolean missingResults,
+        @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
       this.frame = frame;
       this.missingStatement = missingStatement;
       this.missingResults = missingResults;
+      this.rpcMetadata = rpcMetadata;
     }
 
     @Override FetchResponse deserialize(Message genericMsg) {
@@ -1613,9 +1692,15 @@ public interface Service {
       }
 
       Responses.FetchResponse msg = (Responses.FetchResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
+
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc, Responses.FetchResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
 
       return new FetchResponse(Meta.Frame.fromProto(msg.getFrame()), msg.getMissingStatement(),
-          msg.getMissingResults());
+          msg.getMissingResults(), metadata);
     }
 
     @Override Responses.FetchResponse serialize() {
@@ -1625,6 +1710,10 @@ public interface Service {
         builder.setFrame(frame.toProto());
       }
 
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
       return builder.setMissingStatement(missingStatement)
           .setMissingResults(missingResults).build();
     }
@@ -1633,6 +1722,7 @@ public interface Service {
       final int prime = 31;
       int result = 1;
       result = prime * result + ((frame == null) ? 0 : frame.hashCode());
+      result = prime * result + ((null == rpcMetadata) ? 0 : rpcMetadata.hashCode());
       return result;
     }
 
@@ -1651,6 +1741,14 @@ public interface Service {
           return false;
         }
 
+        if (null == rpcMetadata) {
+          if (null != other.rpcMetadata) {
+            return false;
+          }
+        } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+          return false;
+        }
+
         return missingStatement == other.missingStatement;
       }
 
@@ -1740,18 +1838,22 @@ public interface Service {
   class CreateStatementResponse extends Response {
     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("statementId") int statementId,
+        @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
       this.connectionId = connectionId;
       this.statementId = statementId;
+      this.rpcMetadata = rpcMetadata;
     }
 
     @Override CreateStatementResponse deserialize(Message genericMsg) {
@@ -1769,7 +1871,13 @@ public interface Service {
         connectionId = msg.getConnectionId();
       }
 
-      return new CreateStatementResponse(connectionId, msg.getStatementId());
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.CreateStatementResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
+      return new CreateStatementResponse(connectionId, msg.getStatementId(), metadata);
     }
 
     @Override Responses.CreateStatementResponse serialize() {
@@ -1780,6 +1888,10 @@ public interface Service {
         builder.setConnectionId(connectionId);
       }
 
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
       builder.setStatementId(statementId);
 
       return builder.build();
@@ -1790,6 +1902,7 @@ public interface Service {
       int result = 1;
       result = prime * result + ((connectionId == null) ? 0 : connectionId.hashCode());
       result = prime * result + statementId;
+      result = prime * result + ((null == rpcMetadata) ? 0 : rpcMetadata.hashCode());
       return result;
     }
 
@@ -1808,6 +1921,14 @@ public interface Service {
           return false;
         }
 
+        if (null == rpcMetadata) {
+          if (null != other.rpcMetadata) {
+            return false;
+          }
+        } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+          return false;
+        }
+
         return statementId == other.statementId;
       }
 
@@ -1899,8 +2020,16 @@ public interface Service {
   /** Response from
    * {@link org.apache.calcite.avatica.remote.Service.CloseStatementRequest}. */
   class CloseStatementResponse extends Response {
+    public final RpcMetadataResponse rpcMetadata;
+
+    public CloseStatementResponse() {
+      rpcMetadata = null;
+    }
+
     @JsonCreator
-    public CloseStatementResponse() {}
+    public CloseStatementResponse(@JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
+      this.rpcMetadata = rpcMetadata;
+    }
 
     @Override CloseStatementResponse deserialize(Message genericMsg) {
       if (!(genericMsg instanceof Responses.CloseStatementResponse)) {
@@ -1908,22 +2037,52 @@ public interface Service {
             "Expected CloseStatementResponse, but got " + genericMsg.getClass().getName());
       }
 
-      return new CloseStatementResponse();
+      Responses.CloseStatementResponse msg = (Responses.CloseStatementResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
+
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.CloseStatementResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
+      return new CloseStatementResponse(metadata);
     }
 
     @Override Responses.CloseStatementResponse serialize() {
-      return Responses.CloseStatementResponse.newBuilder().build();
+      Responses.CloseStatementResponse.Builder builder =
+          Responses.CloseStatementResponse.newBuilder();
+
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
+      return builder.build();
     }
 
     @Override public int hashCode() {
-      return 0;
+      return (null == rpcMetadata) ? 0 : rpcMetadata.hashCode();
     }
 
     @Override public boolean equals(Object o) {
       if (o == this) {
         return true;
       }
-      return o instanceof CloseStatementResponse;
+      if (o == null || !(o instanceof CloseStatementResponse)) {
+        return false;
+      }
+
+      CloseStatementResponse other = (CloseStatementResponse) o;
+
+      if (null == rpcMetadata) {
+        if (null != other.rpcMetadata) {
+          return false;
+        }
+      } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+        return false;
+      }
+
+      return true;
     }
   }
 
@@ -2050,9 +2209,15 @@ public interface Service {
   /** Response from
    * {@link org.apache.calcite.avatica.remote.Service.OpenConnectionRequest}. */
   class OpenConnectionResponse extends Response {
+    public final RpcMetadataResponse rpcMetadata;
 
-    @JsonCreator
     public OpenConnectionResponse() {
+      rpcMetadata = null;
+    }
+
+    @JsonCreator
+    public OpenConnectionResponse(@JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
+      this.rpcMetadata = rpcMetadata;
     }
 
     @Override OpenConnectionResponse deserialize(Message genericMsg) {
@@ -2061,22 +2226,50 @@ public interface Service {
             "Expected OpenConnectionResponse, but got " + genericMsg.getClass().getName());
       }
 
-      return new OpenConnectionResponse();
+      Responses.OpenConnectionResponse msg = (Responses.OpenConnectionResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
+
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.OpenConnectionResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
+      return new OpenConnectionResponse(metadata);
     }
 
     @Override Responses.OpenConnectionResponse serialize() {
-      return Responses.OpenConnectionResponse.newBuilder().build();
+      Responses.OpenConnectionResponse.Builder builder =
+          Responses.OpenConnectionResponse.newBuilder();
+
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
+      return builder.build();
     }
 
     @Override public int hashCode() {
-      return 0;
+      return (null == rpcMetadata) ? 0 : rpcMetadata.hashCode();
     }
 
     @Override public boolean equals(Object o) {
       if (o == this) {
         return true;
       }
-      return o instanceof OpenConnectionResponse;
+      if (null == o || !(o instanceof OpenConnectionResponse)) {
+        return false;
+      }
+
+      OpenConnectionResponse other = (OpenConnectionResponse) o;
+      if (null == rpcMetadata) {
+        if (null != rpcMetadata) {
+          return false;
+        }
+      } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+        return false;
+      }
+      return true;
     }
   }
 
@@ -2160,9 +2353,16 @@ public interface Service {
   /** Response from
    * {@link org.apache.calcite.avatica.remote.Service.CloseConnectionRequest}. */
   class CloseConnectionResponse extends Response {
+    public final RpcMetadataResponse rpcMetadata;
+
+    public CloseConnectionResponse() {
+      rpcMetadata = null;
+    }
 
     @JsonCreator
-    public CloseConnectionResponse() {}
+    public CloseConnectionResponse(@JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
+      this.rpcMetadata = rpcMetadata;
+    }
 
     @Override CloseConnectionResponse deserialize(Message genericMsg) {
       if (!(genericMsg instanceof Responses.CloseConnectionResponse)) {
@@ -2170,22 +2370,51 @@ public interface Service {
             "Expected CloseConnectionResponse, but got " + genericMsg.getClass().getName());
       }
 
-      return new CloseConnectionResponse();
+      Responses.CloseConnectionResponse msg = (Responses.CloseConnectionResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
+
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.CloseConnectionResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
+      return new CloseConnectionResponse(metadata);
     }
 
     @Override Responses.CloseConnectionResponse serialize() {
-      return Responses.CloseConnectionResponse.newBuilder().build();
+      Responses.CloseConnectionResponse.Builder builder =
+          Responses.CloseConnectionResponse.newBuilder();
+
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
+      return builder.build();
     }
 
     @Override public int hashCode() {
-      return 0;
+      return (null == rpcMetadata) ? 0 : rpcMetadata.hashCode();
     }
 
     @Override public boolean equals(Object o) {
       if (o == this) {
         return true;
       }
-      return o instanceof CloseConnectionResponse;
+      if (o == null || !(o instanceof CloseConnectionResponse)) {
+        return false;
+      }
+      CloseConnectionResponse other = (CloseConnectionResponse) o;
+
+      if (null == rpcMetadata) {
+        if (null != other.rpcMetadata) {
+          return false;
+        }
+      } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+        return false;
+      }
+
+      return true;
     }
   }
 
@@ -2291,14 +2520,18 @@ public interface Service {
    * {@link Meta#connectionSync(Meta.ConnectionHandle, Meta.ConnectionProperties)}. */
   class ConnectionSyncResponse extends Response {
     public final Meta.ConnectionProperties connProps;
+    public final RpcMetadataResponse rpcMetadata;
 
     ConnectionSyncResponse() {
       connProps = null;
+      rpcMetadata = null;
     }
 
     @JsonCreator
-    public ConnectionSyncResponse(@JsonProperty("connProps") Meta.ConnectionProperties connProps) {
+    public ConnectionSyncResponse(@JsonProperty("connProps") Meta.ConnectionProperties connProps,
+        @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
       this.connProps = connProps;
+      this.rpcMetadata = rpcMetadata;
     }
 
     @Override ConnectionSyncResponse deserialize(Message genericMsg) {
@@ -2308,8 +2541,16 @@ public interface Service {
       }
 
       Responses.ConnectionSyncResponse msg = (Responses.ConnectionSyncResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
 
-      return new ConnectionSyncResponse(ConnectionPropertiesImpl.fromProto(msg.getConnProps()));
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.ConnectionSyncResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
+      return new ConnectionSyncResponse(ConnectionPropertiesImpl.fromProto(msg.getConnProps()),
+          metadata);
     }
 
     @Override Responses.ConnectionSyncResponse serialize() {
@@ -2320,15 +2561,19 @@ public interface Service {
         builder.setConnProps(connProps.toProto());
       }
 
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
       return builder.build();
     }
 
     @Override public int hashCode() {
-      if (null == connProps) {
-        return 0;
-      }
-
-      return connProps.hashCode();
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((connProps == null) ? 0 : connProps.hashCode());
+      result = prime * result + ((rpcMetadata == null) ? 0 : rpcMetadata.hashCode());
+      return result;
     }
 
     @Override public boolean equals(Object o) {
@@ -2346,6 +2591,14 @@ public interface Service {
           return false;
         }
 
+        if (null == rpcMetadata) {
+          if (null != other.rpcMetadata) {
+            return false;
+          }
+        } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+          return false;
+        }
+
         return true;
       }
 
@@ -2357,14 +2610,18 @@ public interface Service {
    * {@link Meta#getDatabaseProperties(Meta.ConnectionHandle)}. */
   class DatabasePropertyResponse extends Response {
     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) {
+    public DatabasePropertyResponse(@JsonProperty("map") Map<Meta.DatabaseProperty, Object> map,
+        @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
       this.map = map;
+      this.rpcMetadata = rpcMetadata;
     }
 
     @Override DatabasePropertyResponse deserialize(Message genericMsg) {
@@ -2374,6 +2631,7 @@ public interface Service {
       }
 
       Responses.DatabasePropertyResponse msg = (Responses.DatabasePropertyResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
 
       HashMap<Meta.DatabaseProperty, Object> properties = new HashMap<>();
       for (Responses.DatabasePropertyElement property : msg.getPropsList()) {
@@ -2410,7 +2668,13 @@ public interface Service {
         properties.put(dbProp, obj);
       }
 
-      return new DatabasePropertyResponse(properties);
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.DatabasePropertyResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
+      return new DatabasePropertyResponse(properties, metadata);
     }
 
     @Override Responses.DatabasePropertyResponse serialize() {
@@ -2453,15 +2717,19 @@ public interface Service {
         }
       }
 
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
       return builder.build();
     }
 
     @Override public int hashCode() {
-      if (null == map) {
-        return 0;
-      }
-
-      return map.hashCode();
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((map == null) ? 0 : map.hashCode());
+      result = prime * result + ((rpcMetadata == null) ? 0 : rpcMetadata.hashCode());
+      return result;
     }
 
     @Override public boolean equals(Object o) {
@@ -2479,6 +2747,14 @@ public interface Service {
           return false;
         }
 
+        if (null == rpcMetadata) {
+          if (null != other.rpcMetadata) {
+            return false;
+          }
+        } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+          return false;
+        }
+
         return true;
       }
 
@@ -2489,7 +2765,7 @@ public interface Service {
   /**
    * 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 Request#apply} will never return
+   * transport over the wire. Thus, {@link Service#apply} will never return
    * an ErrorResponse.
    */
   public class ErrorResponse extends Response {
@@ -2503,6 +2779,7 @@ public interface Service {
     public final int errorCode;
     public final String sqlState;
     public final AvaticaSeverity severity;
+    public final RpcMetadataResponse rpcMetadata;
 
     ErrorResponse() {
       exceptions = Collections.singletonList("Unhandled exception");
@@ -2510,6 +2787,7 @@ public interface Service {
       errorCode = -1;
       sqlState = UNKNOWN_SQL_STATE;
       severity = AvaticaSeverity.UNKNOWN;
+      rpcMetadata = null;
     }
 
     @JsonCreator
@@ -2517,26 +2795,29 @@ public interface Service {
         @JsonProperty("errorMessage") String errorMessage,
         @JsonProperty("errorCode") int errorCode,
         @JsonProperty("sqlState") String sqlState,
-        @JsonProperty("severity") AvaticaSeverity severity) {
+        @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) {
-      this(errorMessage, code, sqlState, severity, toStackTraces(e));
+        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) {
+        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) {
@@ -2565,12 +2846,25 @@ public interface Service {
       }
 
       Responses.ErrorResponse msg = (Responses.ErrorResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
+
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc, Responses.ErrorResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
+
       return new ErrorResponse(msg.getExceptionsList(), msg.getErrorMessage(),
-          msg.getErrorCode(), msg.getSqlState(), AvaticaSeverity.fromProto(msg.getSeverity()));
+          msg.getErrorCode(), msg.getSqlState(), AvaticaSeverity.fromProto(msg.getSeverity()),
+          metadata);
     }
 
     @Override Responses.ErrorResponse serialize() {
       Responses.ErrorResponse.Builder builder = Responses.ErrorResponse.newBuilder();
+
+      if (null != rpcMetadata) {
+        builder.setMetadata(rpcMetadata.serialize());
+      }
+
       return builder.addAllExceptions(exceptions).setErrorMessage(errorMessage)
           .setErrorCode(errorCode).setSqlState(sqlState).setSeverity(severity.toProto()).build();
     }
@@ -2582,6 +2876,7 @@ public interface Service {
       result = prime * result + errorCode;
       result = prime * result + ((sqlState == null) ? 0 : sqlState.hashCode());
       result = prime * result + ((severity == null) ? 0 : severity.hashCode());
+      result = prime * result + ((rpcMetadata == null) ? 0 : rpcMetadata.hashCode());
       return result;
     }
 
@@ -2626,12 +2921,20 @@ public interface Service {
         return false;
       }
 
+      if (null == rpcMetadata) {
+        if (null != other.rpcMetadata) {
+          return false;
+        }
+      } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+        return false;
+      }
+
       return true;
     }
 
     public AvaticaClientRuntimeException toException() {
       return new AvaticaClientRuntimeException("Remote driver error: " + errorMessage, errorCode,
-          sqlState, severity, exceptions);
+          sqlState, severity, exceptions, rpcMetadata);
     }
   }
 
@@ -2748,15 +3051,19 @@ public interface Service {
   class SyncResultsResponse extends Response {
     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("missingStatement") boolean missingStatement,
+        @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
       this.moreResults = moreResults;
       this.missingStatement = missingStatement;
+      this.rpcMetadata = rpcMetadata;
     }
 
     SyncResultsResponse deserialize(Message genericMsg) {
@@ -2766,13 +3073,24 @@ public interface Service {
       }
 
       Responses.SyncResultsResponse msg = (Responses.SyncResultsResponse) genericMsg;
+      Descriptor desc = msg.getDescriptorForType();
+
+      RpcMetadataResponse metadata = null;
+      if (ProtobufService.hasField(msg, desc,
+          Responses.SyncResultsResponse.METADATA_FIELD_NUMBER)) {
+        metadata = RpcMetadataResponse.fromProto(msg.getMetadata());
+      }
 
-      return new SyncResultsResponse(msg.getMoreResults(), msg.getMissingStatement());
+      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();
     }
 
@@ -2781,6 +3099,7 @@ public interface Service {
       int result = 1;
       result = prime * result + (missingStatement ? 1231 : 1237);
       result = prime * result + (moreResults ? 1231 : 1237);
+      result = prime * result + ((rpcMetadata == null) ? 0 : rpcMetadata.hashCode());
       return result;
     }
 
@@ -2794,9 +3113,88 @@ public interface Service {
 
       SyncResultsResponse other = (SyncResultsResponse) obj;
 
+      if (null == rpcMetadata) {
+        if (null != other.rpcMetadata) {
+          return false;
+        }
+      } else if (!rpcMetadata.equals(other.rpcMetadata)) {
+        return false;
+      }
+
       return missingStatement == other.missingStatement && moreResults == other.moreResults;
     }
   }
+
+  /**
+   * Response that includes information about the server that handled an RPC.
+   *
+   * 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.
+   */
+  public class RpcMetadataResponse extends Response {
+    public final String serverAddress;
+
+    public RpcMetadataResponse() {
+      this.serverAddress = null;
+    }
+
+    public RpcMetadataResponse(@JsonProperty("serverAddress") String serverAddress) {
+      this.serverAddress = serverAddress;
+    }
+
+    @Override
+    RpcMetadataResponse deserialize(Message genericMsg) {
+      if (!(genericMsg instanceof Responses.RpcMetadata)) {
+        throw new IllegalArgumentException("Expected RpcMetadata, but got "
+            + genericMsg.getClass().getName());
+      }
+
+      return fromProto((Responses.RpcMetadata) genericMsg);
+    }
+
+    @Override
+    Responses.RpcMetadata serialize() {
+      return Responses.RpcMetadata.newBuilder().setServerAddress(serverAddress).build();
+    }
+
+    static RpcMetadataResponse fromProto(Responses.RpcMetadata msg) {
+      Descriptor desc = msg.getDescriptorForType();
+
+      String serverAddress = null;
+      if (ProtobufService.hasField(msg, desc, Responses.RpcMetadata.SERVER_ADDRESS_FIELD_NUMBER)) {
+        serverAddress = msg.getServerAddress();
+      }
+
+      return new RpcMetadataResponse(serverAddress);
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((serverAddress == null) ? 0 : serverAddress.hashCode());
+      return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+
+      if (obj == null || !(obj instanceof RpcMetadataResponse)) {
+        return false;
+      }
+
+      RpcMetadataResponse other = (RpcMetadataResponse) obj;
+      if (serverAddress == null) {
+        if (other.serverAddress != null) {
+          return false;
+        }
+      }
+      return serverAddress.equals(other.serverAddress);
+    }
+  }
 }
 
 // End Service.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/main/protobuf/responses.proto
----------------------------------------------------------------------
diff --git a/avatica/src/main/protobuf/responses.proto b/avatica/src/main/protobuf/responses.proto
index a899513..98ccf7f 100644
--- a/avatica/src/main/protobuf/responses.proto
+++ b/avatica/src/main/protobuf/responses.proto
@@ -29,17 +29,20 @@ message ResultSetResponse {
   Frame first_frame = 5;
   uint64 update_count = 6; // -1 for normal result sets, else this response contains a dummy result set
                                     // with no signature nor other data.
+  RpcMetadata metadata = 7;
 }
 
 // Response to PrepareAndExecuteRequest
 message ExecuteResponse {
   repeated ResultSetResponse results = 1;
   bool missing_statement = 2; // Did the request fail because of no-cached statement
+  RpcMetadata metadata = 3;
 }
 
 // Response to PrepareRequest
 message PrepareResponse {
   StatementHandle statement = 1;
+  RpcMetadata metadata = 2;
 }
 
 // Response to FetchRequest
@@ -47,42 +50,47 @@ message FetchResponse {
   Frame frame = 1;
   bool missing_statement = 2; // Did the request fail because of no-cached statement
   bool missing_results = 3; // Did the request fail because of a cached-statement w/o ResultSet
+  RpcMetadata metadata = 4;
 }
 
 // Response to CreateStatementRequest
 message CreateStatementResponse {
   string connection_id = 1;
   uint32 statement_id = 2;
+  RpcMetadata metadata = 3;
 }
 
 // Response to CloseStatementRequest
 message CloseStatementResponse {
-
+  RpcMetadata metadata = 1;
 }
 
 // Response to OpenConnectionRequest {
 message OpenConnectionResponse {
-
+  RpcMetadata metadata = 1;
 }
 
 // Response to CloseConnectionRequest {
 message CloseConnectionResponse {
-
+  RpcMetadata metadata = 1;
 }
 
 // Response to ConnectionSyncRequest
 message ConnectionSyncResponse {
   ConnectionProperties conn_props = 1;
+  RpcMetadata metadata = 2;
 }
 
 message DatabasePropertyElement {
   DatabaseProperty key = 1;
   TypedValue value = 2;
+  RpcMetadata metadata = 3;
 }
 
 // Response for Meta#getDatabaseProperties()
 message DatabasePropertyResponse {
   repeated DatabasePropertyElement props = 1;
+  RpcMetadata metadata = 2;
 }
 
 // Send contextual information about some error over the wire from the server.
@@ -92,9 +100,16 @@ message ErrorResponse {
   Severity severity = 3;
   uint32 error_code = 4; // numeric identifier for error
   string sql_state = 5; // five-character standard-defined value
+  RpcMetadata metadata = 6;
 }
 
 message SyncResultsResponse {
   bool missing_statement = 1; // Server doesn't have the statement with the ID from the request
   bool more_results = 2; // Should the client fetch() to get more results
+  RpcMetadata metadata = 3;
 }
+
+// Generic metadata for the server to return with each response.
+message RpcMetadata {
+  string server_address = 1; // The host:port of the server
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java b/avatica/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java
index 7ee6fcf..f66cb26 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java
@@ -19,6 +19,7 @@ package org.apache.calcite.avatica.remote;
 import org.apache.calcite.avatica.AvaticaClientRuntimeException;
 import org.apache.calcite.avatica.AvaticaSeverity;
 import org.apache.calcite.avatica.remote.Service.ErrorResponse;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
 
 import org.junit.Test;
 
@@ -39,8 +40,9 @@ public class ErrorResponseTest {
     final String state = "a1b2c";
     final AvaticaSeverity severity = AvaticaSeverity.ERROR;
     final List<String> exceptions = Arrays.asList("Server Stacktrace 1", "Server Stacktace 2");
-    assertEquals(new ErrorResponse(message, code, state, severity, exceptions),
-        new ErrorResponse(message, code, state, severity, exceptions));
+    final RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
+    assertEquals(new ErrorResponse(message, code, state, severity, exceptions, metadata),
+        new ErrorResponse(message, code, state, severity, exceptions, metadata));
   }
 
   @Test public void testToClientRTE() {
@@ -49,7 +51,9 @@ public class ErrorResponseTest {
     final String state = "a1b2c";
     final AvaticaSeverity severity = AvaticaSeverity.ERROR;
     final List<String> exceptions = Arrays.asList("Server Stacktrace 1", "Server Stacktace 2");
-    final ErrorResponse resp = new ErrorResponse(message, code, state, severity, exceptions);
+    final RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
+    final ErrorResponse resp = new ErrorResponse(message, code, state, severity, exceptions,
+        metadata);
     AvaticaClientRuntimeException exception = resp.toException();
     assertTrue("Expected error message to end with '" + resp.errorMessage + "', but was '"
         + exception.getMessage() + "'", exception.getMessage().endsWith(resp.errorMessage));
@@ -57,6 +61,7 @@ public class ErrorResponseTest {
     assertEquals(resp.severity, exception.getSeverity());
     assertEquals(resp.sqlState, exception.getSqlState());
     assertEquals(resp.exceptions, exception.getServerExceptions());
+    assertEquals(resp.rpcMetadata, exception.getRpcMetadata());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java b/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
index 3c25e13..7d24598 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
@@ -24,6 +24,7 @@ import org.apache.calcite.avatica.proto.Responses;
 import org.apache.calcite.avatica.remote.Handler.HandlerResponse;
 import org.apache.calcite.avatica.remote.Service.FetchRequest;
 import org.apache.calcite.avatica.remote.Service.FetchResponse;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -84,7 +85,8 @@ public class ProtobufHandlerTest {
     frameRows.add(new Object[] {true, "my_string"});
 
     Meta.Frame frame = Frame.create(0, true, frameRows);
-    FetchResponse response = new FetchResponse(frame, false, false);
+    RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
+    FetchResponse response = new FetchResponse(frame, false, false, metadata);
 
     when(translation.parseRequest(serializedRequest)).thenReturn(request);
     when(service.apply(request)).thenReturn(response);

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java b/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
index 3ca44e9..8e0d738 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
@@ -51,6 +51,7 @@ import org.apache.calcite.avatica.remote.Service.PrepareResponse;
 import org.apache.calcite.avatica.remote.Service.Request;
 import org.apache.calcite.avatica.remote.Service.Response;
 import org.apache.calcite.avatica.remote.Service.ResultSetResponse;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
 import org.apache.calcite.avatica.remote.Service.SchemasRequest;
 import org.apache.calcite.avatica.remote.Service.SyncResultsRequest;
 import org.apache.calcite.avatica.remote.Service.SyncResultsResponse;
@@ -248,6 +249,7 @@ public class ProtobufTranslationImplTest<T> {
    * Generates a collection of Responses whose serialization will be tested.
    */
   private static List<Response> getResponses() {
+    final RpcMetadataResponse rpcMetadata = new RpcMetadataResponse("localhost:8765");
     LinkedList<Response> responses = new LinkedList<>();
 
     // Nested classes (Signature, ColumnMetaData, CursorFactory, etc) are implicitly getting tested)
@@ -273,48 +275,49 @@ public class ProtobufTranslationImplTest<T> {
 
     // And then create a ResultSetResponse
     ResultSetResponse results1 = new ResultSetResponse("connectionId", Integer.MAX_VALUE, true,
-        signature, frame, Long.MAX_VALUE);
+        signature, frame, Long.MAX_VALUE, rpcMetadata);
     responses.add(results1);
 
-    responses.add(new CloseStatementResponse());
+    responses.add(new CloseStatementResponse(rpcMetadata));
 
     ConnectionPropertiesImpl connProps = new ConnectionPropertiesImpl(false, true,
         Integer.MAX_VALUE, "catalog", "schema");
-    responses.add(new ConnectionSyncResponse(connProps));
+    responses.add(new ConnectionSyncResponse(connProps, rpcMetadata));
 
-    responses.add(new OpenConnectionResponse());
-    responses.add(new CloseConnectionResponse());
+    responses.add(new OpenConnectionResponse(rpcMetadata));
+    responses.add(new CloseConnectionResponse(rpcMetadata));
 
-    responses.add(new CreateStatementResponse("connectionId", Integer.MAX_VALUE));
+    responses.add(new CreateStatementResponse("connectionId", Integer.MAX_VALUE, rpcMetadata));
 
     Map<Meta.DatabaseProperty, Object> propertyMap = new HashMap<>();
     for (Meta.DatabaseProperty prop : Meta.DatabaseProperty.values()) {
       propertyMap.put(prop, prop.defaultValue);
     }
-    responses.add(new DatabasePropertyResponse(propertyMap));
+    responses.add(new DatabasePropertyResponse(propertyMap, rpcMetadata));
 
-    responses.add(new ExecuteResponse(Arrays.asList(results1, results1, results1), false));
-    responses.add(new FetchResponse(frame, false, false));
-    responses.add(new FetchResponse(frame, true, true));
-    responses.add(new FetchResponse(frame, false, true));
+    responses.add(new ExecuteResponse(Arrays.asList(results1, results1, results1), false,
+        rpcMetadata));
+    responses.add(new FetchResponse(frame, false, false, rpcMetadata));
+    responses.add(new FetchResponse(frame, true, true, rpcMetadata));
+    responses.add(new FetchResponse(frame, false, true, rpcMetadata));
     responses.add(
         new PrepareResponse(
             new Meta.StatementHandle("connectionId", Integer.MAX_VALUE,
-                signature)));
+                signature), rpcMetadata));
 
     StringWriter sw = new StringWriter();
     new Exception().printStackTrace(new PrintWriter(sw));
     responses.add(
         new ErrorResponse(Collections.singletonList(sw.toString()), "Test Error Message",
             ErrorResponse.UNKNOWN_ERROR_CODE, ErrorResponse.UNKNOWN_SQL_STATE,
-            AvaticaSeverity.WARNING));
+            AvaticaSeverity.WARNING, rpcMetadata));
 
     // No more results, statement not missing
-    responses.add(new SyncResultsResponse(false, false));
+    responses.add(new SyncResultsResponse(false, false, rpcMetadata));
     // Missing statement, no results
-    responses.add(new SyncResultsResponse(false, true));
+    responses.add(new SyncResultsResponse(false, true, rpcMetadata));
     // More results, no missing statement
-    responses.add(new SyncResultsResponse(true, false));
+    responses.add(new SyncResultsResponse(true, false, rpcMetadata));
 
     return responses;
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java b/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java
index d457aa3..411f29c 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaClientRuntimeExceptionTest.java
@@ -18,6 +18,7 @@ package org.apache.calcite.avatica.test;
 
 import org.apache.calcite.avatica.AvaticaClientRuntimeException;
 import org.apache.calcite.avatica.AvaticaSeverity;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
 
 import org.junit.Test;
 
@@ -37,12 +38,14 @@ public class AvaticaClientRuntimeExceptionTest {
     final String sqlState = "abc12";
     final AvaticaSeverity severity = AvaticaSeverity.ERROR;
     final List<String> stacktraces = Arrays.asList("my stack trace");
+    final RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
     AvaticaClientRuntimeException e = new AvaticaClientRuntimeException(errorMsg, errorCode,
-        sqlState, severity, stacktraces);
+        sqlState, severity, stacktraces, metadata);
     assertEquals(errorMsg, e.getMessage());
     assertEquals(errorCode, e.getErrorCode());
     assertEquals(severity, e.getSeverity());
     assertEquals(stacktraces, e.getServerExceptions());
+    assertEquals(metadata, e.getRpcMetadata());
   }
 
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java b/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java
index fb10c45..3d7b5b0 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSqlExceptionTest.java
@@ -35,13 +35,16 @@ public class AvaticaSqlExceptionTest {
     final int code = 42;
     final String sql = "SELECT foo FROM bar;";
     final String stacktrace = "My Stack Trace";
+    final String server = "localhost:8765";
 
-    AvaticaSqlException e = new AvaticaSqlException(msg, sql, code, Arrays.asList(stacktrace));
+    AvaticaSqlException e = new AvaticaSqlException(msg, sql, code, Arrays.asList(stacktrace),
+        server);
     assertTrue(e.getMessage().contains(msg));
     assertEquals(code, e.getErrorCode());
     assertEquals(sql, e.getSQLState());
     assertEquals(1, e.getStackTraces().size());
     assertEquals(stacktrace, e.getStackTraces().get(0));
+    assertEquals(server, e.getRemoteServer());
   }
 
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/3be816f4/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java b/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
index dccf889..bbfc4a7 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
@@ -118,6 +118,9 @@ public class JsonHandlerTest {
     @Override public ExecuteResponse apply(ExecuteRequest request) {
       return null;
     }
+
+    @Override
+    public void setRpcMetadata(RpcMetadataResponse metadata) {}
   }
 
   /**
@@ -146,10 +149,10 @@ public class JsonHandlerTest {
 
       final Service.ResultSetResponse resultSetResponse =
           new Service.ResultSetResponse(UUID.randomUUID().toString(),
-              RANDOM.nextInt(), false, signature, Meta.Frame.EMPTY, -1L);
+              RANDOM.nextInt(), false, signature, Meta.Frame.EMPTY, -1L, null);
 
       return new Service.ExecuteResponse(
-          Collections.singletonList(resultSetResponse), false);
+          Collections.singletonList(resultSetResponse), false, null);
     }
   }
 


Mime
View raw message