Skip to content

Commit dd7d4f5

Browse files
authored
Merge branch 'main' into main
2 parents 1378092 + 5dc8e67 commit dd7d4f5

File tree

17 files changed

+406
-25
lines changed

17 files changed

+406
-25
lines changed

clickhouse-client/src/test/resources/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,11 @@ openssl x509 -req -in server.csr -CA myCA.crt -CAkey myCA.key -CAcreateserial -o
1818
openssl req -nodes -subj "/CN=me" -newkey rsa:2048 -keyout client.key -out client.csr
1919
openssl x509 -req -in client.csr -out client.crt -CAcreateserial -CA myCA.crt -CAkey myCA.key -days 36500
2020
```
21+
22+
### Some_user
23+
24+
```bash
25+
openssl req -nodes -subj "/CN=some_user" -newkey rsa:2048 -keyout some_user.key -out some_user.csr
26+
openssl x509 -req -in some_user.csr -out some_user.crt -CAcreateserial -CA marsnet_ca.crt -CAkey marsnet_ca.key -days 36500
27+
28+
```
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICtDCCAZwCFBbI6UQK2g1r8o4XRXu+9wvQBHmnMA0GCSqGSIb3DQEBCwUAMBcx
3+
FTATBgNVBAMMDGxvY2FsaG9zdCBDQTAgFw0yNDEwMTAxNjM3MzhaGA8yMTI0MDkx
4+
NjE2MzczOFowFDESMBAGA1UEAwwJc29tZV91c2VyMIIBIjANBgkqhkiG9w0BAQEF
5+
AAOCAQ8AMIIBCgKCAQEA68bBZlvBT64suwLa61eob9roTVXlJQmB9tGvX2cnJacP
6+
NBx2h6W8Ow43doRLBRt32SopV06O1i2c0L84pRoliJcGrUhKyxAsVxVv11mFd4qg
7+
962TeYe3VawSKK2w83GNfVhjQFwuNEDuzJT0I7J0jH/uNclMxAtFZNkKVMA2GOK2
8+
c3Pib8zCmqITWAX5XXWUUvS0LWsASaBAEVh4R7StYbDl0L3VeiHCw6fKpdevVfw5
9+
eDb+KuwMUOCPak0v31izEsXtcAyc7hxEZLfUMA+00zAdUENTC38GOJNTqirg0YmD
10+
+wxPdp3quWwkF/b831UTczAHkK7GP3swPjfciMN8nwIDAQABMA0GCSqGSIb3DQEB
11+
CwUAA4IBAQB+1poCA9p/XyKf5jxnAkaZQzoRW+fNqZvz8Eld2gGLqw7ZZUiBW6zo
12+
d4aCAeuNehw5zJEOf1ew5EZzdWYRdxXUarjs3HOSQalfYTS8HqNI19sgWYD6Zcx+
13+
sygJqswtplvPAB6phk9zyhQDLFNuJ8dp28xRgGuywYtVMnvLG1wapPf/fnqkRcOW
14+
yTBS4BBvtmzKPzMMZl/qB4Ol/STgVphceMFmI71HQQFUPb56E5tAQ+m3fezjdAJ2
15+
gZ+/LsApHLwhEV0ZGyIe/MNx0nDrkfWYWa7BsqvG6uuxyPXxgXSQofNjJN+RahRm
16+
oHREhAYRL40BS1F20aLRFRupzLJngLBh
17+
-----END CERTIFICATE-----
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN CERTIFICATE REQUEST-----
2+
MIICWTCCAUECAQAwFDESMBAGA1UEAwwJc29tZV91c2VyMIIBIjANBgkqhkiG9w0B
3+
AQEFAAOCAQ8AMIIBCgKCAQEA68bBZlvBT64suwLa61eob9roTVXlJQmB9tGvX2cn
4+
JacPNBx2h6W8Ow43doRLBRt32SopV06O1i2c0L84pRoliJcGrUhKyxAsVxVv11mF
5+
d4qg962TeYe3VawSKK2w83GNfVhjQFwuNEDuzJT0I7J0jH/uNclMxAtFZNkKVMA2
6+
GOK2c3Pib8zCmqITWAX5XXWUUvS0LWsASaBAEVh4R7StYbDl0L3VeiHCw6fKpdev
7+
Vfw5eDb+KuwMUOCPak0v31izEsXtcAyc7hxEZLfUMA+00zAdUENTC38GOJNTqirg
8+
0YmD+wxPdp3quWwkF/b831UTczAHkK7GP3swPjfciMN8nwIDAQABoAAwDQYJKoZI
9+
hvcNAQELBQADggEBAA6cpW7rdV0a8FDxEBfZoStJPEVUisqS5pUT43UjFJ7M55kC
10+
LGQ9Vl2Ua0nA4BwX5Le/IWVwwnhnnIJWvoPEbka9TWBVGujOPvt/WwBbEN2yHgGD
11+
QgFrIq/zOaFVj3J3EuJtIXL2jOylDK14j+2k4MN4OJobVtQhyUHpmRTPgq4EVJIw
12+
/PU6Lltgr2V4pTs3m9Ey2pIHF04HQIzr6Tt6MRJkKGEYWvOZlYuCbXA5bPLMyq5g
13+
rs0kC1DMF5C3VsBND8oGQt0ULbc2AQy6AFJegdD/ZT+d4eeh+ejymc0nmB+kbxaM
14+
tAxp2yTsRKUsGu7TBeMY1DxoP1xG5lAHkGznESg=
15+
-----END CERTIFICATE REQUEST-----
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDrxsFmW8FPriy7
3+
AtrrV6hv2uhNVeUlCYH20a9fZyclpw80HHaHpbw7Djd2hEsFG3fZKilXTo7WLZzQ
4+
vzilGiWIlwatSErLECxXFW/XWYV3iqD3rZN5h7dVrBIorbDzcY19WGNAXC40QO7M
5+
lPQjsnSMf+41yUzEC0Vk2QpUwDYY4rZzc+JvzMKaohNYBflddZRS9LQtawBJoEAR
6+
WHhHtK1hsOXQvdV6IcLDp8ql169V/Dl4Nv4q7AxQ4I9qTS/fWLMSxe1wDJzuHERk
7+
t9QwD7TTMB1QQ1MLfwY4k1OqKuDRiYP7DE92neq5bCQX9vzfVRNzMAeQrsY/ezA+
8+
N9yIw3yfAgMBAAECggEAG+dVD976al/ehbAepkKkub7fxk33mrdc17qqNYbDlcux
9+
2297lwMw2zu2qa1EzvvDZoKnC4ujEPWrUkiHP4Ga1pGqeyCL+tX/rBC/60Mk2L3M
10+
iMjUpB9BPdTpqJch0uCUp7R/DpNk7nnkKSHUdlMSQxHdkyUEk6ESheRqj2wuGtSj
11+
zVjXqUQa1yUiD1RZsg+o1v0Jk1bPvljMAblWslD4fBicX82MslExkuG4Kv411hss
12+
EBfkbGAQAAVHGiQijaiJ1nuwuiiqHNgaNRUZSqnIZm6+TGCbCDbXjzRIVBzMEvfd
13+
kB1DmpmxpzsVMDN6CK+RSpZXOgq2yTwcYAAbR5NHUQKBgQD/87ZGygLCHxzlj7T6
14+
MpinZq8QEoB4OCqPn8gSvWI237U1Kr32KxjPHSHBu5DPDPNy6bf/upha3kqJ+53c
15+
KVy3vZ9rEfkkXO9+5lNmlRWzG3+2TavH0SuihMQa1rK1aReyaDNG4xN8l9JxrVjx
16+
iKo/lTdBKpSZjxC11mbbWBTPEQKBgQDr0hMoxazSMcCxYO2WXYVejVyiEueoQqSQ
17+
BSDBqRKbv0+Gt5geM1dPxInRYCPJDhzgTbTvp33NSnl9LkhIBy/g2Z/jKZxxQB8f
18+
LQiN+yoja6kYTagBfogCLHObdPl/VV5/hBEaffeM94KLwGfOxGXbWUzA/sdoHjxc
19+
EcD/ncrwrwKBgQDw3H7WxPmthiviV8cegAip1/a8cDzXZTugJuPXxsKrEwBqxQs4
20+
ojvZg/elYYYXYn+izxBpJkaDlJaenNtkOMRY4Kgp0SMcthxm1gb8DSX7g9A+VX9n
21+
LY8bhEcrXomUMA6txGMkvUI0SIcwlMmTmmFkLl5uA80NaNV32Qi4N351kQKBgQCp
22+
/Ic1B7D430ZAVldM4WMG8i1I4wm73zYSXq/rCT3RqQjhWiw78NRKOqkBlSSWhCbK
23+
hRkc+4YSWlHSq28NBKk9koHPVKphdFA6v9J/zgHlAHEmhKvLT/MoZfR7pclHQTlZ
24+
/8/4Yb71DWE77dimUin+AJP0NnN1GP53e5C8cXjdHwKBgCeCtZtNpj0O2dt4s6CC
25+
392etUExPNvV5vyLfAlgTGI9SDPHApxomeu4wmsdBn8pIKY4apdP7MDXRgeU85Ql
26+
DNOFDngldtgzvTS7PjyJ3JORqDdzidKnmQ0YLlLvzdoD1xQtI+YIkZoMB6dgugfC
27+
tG/1B7aaPnbRlHz98DJBpEk4
28+
-----END PRIVATE KEY-----

clickhouse-http-client/src/main/java/com/clickhouse/client/http/ClickHouseHttpProto.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public class ClickHouseHttpProto {
4646

4747
public static final String HEADER_DB_PASSWORD = "X-ClickHouse-Key";
4848

49+
public static final String HEADER_SSL_CERT_AUTH = "x-clickhouse-ssl-certificate-auth";
50+
4951
/**
5052
* Query parameter to specify the query ID.
5153
*/

client-v2/src/main/java/com/clickhouse/client/api/Client.java

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import com.clickhouse.client.api.internal.SettingsConverter;
3030
import com.clickhouse.client.api.internal.TableSchemaParser;
3131
import com.clickhouse.client.api.internal.ValidationUtils;
32+
import com.clickhouse.client.api.metadata.ColumnToMethodMatchingStrategy;
33+
import com.clickhouse.client.api.metadata.DefaultColumnToMethodMatchingStrategy;
3234
import com.clickhouse.client.api.metadata.TableSchema;
3335
import com.clickhouse.client.api.metrics.ClientMetrics;
3436
import com.clickhouse.client.api.metrics.OperationMetrics;
@@ -146,8 +148,10 @@ public class Client implements AutoCloseable {
146148
private Map<String, TableSchema> tableSchemaCache = new ConcurrentHashMap<>();
147149
private Map<String, Boolean> tableSchemaHasDefaults = new ConcurrentHashMap<>();
148150

151+
private final ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy;
152+
149153
private Client(Set<String> endpoints, Map<String,String> configuration, boolean useNewImplementation,
150-
ExecutorService sharedOperationExecutor) {
154+
ExecutorService sharedOperationExecutor, ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy) {
151155
this.endpoints = endpoints;
152156
this.configuration = configuration;
153157
this.endpoints.forEach(endpoint -> {
@@ -170,6 +174,7 @@ private Client(Set<String> endpoints, Map<String,String> configuration, boolean
170174
this.oldClient = ClientV1AdaptorHelper.createClient(configuration);
171175
LOG.info("Using old http client implementation");
172176
}
177+
this.columnToMethodMatchingStrategy = columnToMethodMatchingStrategy;
173178
}
174179

175180
/**
@@ -211,6 +216,7 @@ public static class Builder {
211216
private boolean useNewImplementation = true;
212217

213218
private ExecutorService sharedOperationExecutor = null;
219+
private ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy;
214220

215221
public Builder() {
216222
this.endpoints = new HashSet<>();
@@ -320,6 +326,17 @@ public Builder setAccessToken(String accessToken) {
320326
return this;
321327
}
322328

329+
/**
330+
* Makes client to use SSL Client Certificate to authenticate with server.
331+
* Client certificate should be set as well. {@link Client.Builder#setClientCertificate(String)}
332+
* @param useSSLAuthentication
333+
* @return
334+
*/
335+
public Builder useSSLAuthentication(boolean useSSLAuthentication) {
336+
this.configuration.put("ssl_authentication", String.valueOf(useSSLAuthentication));
337+
return this;
338+
}
339+
323340
/**
324341
* Configures client to use build-in connection pool
325342
* @param enable - if connection pool should be enabled
@@ -846,6 +863,18 @@ public Builder serverSetting(String name, Collection<String> values) {
846863
return this;
847864
}
848865

866+
/**
867+
* Sets column to method matching strategy. It is used while registering POJO serializers and deserializers.
868+
* Default is {@link DefaultColumnToMethodMatchingStrategy}.
869+
*
870+
* @param strategy - matching strategy
871+
* @return same instance of the builder
872+
*/
873+
public Builder columnToMethodMatchingStrategy(ColumnToMethodMatchingStrategy strategy) {
874+
this.columnToMethodMatchingStrategy = strategy;
875+
return this;
876+
}
877+
849878
public Client build() {
850879
setDefaults();
851880

@@ -854,12 +883,24 @@ public Client build() {
854883
throw new IllegalArgumentException("At least one endpoint is required");
855884
}
856885
// check if username and password are empty. so can not initiate client?
857-
if (!this.configuration.containsKey("access_token") && (!this.configuration.containsKey("user") || !this.configuration.containsKey("password"))) {
858-
throw new IllegalArgumentException("Username and password are required");
886+
if (!this.configuration.containsKey("access_token") &&
887+
(!this.configuration.containsKey("user") || !this.configuration.containsKey("password")) &&
888+
!MapUtils.getFlag(this.configuration, "ssl_authentication")) {
889+
throw new IllegalArgumentException("Username and password (or access token, or SSL authentication) are required");
859890
}
860891

861-
if (this.configuration.containsKey(ClickHouseClientOption.TRUST_STORE) &&
862-
this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE)) {
892+
if (this.configuration.containsKey("ssl_authentication") &&
893+
(this.configuration.containsKey("password") || this.configuration.containsKey("access_token"))) {
894+
throw new IllegalArgumentException("Only one of password, access token or SSL authentication can be used per client.");
895+
}
896+
897+
if (this.configuration.containsKey("ssl_authentication") &&
898+
!this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE.getKey())) {
899+
throw new IllegalArgumentException("SSL authentication requires a client certificate");
900+
}
901+
902+
if (this.configuration.containsKey(ClickHouseClientOption.TRUST_STORE.getKey()) &&
903+
this.configuration.containsKey(ClickHouseClientOption.SSL_CERTIFICATE.getKey())) {
863904
throw new IllegalArgumentException("Trust store and certificates cannot be used together");
864905
}
865906

@@ -891,7 +932,7 @@ public Client build() {
891932
throw new IllegalArgumentException("Nor server timezone nor specific timezone is set");
892933
}
893934

894-
return new Client(this.endpoints, this.configuration, this.useNewImplementation, this.sharedOperationExecutor);
935+
return new Client(this.endpoints, this.configuration, this.useNewImplementation, this.sharedOperationExecutor, this.columnToMethodMatchingStrategy);
895936
}
896937

897938
private static final int DEFAULT_NETWORK_BUFFER_SIZE = 300_000;
@@ -963,6 +1004,10 @@ private void setDefaults() {
9631004
if (!configuration.containsKey("client_allow_binary_reader_to_reuse_buffers")) {
9641005
allowBinaryReaderToReuseBuffers(false);
9651006
}
1007+
1008+
if (columnToMethodMatchingStrategy == null) {
1009+
columnToMethodMatchingStrategy = DefaultColumnToMethodMatchingStrategy.INSTANCE;
1010+
}
9661011
}
9671012
}
9681013

@@ -1018,19 +1063,17 @@ public synchronized void register(Class<?> clazz, TableSchema schema) {
10181063
}
10191064
tableSchemaCache.put(schemaKey, schema);
10201065

1066+
ColumnToMethodMatchingStrategy matchingStrategy = columnToMethodMatchingStrategy;
1067+
10211068
//Create a new POJOSerializer with static .serialize(object, columns) methods
10221069
Map<String, Method> classGetters = new HashMap<>();
10231070
Map<String, Method> classSetters = new HashMap<>();
10241071
for (Method method : clazz.getMethods()) {//Clean up the method names
1025-
String methodName = method.getName();
1026-
if (methodName.startsWith("get") || methodName.startsWith("has")) {
1027-
methodName = methodName.substring(3).toLowerCase();
1028-
classGetters.put(methodName, method);
1029-
} else if (methodName.startsWith("is")) {
1030-
methodName = methodName.substring(2).toLowerCase();
1072+
if (matchingStrategy.isGetter(method.getName())) {
1073+
String methodName = matchingStrategy.normalizeMethodName(method.getName());
10311074
classGetters.put(methodName, method);
1032-
} else if (methodName.startsWith("set")) {
1033-
methodName = methodName.substring(3).toLowerCase();
1075+
} else if (matchingStrategy.isSetter(method.getName())) {
1076+
String methodName = matchingStrategy.normalizeMethodName(method.getName());
10341077
classSetters.put(methodName, method);
10351078
}
10361079
}
@@ -1040,7 +1083,7 @@ public synchronized void register(Class<?> clazz, TableSchema schema) {
10401083
boolean defaultsSupport = schema.hasDefaults();
10411084
tableSchemaHasDefaults.put(schemaKey, defaultsSupport);
10421085
for (ClickHouseColumn column : schema.getColumns()) {
1043-
String propertyName = column.getColumnName().toLowerCase().replace("_", "").replace(".", "");
1086+
String propertyName = columnToMethodMatchingStrategy.normalizeColumnName(column.getColumnName());
10441087
Method getterMethod = classGetters.get(propertyName);
10451088
if (getterMethod != null) {
10461089
schemaSerializers.put(column.getColumnName(), (obj, stream) -> {

client-v2/src/main/java/com/clickhouse/client/api/DataTypeUtils.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,19 @@
44

55
public class DataTypeUtils {
66

7-
public static DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
7+
/**
8+
* Formatter for the DateTime type.
9+
*/
10+
public static DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
811

12+
/**
13+
* Formatter for the Date type.
14+
*/
915
public static DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
16+
17+
/**
18+
* Formatter for the DateTime type with nanoseconds.
19+
*/
20+
public static DateTimeFormatter DATETIME_WITH_NANOS_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.nnnnnnnnn");
21+
1022
}

client-v2/src/main/java/com/clickhouse/client/api/command/CommandResponse.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.clickhouse.client.api.metrics.ServerMetrics;
66
import com.clickhouse.client.api.query.QueryResponse;
77

8-
public class CommandResponse{
8+
public class CommandResponse implements AutoCloseable {
99

1010
private final QueryResponse response;
1111

@@ -71,4 +71,9 @@ public long getWrittenBytes() {
7171
public long getServerTime() {
7272
return response.getServerTime();
7373
}
74+
75+
@Override
76+
public void close() throws Exception {
77+
response.close();
78+
}
7479
}

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public <T> T readValue(ClickHouseColumn column, Class<?> typeHint) throws IOExce
171171
return convertDateTime(readDateTime32(column.getTimeZone() == null ? timeZone :
172172
column.getTimeZone()), typeHint);
173173
case DateTime64:
174-
return convertDateTime(readDateTime64(3, column.getTimeZone() == null ? timeZone :
174+
return convertDateTime(readDateTime64(column.getScale(), column.getTimeZone() == null ? timeZone :
175175
column.getTimeZone()), typeHint);
176176

177177
case IntervalYear:

client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,19 @@ private void addHeaders(HttpPost req, Map<String, String> chConfig, Map<String,
396396
}else {
397397
req.addHeader(ClickHouseHttpProto.HEADER_DATABASE, chConfig.get(ClickHouseClientOption.DATABASE.getKey()));
398398
}
399+
if (requestConfig.containsKey(ClickHouseClientOption.FORMAT.getKey())) {
400+
req.addHeader(ClickHouseHttpProto.HEADER_FORMAT, requestConfig.get(ClickHouseClientOption.FORMAT.getKey()));
401+
}
402+
if (requestConfig.containsKey(ClickHouseClientOption.QUERY_ID.getKey())) {
403+
req.addHeader(ClickHouseHttpProto.HEADER_QUERY_ID, requestConfig.get(ClickHouseClientOption.QUERY_ID.getKey()).toString());
404+
}
399405
}
400406
req.addHeader(ClickHouseHttpProto.HEADER_DB_USER, chConfig.get(ClickHouseDefaults.USER.getKey()));
401-
req.addHeader(ClickHouseHttpProto.HEADER_DB_PASSWORD, chConfig.get(ClickHouseDefaults.PASSWORD.getKey()));
402-
407+
if (MapUtils.getFlag(chConfig, "ssl_authentication", false)) {
408+
req.addHeader(ClickHouseHttpProto.HEADER_SSL_CERT_AUTH, "on");
409+
} else {
410+
req.addHeader(ClickHouseHttpProto.HEADER_DB_PASSWORD, chConfig.get(ClickHouseDefaults.PASSWORD.getKey()));
411+
}
403412
if (proxyAuthHeaderValue != null) {
404413
req.addHeader(HttpHeaders.PROXY_AUTHORIZATION, proxyAuthHeaderValue);
405414
}

0 commit comments

Comments
 (0)