diff --git a/client-v2/src/main/java/com/clickhouse/client/api/Client.java b/client-v2/src/main/java/com/clickhouse/client/api/Client.java index 9d983decb..73ec21155 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/Client.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/Client.java @@ -1338,7 +1338,7 @@ public CompletableFuture insert(String tableName, List data, String queryId = HttpAPIClientHelper.getHeaderVal(httpResponse.getFirstHeader(ClickHouseHttpProto.HEADER_QUERY_ID), requestSettings.getQueryId(), String::valueOf); metrics.operationComplete(); metrics.setQueryId(queryId); - return new InsertResponse(metrics); + return new InsertResponse(metrics, HttpAPIClientHelper.collectResponseHeaders(httpResponse)); } catch (Exception e) { String msg = requestExMsg("Insert", (i + 1), durationSince(startTime).toMillis(), requestSettings.getQueryId()); lastException = httpClientHelper.wrapException(msg, e, requestSettings.getQueryId()); @@ -1545,7 +1545,7 @@ public CompletableFuture insert(String tableName, String queryId = HttpAPIClientHelper.getHeaderVal(httpResponse.getFirstHeader(ClickHouseHttpProto.HEADER_QUERY_ID), requestSettings.getQueryId(), String::valueOf); metrics.operationComplete(); metrics.setQueryId(queryId); - return new InsertResponse(metrics); + return new InsertResponse(metrics, HttpAPIClientHelper.collectResponseHeaders(httpResponse)); } catch (Exception e) { String msg = requestExMsg("Insert", (i + 1), durationSince(startTime).toMillis(), requestSettings.getQueryId()); lastException = httpClientHelper.wrapException(msg, e, requestSettings.getQueryId()); @@ -1684,7 +1684,8 @@ public CompletableFuture query(String sqlQuery, Map getResponseHeaders() { + return response.getResponseHeaders(); + } + @Override public void close() throws Exception { response.close(); diff --git a/client-v2/src/main/java/com/clickhouse/client/api/insert/InsertResponse.java b/client-v2/src/main/java/com/clickhouse/client/api/insert/InsertResponse.java index 09dd11667..2fc09f312 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/insert/InsertResponse.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/insert/InsertResponse.java @@ -1,13 +1,23 @@ package com.clickhouse.client.api.insert; +import com.clickhouse.client.api.http.ClickHouseHttpProto; import com.clickhouse.client.api.metrics.OperationMetrics; import com.clickhouse.client.api.metrics.ServerMetrics; +import java.util.Collections; +import java.util.Map; + public class InsertResponse implements AutoCloseable { private OperationMetrics operationMetrics; + private final Map responseHeaders; public InsertResponse(OperationMetrics metrics) { + this(metrics, Collections.emptyMap()); + } + + public InsertResponse(OperationMetrics metrics, Map responseHeaders) { this.operationMetrics = metrics; + this.responseHeaders = responseHeaders; } @Override @@ -78,4 +88,23 @@ public long getResultRows() { public String getQueryId() { return operationMetrics.getQueryId(); } + + /** + * Returns the value of {@code X-ClickHouse-Server-Display-Name} response header. + * + * @return server display name or {@code null} if not present + */ + public String getServerDisplayName() { + return responseHeaders.get(ClickHouseHttpProto.HEADER_SRV_DISPLAY_NAME); + } + + /** + * Returns all collected response headers as an unmodifiable map. + * Only whitelisted ClickHouse headers are included. + * + * @return map of header name to header value + */ + public Map getResponseHeaders() { + return responseHeaders; + } } diff --git a/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java b/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java index 31cdeff3f..76e2dec93 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java @@ -95,6 +95,8 @@ import java.util.Objects; import java.util.Optional; import java.util.Properties; +import java.util.Arrays; +import java.util.HashMap; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; @@ -752,6 +754,31 @@ public static int getHeaderInt(Header header, int defaultValue) { return getHeaderVal(header, defaultValue, Integer::parseInt); } + private static final Set RESPONSE_HEADER_WHITELIST = new HashSet<>(Arrays.asList( + ClickHouseHttpProto.HEADER_QUERY_ID, + ClickHouseHttpProto.HEADER_SRV_SUMMARY, + ClickHouseHttpProto.HEADER_SRV_DISPLAY_NAME, + ClickHouseHttpProto.HEADER_DATABASE, + ClickHouseHttpProto.HEADER_DB_USER + )); + + /** + * Collects whitelisted response headers from an HTTP response into a map. + * + * @param response the HTTP response + * @return unmodifiable map of header name to header value for whitelisted headers present in the response + */ + public static Map collectResponseHeaders(ClassicHttpResponse response) { + Map headers = new HashMap<>(); + for (String name : RESPONSE_HEADER_WHITELIST) { + Header header = response.getFirstHeader(name); + if (header != null) { + headers.put(name, header.getValue()); + } + } + return Collections.unmodifiableMap(headers); + } + public static String getHeaderVal(Header header, String defaultValue) { return getHeaderVal(header, defaultValue, Function.identity()); } diff --git a/client-v2/src/main/java/com/clickhouse/client/api/query/QueryResponse.java b/client-v2/src/main/java/com/clickhouse/client/api/query/QueryResponse.java index 735fe6f58..6e237a18d 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/query/QueryResponse.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/query/QueryResponse.java @@ -10,6 +10,8 @@ import org.apache.hc.core5.http.Header; import java.io.InputStream; +import java.util.Collections; +import java.util.Map; import java.util.TimeZone; /** @@ -35,12 +37,20 @@ public class QueryResponse implements AutoCloseable { private ClassicHttpResponse httpResponse; + private final Map responseHeaders; + public QueryResponse(ClassicHttpResponse response, ClickHouseFormat format, QuerySettings settings, OperationMetrics operationMetrics) { + this(response, format, settings, operationMetrics, Collections.emptyMap()); + } + + public QueryResponse(ClassicHttpResponse response, ClickHouseFormat format, QuerySettings settings, + OperationMetrics operationMetrics, Map responseHeaders) { this.httpResponse = response; this.format = format; this.operationMetrics = operationMetrics; this.settings = settings; + this.responseHeaders = responseHeaders; Header tzHeader = response.getFirstHeader(ClickHouseHttpProto.HEADER_TIMEZONE); if (tzHeader != null) { @@ -150,6 +160,25 @@ public String getQueryId() { return operationMetrics.getQueryId(); } + /** + * Returns the value of {@code X-ClickHouse-Server-Display-Name} response header. + * + * @return server display name or {@code null} if not present + */ + public String getServerDisplayName() { + return responseHeaders.get(ClickHouseHttpProto.HEADER_SRV_DISPLAY_NAME); + } + + /** + * Returns all collected response headers as an unmodifiable map. + * Only whitelisted ClickHouse headers are included. + * + * @return map of header name to header value + */ + public Map getResponseHeaders() { + return responseHeaders; + } + public TimeZone getTimeZone() { return settings.getOption(ClientConfigProperties.SERVER_TIMEZONE.getKey()) == null ? null