Skip to content

Commit 5ba2b49

Browse files
authored
Merge pull request #1063 from zhicwu/develop
Enable lightweight delete support and fix minor issues in sight
2 parents e358f46 + c2e534e commit 5ba2b49

File tree

9 files changed

+310
-63
lines changed

9 files changed

+310
-63
lines changed

clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseRequest.java

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -413,13 +413,15 @@ protected ClickHouseRequest(ClickHouseClient client, Function<ClickHouseNodeSele
413413
this.server = (Function<ClickHouseNodeSelector, ClickHouseNode> & Serializable) server;
414414
this.serverRef = ref == null ? new AtomicReference<>(null) : ref;
415415
this.txRef = new AtomicReference<>(null);
416-
this.sealed = sealed;
417416

418417
this.externalTables = new LinkedList<>();
419-
this.options = options != null ? new HashMap<>(options) : new HashMap<>();
418+
this.options = new HashMap<>();
420419
this.settings = new LinkedHashMap<>(client.getConfig().getCustomSettings());
420+
options(options);
421421

422422
this.namedParameters = new HashMap<>();
423+
424+
this.sealed = sealed;
423425
}
424426

425427
protected <T> T changeProperty(String property, T oldValue, T newValue) {
@@ -673,6 +675,47 @@ public ClickHouseParameterizedQuery getPreparedQuery() {
673675
return preparedQuery;
674676
}
675677

678+
/**
679+
* Gets typed setting value.
680+
*
681+
* @param <T> type of the setting value
682+
* @param setting non-null setting key
683+
* @param valueType non-null value type
684+
* @return non-null value
685+
*/
686+
public <T extends Serializable> T getSetting(String setting, Class<T> valueType) {
687+
Serializable value = settings.get(ClickHouseChecker.nonBlank(setting, PARAM_SETTING));
688+
return ClickHouseOption.fromString(value == null ? "" : value.toString(), valueType);
689+
}
690+
691+
/**
692+
* Gets typed setting value.
693+
*
694+
* @param <T> type of the setting value
695+
* @param setting non-null setting key
696+
* @param defaultValue non-null default value
697+
* @return non-null value
698+
*/
699+
public <T extends Serializable> T getSetting(String setting, T defaultValue) {
700+
Serializable value = settings.get(ClickHouseChecker.nonBlank(setting, PARAM_SETTING));
701+
ClickHouseChecker.nonNull(defaultValue, "defaultValue");
702+
if (value == null) {
703+
return defaultValue;
704+
}
705+
706+
return (T) ClickHouseOption.fromString(value.toString(), defaultValue.getClass());
707+
}
708+
709+
/**
710+
* Checks if a setting has been defined or not.
711+
*
712+
* @param setting setting
713+
* @return true if the setting has been defined; false otherwise
714+
*/
715+
public boolean hasSetting(String setting) {
716+
return settings.containsKey(setting);
717+
}
718+
676719
/**
677720
* Gets immutable settings.
678721
*
@@ -1087,6 +1130,26 @@ public SelfT options(Properties options) {
10871130
return (SelfT) this;
10881131
}
10891132

1133+
/**
1134+
* Checks if a option has been defined or not.
1135+
*
1136+
* @param option option
1137+
* @return true if the option has been defined; false otherwise
1138+
*/
1139+
public boolean hasOption(ClickHouseOption option) {
1140+
return options.containsKey(option);
1141+
}
1142+
1143+
/**
1144+
* Checks if a option has been defined or not.
1145+
*
1146+
* @param key key of the option
1147+
* @return true if the option has been defined; false otherwise
1148+
*/
1149+
public boolean hasOption(String key) {
1150+
return options.containsKey(ClickHouseClientOption.fromKey(key));
1151+
}
1152+
10901153
/**
10911154
* Sets output file, to which response will be redirected.
10921155
*
@@ -1503,7 +1566,8 @@ public SelfT query(String query, String queryId) {
15031566
}
15041567

15051568
/**
1506-
* Sets all server settings.
1569+
* Sets all server settings. When {@code settings} is null or empty, it's same
1570+
* as {@link #clearSettings()}.
15071571
*
15081572
* @param settings settings
15091573
* @return the request itself
@@ -1523,7 +1587,7 @@ public SelfT settings(Map<String, Serializable> settings) {
15231587
} else {
15241588
Map<String, Serializable> m = new HashMap<>();
15251589
m.putAll(this.settings);
1526-
if (options != null) {
1590+
if (settings != null) {
15271591
for (Entry<String, Serializable> e : settings.entrySet()) {
15281592
set(e.getKey(), e.getValue());
15291593
m.remove(e.getKey());
@@ -1537,6 +1601,29 @@ public SelfT settings(Map<String, Serializable> settings) {
15371601
return (SelfT) this;
15381602
}
15391603

1604+
/**
1605+
* Clears server settings.
1606+
*
1607+
* @return the request itself
1608+
*/
1609+
@SuppressWarnings("unchecked")
1610+
public SelfT clearSettings() {
1611+
checkSealed();
1612+
1613+
if (!this.settings.isEmpty()) {
1614+
if (changeListener == null) {
1615+
this.settings.clear();
1616+
resetCache();
1617+
} else {
1618+
for (Iterator<String> it = settings.keySet().iterator(); it.hasNext();) {
1619+
removeSetting(it.next());
1620+
}
1621+
}
1622+
}
1623+
1624+
return (SelfT) this;
1625+
}
1626+
15401627
/**
15411628
* Clears session configuration including session id, session check(whether to
15421629
* validate the id), and session timeout. Transaction will be removed as well.
@@ -1911,11 +1998,11 @@ public SelfT reset() {
19111998
this.options.clear();
19121999
this.settings.clear();
19132000
} else {
1914-
for (ClickHouseOption o : this.options.keySet().toArray(new ClickHouseOption[0])) {
1915-
removeOption(o);
2001+
for (Iterator<ClickHouseOption> it = this.options.keySet().iterator(); it.hasNext();) {
2002+
removeOption(it.next());
19162003
}
1917-
for (String s : this.settings.keySet().toArray(new String[0])) {
1918-
removeSetting(s);
2004+
for (Iterator<String> it = this.settings.keySet().iterator(); it.hasNext();) {
2005+
removeSetting(it.next());
19192006
}
19202007
}
19212008
this.input = changeProperty(PROP_DATA, this.input, null);

clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseOption.java

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.util.Optional;
99
import java.util.TimeZone;
1010

11+
import com.clickhouse.client.ClickHouseChecker;
12+
1113
/**
1214
* This defines a configuration option. To put it in a nutshell, an option is
1315
* composed of key, default value(which implies type of the value) and
@@ -71,35 +73,39 @@ public static Map<String, String> toKeyValuePairs(String str) {
7173
* @param <T> type of the value
7274
* @param value value in string format
7375
* @param clazz non-null class of the value
74-
* @return typed value
76+
* @return non-null typed value
7577
*/
7678
@SuppressWarnings("unchecked")
77-
static <T extends Serializable> T fromString(String value, Class<T> clazz) {
78-
if (value == null || clazz == null) {
79-
throw new IllegalArgumentException("Non-null value and class are required");
79+
public static <T extends Serializable> T fromString(String value, Class<T> clazz) {
80+
if (clazz == null) {
81+
throw new IllegalArgumentException("Non-null value type is required");
82+
} else if (value == null) {
83+
value = "";
8084
}
8185

8286
T result;
8387
if (clazz == boolean.class || clazz == Boolean.class) {
8488
final Boolean boolValue;
85-
if ("1".equals(value) || "0".equals(value)) {
89+
if (value.isEmpty()) {
90+
boolValue = Boolean.FALSE;
91+
} else if (value.length() == 1) {
8692
boolValue = "1".equals(value);
8793
} else {
8894
boolValue = Boolean.valueOf(value);
8995
}
90-
result = clazz.cast(boolValue);
96+
result = (T) boolValue;
9197
} else if (byte.class == clazz || Byte.class == clazz) {
92-
result = clazz.cast(value.isEmpty() ? Byte.valueOf((byte) 0) : Byte.valueOf(value));
98+
result = (T) (value.isEmpty() ? Byte.valueOf((byte) 0) : Byte.valueOf(value));
9399
} else if (short.class == clazz || Short.class == clazz) {
94-
result = clazz.cast(value.isEmpty() ? Short.valueOf((short) 0) : Short.valueOf(value));
100+
result = (T) (value.isEmpty() ? Short.valueOf((short) 0) : Short.valueOf(value));
95101
} else if (int.class == clazz || Integer.class == clazz) {
96-
result = clazz.cast(value.isEmpty() ? Integer.valueOf(0) : Integer.valueOf(value));
102+
result = (T) (value.isEmpty() ? Integer.valueOf(0) : Integer.valueOf(value));
97103
} else if (long.class == clazz || Long.class == clazz) {
98-
result = clazz.cast(value.isEmpty() ? Long.valueOf(0L) : Long.valueOf(value));
104+
result = (T) (value.isEmpty() ? Long.valueOf(0L) : Long.valueOf(value));
99105
} else if (float.class == clazz || Float.class == clazz) {
100-
result = clazz.cast(value.isEmpty() ? Float.valueOf(0F) : Float.valueOf(value));
106+
result = (T) (value.isEmpty() ? Float.valueOf(0F) : Float.valueOf(value));
101107
} else if (double.class == clazz || Double.class == clazz) {
102-
result = clazz.cast(value.isEmpty() ? Double.valueOf(0D) : Double.valueOf(value));
108+
result = (T) (value.isEmpty() ? Double.valueOf(0D) : Double.valueOf(value));
103109
} else if (Enum.class.isAssignableFrom(clazz)) {
104110
Enum enumValue = null;
105111
try {
@@ -127,6 +133,27 @@ static <T extends Serializable> T fromString(String value, Class<T> clazz) {
127133
return result;
128134
}
129135

136+
/**
137+
* Converts given string to typed value. When {@code value} is null or blank,
138+
* {@code defaultValue} will be returned.
139+
*
140+
* @param <T> type of the value
141+
* @param value value in string format
142+
* @param defaultValue non-null default value
143+
* @return non-null typed value
144+
*/
145+
@SuppressWarnings("unchecked")
146+
public static <T extends Serializable> T fromString(String value, T defaultValue) {
147+
if (defaultValue == null) {
148+
throw new IllegalArgumentException("Non-null default value is required");
149+
}
150+
if (ClickHouseChecker.isNullOrBlank(value)) {
151+
return defaultValue;
152+
}
153+
154+
return (T) fromString(value, defaultValue.getClass());
155+
}
156+
130157
/**
131158
* Gets default value of the option.
132159
*

clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseRequestTest.java

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,14 @@ public void settingChanged(ClickHouseRequest<?> source, String setting, Serializ
116116
Assert.assertEquals(changedOptions.toArray(new Object[0]),
117117
new Object[][] {
118118
new Object[] { request, ClickHouseClientOption.ASYNC, null, false },
119-
new Object[] { request, ClickHouseClientOption.FORMAT, null, ClickHouseFormat.Arrow },
120-
new Object[] { request, ClickHouseClientOption.FORMAT, ClickHouseFormat.Arrow,
119+
new Object[] { request, ClickHouseClientOption.FORMAT, null,
120+
ClickHouseFormat.Arrow },
121+
new Object[] { request, ClickHouseClientOption.FORMAT,
122+
ClickHouseFormat.Arrow,
121123
ClickHouseFormat.Avro },
122124
new Object[] { request, ClickHouseClientOption.ASYNC, false, null },
123-
new Object[] { request, ClickHouseClientOption.FORMAT, ClickHouseFormat.Avro, null } });
125+
new Object[] { request, ClickHouseClientOption.FORMAT,
126+
ClickHouseFormat.Avro, null } });
124127
Assert.assertEquals(changedProperties.toArray(new Object[0]), new Object[][] {
125128
{ request, ClickHouseRequest.PROP_QUERY, null, "select 1" },
126129
{ request, ClickHouseRequest.PROP_QUERY, "select 1", "select 2" },
@@ -160,15 +163,18 @@ public void testServerListener() {
160163
ClickHouseRequest<?> request = ClickHouseClient.newInstance().connect(ClickHouseNode.builder().build());
161164
final List<Object[]> serverChanges = new ArrayList<>();
162165
request.setServerListener(
163-
(currentServer, newServer) -> serverChanges.add(new Object[] { currentServer, newServer }));
166+
(currentServer, newServer) -> serverChanges
167+
.add(new Object[] { currentServer, newServer }));
164168
ClickHouseNode s11 = ClickHouseNode.of("http://node1");
165169
ClickHouseNode s12 = ClickHouseNode.of("grpc://node1/system");
166170
ClickHouseNode s21 = ClickHouseNode.of("tcp://node2");
167171
ClickHouseNode s22 = ClickHouseNode.of("https://node2");
168172
request.changeServer(request.getServer(), s11);
169-
Assert.assertEquals(serverChanges.toArray(new Object[0]), new Object[][] { { ClickHouseNode.DEFAULT, s11 } });
173+
Assert.assertEquals(serverChanges.toArray(new Object[0]),
174+
new Object[][] { { ClickHouseNode.DEFAULT, s11 } });
170175
request.changeServer(ClickHouseNode.DEFAULT, s12);
171-
Assert.assertEquals(serverChanges.toArray(new Object[0]), new Object[][] { { ClickHouseNode.DEFAULT, s11 } });
176+
Assert.assertEquals(serverChanges.toArray(new Object[0]),
177+
new Object[][] { { ClickHouseNode.DEFAULT, s11 } });
172178
request.changeServer(s11, s21);
173179
Assert.assertEquals(serverChanges.toArray(new Object[0]),
174180
new Object[][] { { ClickHouseNode.DEFAULT, s11 }, { s11, s21 } });
@@ -183,7 +189,8 @@ public void testCopy() {
183189
ClickHouseRequest<?> request = ClickHouseClient.newInstance().connect(ClickHouseNode.builder().build());
184190
request.compressServerResponse(true, ClickHouseCompression.BROTLI, 2);
185191
request.decompressClientRequest(true, ClickHouseCompression.ZSTD, 5);
186-
request.external(ClickHouseExternalTable.builder().content(new ByteArrayInputStream(new byte[0])).build());
192+
request.external(ClickHouseExternalTable.builder().content(new ByteArrayInputStream(new byte[0]))
193+
.build());
187194
request.format(ClickHouseFormat.Avro);
188195
request.table("table1", "query_id1");
189196
request.query("select :a", UUID.randomUUID().toString());
@@ -244,12 +251,30 @@ public void testFormat() {
244251
Assert.assertEquals(request.getFormat(),
245252
(ClickHouseFormat) ClickHouseDefaults.FORMAT.getEffectiveDefaultValue());
246253
Assert.assertEquals(request.getInputFormat(),
247-
((ClickHouseFormat) ClickHouseDefaults.FORMAT.getEffectiveDefaultValue()).defaultInputFormat());
254+
((ClickHouseFormat) ClickHouseDefaults.FORMAT.getEffectiveDefaultValue())
255+
.defaultInputFormat());
248256
request.format(ClickHouseFormat.Arrow);
249257
Assert.assertEquals(request.getFormat(), ClickHouseFormat.Arrow);
250258
Assert.assertEquals(request.getInputFormat(), ClickHouseFormat.Arrow);
251259
}
252260

261+
@Test(groups = { "unit" })
262+
public void testGetSetting() {
263+
ClickHouseRequest<?> request = ClickHouseClient.newInstance()
264+
.connect("http://localhost?custom_settings=a%3D1%2Cb%3D2");
265+
Assert.assertEquals(request.getSetting("a", boolean.class), true);
266+
Assert.assertEquals(request.getSetting("a", Boolean.class), true);
267+
Assert.assertEquals(request.getSetting("a", false), true);
268+
Assert.assertEquals(request.getSetting("a", int.class), 1);
269+
Assert.assertEquals(request.getSetting("a", Integer.class), 1);
270+
Assert.assertEquals(request.getSetting("a", 9), 1);
271+
Assert.assertEquals(request.getSetting("b", "3"), "2");
272+
// request.settings(null);
273+
request.clearSettings();
274+
Assert.assertTrue(request.getSettings().isEmpty());
275+
Assert.assertEquals(request.getSetting("b", 9), 9);
276+
}
277+
253278
@Test(groups = { "unit" })
254279
public void testNamedParameters() {
255280
// String sql = "select xxx from xxx settings max_execution_time =
@@ -293,7 +318,8 @@ public void testParams() {
293318
"select -128 as one, NULL as two, * from my_table where key=NULL and arr[NULL] in numbers(NULL)");
294319

295320
request.params(ClickHouseStringValue.of(""),
296-
ClickHouseDateTimeValue.of("2012-12-12 12:23:34.56789", 2, ClickHouseValues.UTC_TIMEZONE),
321+
ClickHouseDateTimeValue.of("2012-12-12 12:23:34.56789", 2,
322+
ClickHouseValues.UTC_TIMEZONE),
297323
ClickHouseStringValue.of("key"), ClickHouseIntegerValue.of(1),
298324
ClickHouseBigIntegerValue.of(BigInteger.TEN));
299325
Assert.assertEquals(request.getQuery(), sql);
@@ -310,8 +336,10 @@ public void testParams() {
310336
"select 1.0 as one, NULL as two, * from my_table where key=NULL and arr[NULL] in numbers(NULL)");
311337

312338
params.put("one", ClickHouseStringValue.of("").toSqlExpression());
313-
params.put("two", ClickHouseDateTimeValue.of("2012-12-12 12:23:34.56789", 2, ClickHouseValues.UTC_TIMEZONE)
314-
.toSqlExpression());
339+
params.put("two",
340+
ClickHouseDateTimeValue
341+
.of("2012-12-12 12:23:34.56789", 2, ClickHouseValues.UTC_TIMEZONE)
342+
.toSqlExpression());
315343
params.put("key", ClickHouseStringValue.of("key").toSqlExpression());
316344
params.put("some", ClickHouseBigIntegerValue.of(BigInteger.ONE).toSqlExpression());
317345
params.put("idx", ClickHouseIntegerValue.of(1).toSqlExpression());
@@ -328,7 +356,8 @@ public void testSeal() {
328356
ClickHouseRequest<?> request = ClickHouseClient.newInstance().connect(ClickHouseNode.builder().build());
329357
request.compressServerResponse(true, ClickHouseCompression.BROTLI, 2);
330358
request.decompressClientRequest(true, ClickHouseCompression.ZSTD, 5);
331-
request.external(ClickHouseExternalTable.builder().content(new ByteArrayInputStream(new byte[0])).build());
359+
request.external(ClickHouseExternalTable.builder().content(new ByteArrayInputStream(new byte[0]))
360+
.build());
332361
request.format(ClickHouseFormat.Avro);
333362
request.table("table1", "query_id1");
334363
request.query("select :a", UUID.randomUUID().toString());
@@ -419,7 +448,8 @@ public void testSettings() {
419448
Assert.assertEquals(request.getStatements().get(0), "SET enable_optimize_predicate_expression=1");
420449
request.set("log_queries_min_type", "EXCEPTION_WHILE_PROCESSING");
421450
Assert.assertEquals(request.getStatements().size(), 2);
422-
Assert.assertEquals(request.getStatements().get(1), "SET log_queries_min_type='EXCEPTION_WHILE_PROCESSING'");
451+
Assert.assertEquals(request.getStatements().get(1),
452+
"SET log_queries_min_type='EXCEPTION_WHILE_PROCESSING'");
423453
}
424454

425455
@Test(groups = { "unit" })

clickhouse-client/src/test/java/com/clickhouse/client/config/ClickHouseOptionTest.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,23 @@ public String getDescription() {
6161

6262
@Test(groups = { "unit" })
6363
public void testFromString() {
64-
Assert.assertThrows(IllegalArgumentException.class,
65-
() -> ClickHouseOption.fromString(null, String.class));
64+
Assert.assertThrows(IllegalArgumentException.class, () -> ClickHouseOption.fromString(null, null));
65+
66+
Assert.assertEquals(ClickHouseOption.fromString(null, String.class), "");
6667
Assert.assertEquals(ClickHouseOption.fromString("", String.class), "");
6768

6869
Assert.assertEquals(ClickHouseOption.fromString("", Boolean.class), Boolean.FALSE);
6970
Assert.assertEquals(ClickHouseOption.fromString("Yes", Boolean.class), Boolean.FALSE);
71+
Assert.assertEquals(ClickHouseOption.fromString("1", boolean.class), true);
7072
Assert.assertEquals(ClickHouseOption.fromString("1", Boolean.class), Boolean.TRUE);
7173
Assert.assertEquals(ClickHouseOption.fromString("true", Boolean.class), Boolean.TRUE);
7274
Assert.assertEquals(ClickHouseOption.fromString("True", Boolean.class), Boolean.TRUE);
7375

76+
Assert.assertEquals(ClickHouseOption.fromString(null, Integer.class), Integer.valueOf(0));
7477
Assert.assertEquals(ClickHouseOption.fromString("", Integer.class), Integer.valueOf(0));
78+
Assert.assertEquals(ClickHouseOption.fromString(" ", 1), 1);
7579
Assert.assertEquals(ClickHouseOption.fromString("0", Integer.class), Integer.valueOf(0));
76-
Assert.assertThrows(IllegalArgumentException.class,
77-
() -> ClickHouseOption.fromString(null, Integer.class));
80+
Assert.assertEquals(ClickHouseOption.fromString("0", int.class), 0);
7881

7982
Assert.assertEquals(ClickHouseOption.fromString("0.1", Float.class), Float.valueOf(0.1F));
8083
Assert.assertEquals(ClickHouseOption.fromString("NaN", Float.class), Float.valueOf(Float.NaN));

0 commit comments

Comments
 (0)