Skip to content

Commit 4979706

Browse files
committed
support prepared statement
1 parent ad21b57 commit 4979706

File tree

16 files changed

+419
-31
lines changed

16 files changed

+419
-31
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,10 @@ protected ClickHouseRequest(ClickHouseClient client, Function<ClickHouseNodeSele
403403
this.txRef = new AtomicReference<>(null);
404404

405405
this.externalTables = new LinkedList<>();
406-
this.options = ClickHouseFreezableMap.of(new HashMap<>());
407-
this.settings = ClickHouseFreezableMap.of(new LinkedHashMap<>(client.getConfig().getCustomSettings()));
406+
// TODO configurable whitelist? maybe later
407+
this.options = ClickHouseFreezableMap.of(new HashMap<>(), ClickHouseClientOption.SESSION_ID);
408+
this.settings = ClickHouseFreezableMap.of(new LinkedHashMap<>(client.getConfig().getCustomSettings()),
409+
ClickHouseTransaction.SETTING_IMPLICIT_TRANSACTION);
408410
options(options);
409411

410412
this.namedParameters = new HashMap<>();

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ public long getRowsBeforeLimit() {
134134
return rows_before_limit;
135135
}
136136

137+
public Statistics add(Statistics stats) {
138+
if (stats == null) {
139+
return this;
140+
}
141+
142+
return new Statistics(rows + stats.rows, blocks + stats.blocks, allocated_bytes + stats.allocated_bytes,
143+
applied_limit || stats.applied_limit, rows_before_limit + stats.rows_before_limit);
144+
}
145+
137146
public boolean isEmpty() {
138147
return rows == 0L && blocks == 0L && allocated_bytes == 0L && !applied_limit && rows_before_limit == 0L;
139148
}
@@ -219,6 +228,7 @@ public void add(Progress progress) {
219228

220229
Progress current = this.progress.get();
221230
this.progress.set(current.add(progress));
231+
this.updates.incrementAndGet();
222232
}
223233

224234
public void update(Statistics stats) {
@@ -231,6 +241,24 @@ public void update(Statistics stats) {
231241
}
232242
}
233243

244+
public void add(Statistics stats) {
245+
if (sealed) {
246+
throw new IllegalStateException(ERROR_CANNOT_UPDATE);
247+
}
248+
249+
Statistics current = this.stats.get();
250+
this.stats.set(current.add(stats));
251+
}
252+
253+
public void add(ClickHouseResponseSummary summary) {
254+
if (summary == null) {
255+
return;
256+
}
257+
258+
add(summary.getProgress());
259+
add(summary.getStatistics());
260+
}
261+
234262
/**
235263
* Gets current progress of the query.
236264
*
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package com.clickhouse.client;
2+
3+
import org.testng.Assert;
4+
import org.testng.annotations.Test;
5+
6+
import com.clickhouse.client.ClickHouseResponseSummary.Progress;
7+
import com.clickhouse.client.ClickHouseResponseSummary.Statistics;
8+
9+
public class ClickHouseResponseSummaryTest {
10+
@Test(groups = { "unit" })
11+
public void testConsutrctor() {
12+
ClickHouseResponseSummary summary = new ClickHouseResponseSummary(null, null);
13+
Assert.assertNotNull(summary.getProgress());
14+
Assert.assertNotNull(summary.getStatistics());
15+
Assert.assertEquals(summary.getReadBytes(), 0L);
16+
Assert.assertEquals(summary.getReadRows(), 0L);
17+
Assert.assertEquals(summary.getTotalRowsToRead(), 0L);
18+
Assert.assertEquals(summary.getUpdateCount(), 0L);
19+
Assert.assertEquals(summary.getWrittenBytes(), 0L);
20+
Assert.assertEquals(summary.getWrittenRows(), 0L);
21+
22+
Progress progress = new Progress(1L, 2L, 3L, 4L, 5L);
23+
Statistics stats = new Statistics(6L, 7L, 8L, true, 9L);
24+
summary = new ClickHouseResponseSummary(progress, stats);
25+
Assert.assertTrue(summary.getProgress() == progress);
26+
Assert.assertTrue(summary.getStatistics() == stats);
27+
Assert.assertEquals(summary.getReadBytes(), 2L);
28+
Assert.assertEquals(summary.getReadRows(), 1L);
29+
Assert.assertEquals(summary.getTotalRowsToRead(), 3L);
30+
Assert.assertEquals(summary.getUpdateCount(), 1L);
31+
Assert.assertEquals(summary.getWrittenBytes(), 5L);
32+
Assert.assertEquals(summary.getWrittenRows(), 4L);
33+
}
34+
35+
@Test(groups = { "unit" })
36+
public void testAdd() {
37+
ClickHouseResponseSummary summary = new ClickHouseResponseSummary(null, null);
38+
Assert.assertEquals(summary.getReadBytes(), 0L);
39+
Assert.assertEquals(summary.getReadRows(), 0L);
40+
Assert.assertEquals(summary.getTotalRowsToRead(), 0L);
41+
Assert.assertEquals(summary.getUpdateCount(), 0L);
42+
Assert.assertEquals(summary.getWrittenBytes(), 0L);
43+
Assert.assertEquals(summary.getWrittenRows(), 0L);
44+
45+
summary.add(summary);
46+
Assert.assertEquals(summary.getReadBytes(), 0L);
47+
Assert.assertEquals(summary.getReadRows(), 0L);
48+
Assert.assertEquals(summary.getTotalRowsToRead(), 0L);
49+
Assert.assertEquals(summary.getUpdateCount(), 1L);
50+
Assert.assertEquals(summary.getWrittenBytes(), 0L);
51+
Assert.assertEquals(summary.getWrittenRows(), 0L);
52+
53+
summary.add(ClickHouseResponseSummary.EMPTY);
54+
Assert.assertEquals(summary.getReadBytes(), 0L);
55+
Assert.assertEquals(summary.getReadRows(), 0L);
56+
Assert.assertEquals(summary.getTotalRowsToRead(), 0L);
57+
Assert.assertEquals(summary.getUpdateCount(), 2L);
58+
Assert.assertEquals(summary.getWrittenBytes(), 0L);
59+
Assert.assertEquals(summary.getWrittenRows(), 0L);
60+
61+
summary.add(new Progress(1L, 2L, 3L, 4L, 5L));
62+
Assert.assertEquals(summary.getReadBytes(), 2L);
63+
Assert.assertEquals(summary.getReadRows(), 1L);
64+
Assert.assertEquals(summary.getTotalRowsToRead(), 3L);
65+
Assert.assertEquals(summary.getUpdateCount(), 3L);
66+
Assert.assertEquals(summary.getWrittenBytes(), 5L);
67+
Assert.assertEquals(summary.getWrittenRows(), 4L);
68+
69+
summary.add(new Statistics(6L, 7L, 8L, true, 9L));
70+
Assert.assertEquals(summary.getReadBytes(), 2L);
71+
Assert.assertEquals(summary.getReadRows(), 1L);
72+
Assert.assertEquals(summary.getTotalRowsToRead(), 3L);
73+
Assert.assertEquals(summary.getUpdateCount(), 3L);
74+
Assert.assertEquals(summary.getWrittenBytes(), 5L);
75+
Assert.assertEquals(summary.getWrittenRows(), 4L);
76+
}
77+
78+
@Test(groups = { "unit" })
79+
public void testUpdate() {
80+
ClickHouseResponseSummary summary = new ClickHouseResponseSummary(null, null);
81+
Assert.assertEquals(summary.getReadBytes(), 0L);
82+
Assert.assertEquals(summary.getReadRows(), 0L);
83+
Assert.assertEquals(summary.getTotalRowsToRead(), 0L);
84+
Assert.assertEquals(summary.getUpdateCount(), 0L);
85+
Assert.assertEquals(summary.getWrittenBytes(), 0L);
86+
Assert.assertEquals(summary.getWrittenRows(), 0L);
87+
88+
summary.update();
89+
Assert.assertEquals(summary.getReadBytes(), 0L);
90+
Assert.assertEquals(summary.getReadRows(), 0L);
91+
Assert.assertEquals(summary.getTotalRowsToRead(), 0L);
92+
Assert.assertEquals(summary.getUpdateCount(), 1L);
93+
Assert.assertEquals(summary.getWrittenBytes(), 0L);
94+
Assert.assertEquals(summary.getWrittenRows(), 0L);
95+
96+
summary.update(new Progress(1L, 2L, 3L, 4L, 5L));
97+
Assert.assertEquals(summary.getReadBytes(), 2L);
98+
Assert.assertEquals(summary.getReadRows(), 1L);
99+
Assert.assertEquals(summary.getTotalRowsToRead(), 3L);
100+
Assert.assertEquals(summary.getUpdateCount(), 1L);
101+
Assert.assertEquals(summary.getWrittenBytes(), 5L);
102+
Assert.assertEquals(summary.getWrittenRows(), 4L);
103+
104+
summary.update(new Statistics(6L, 7L, 8L, true, 9L));
105+
Assert.assertEquals(summary.getReadBytes(), 2L);
106+
Assert.assertEquals(summary.getReadRows(), 1L);
107+
Assert.assertEquals(summary.getTotalRowsToRead(), 3L);
108+
Assert.assertEquals(summary.getUpdateCount(), 1L);
109+
Assert.assertEquals(summary.getWrittenBytes(), 5L);
110+
Assert.assertEquals(summary.getWrittenRows(), 4L);
111+
}
112+
113+
@Test(groups = { "unit" })
114+
public void testSeal() {
115+
ClickHouseResponseSummary summary = new ClickHouseResponseSummary(null, null);
116+
summary.add(summary);
117+
summary.add(summary.getProgress());
118+
summary.add(summary.getStatistics());
119+
summary.update();
120+
summary.update(summary.getProgress());
121+
summary.update(summary.getStatistics());
122+
Assert.assertEquals(summary.getReadBytes(), 0L);
123+
Assert.assertEquals(summary.getReadRows(), 0L);
124+
Assert.assertEquals(summary.getTotalRowsToRead(), 0L);
125+
Assert.assertEquals(summary.getUpdateCount(), 3L);
126+
Assert.assertEquals(summary.getWrittenBytes(), 0L);
127+
Assert.assertEquals(summary.getWrittenRows(), 0L);
128+
129+
summary.seal();
130+
131+
Assert.assertThrows(IllegalStateException.class, () -> summary.add(summary));
132+
Assert.assertThrows(IllegalStateException.class, () -> summary.add(summary.getProgress()));
133+
Assert.assertThrows(IllegalStateException.class, () -> summary.add(summary.getStatistics()));
134+
135+
Assert.assertThrows(IllegalStateException.class, () -> summary.update());
136+
Assert.assertThrows(IllegalStateException.class, () -> summary.update(summary.getProgress()));
137+
Assert.assertThrows(IllegalStateException.class, () -> summary.update(summary.getStatistics()));
138+
}
139+
}

clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseFreezableMap.java

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.clickhouse.data;
22

3+
import java.util.ArrayList;
34
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.List;
47
import java.util.Map;
58
import java.util.Set;
69
import java.util.concurrent.atomic.AtomicBoolean;
@@ -10,21 +13,57 @@
1013
* any error.
1114
*/
1215
public class ClickHouseFreezableMap<K, V> implements Map<K, V> {
13-
public static <K, V> ClickHouseFreezableMap<K, V> of(Map<K, V> map) {
14-
return new ClickHouseFreezableMap<>(ClickHouseChecker.nonNull(map, "Map"));
16+
/**
17+
* Creates a freezable map with initial data and optional whitelisted keys.
18+
*
19+
* @param <K> type of the key
20+
* @param <V> type of the value
21+
* @param map non-null map with initial data
22+
* @param whiteListedKeys optional whitelisted keys
23+
* @return non-null freezable map
24+
*/
25+
public static <K, V> ClickHouseFreezableMap<K, V> of(Map<K, V> map, K... whiteListedKeys) {
26+
return new ClickHouseFreezableMap<>(ClickHouseChecker.nonNull(map, "Map"), whiteListedKeys);
1527
}
1628

1729
private final AtomicBoolean freezed;
1830
private final Map<K, V> map;
31+
private final List<K> whitelist;
1932

20-
protected ClickHouseFreezableMap(Map<K, V> map) {
33+
protected ClickHouseFreezableMap(Map<K, V> map, K... keys) {
2134
this.freezed = new AtomicBoolean(false);
2235
this.map = map;
36+
if (keys == null || keys.length == 0) {
37+
this.whitelist = Collections.emptyList();
38+
} else {
39+
List<K> list = new ArrayList<>(keys.length);
40+
for (K k : keys) {
41+
if (k != null && !list.contains(k)) {
42+
list.add(k);
43+
}
44+
}
45+
this.whitelist = Collections.unmodifiableList(list);
46+
}
47+
}
48+
49+
/**
50+
* Checks whether the given key can be used in mutation(e.g. {@code put()},
51+
* {@code remove()}), regardless the map is freezed or not.
52+
*
53+
* @param key non-null key
54+
* @return true if the given key can be used in mutation; false otherwise
55+
*/
56+
protected boolean isMutable(Object key) {
57+
return !freezed.get() || whitelist.contains(key);
2358
}
2459

2560
@Override
2661
public void clear() {
27-
if (!freezed.get()) {
62+
if (freezed.get()) {
63+
for (K k : whitelist) {
64+
map.remove(k);
65+
}
66+
} else {
2867
map.clear();
2968
}
3069
}
@@ -61,19 +100,26 @@ public Set<K> keySet() {
61100

62101
@Override
63102
public V put(K key, V value) {
64-
return !freezed.get() ? map.put(key, value) : value;
103+
return isMutable(key) ? map.put(key, value) : value;
65104
}
66105

67106
@Override
68107
public void putAll(Map<? extends K, ? extends V> m) {
69-
if (!freezed.get()) {
108+
if (freezed.get()) {
109+
for (K k : whitelist) {
110+
V v = m.get(k);
111+
if (v != null) {
112+
map.put(k, v);
113+
}
114+
}
115+
} else {
70116
map.putAll(m);
71117
}
72118
}
73119

74120
@Override
75121
public V remove(Object key) {
76-
return !freezed.get() ? map.remove(key) : null;
122+
return isMutable(key) ? map.remove(key) : null;
77123
}
78124

79125
@Override
@@ -114,4 +160,15 @@ public ClickHouseFreezableMap<K, V> unfreeze() {
114160
public boolean isFreezed() {
115161
return freezed.get();
116162
}
163+
164+
/**
165+
* Checks whether the given key is whitelisted, meaning corresponding entry can
166+
* be changed even the map is freezed.
167+
*
168+
* @param key non-null key
169+
* @return true if the key is whitelisted; false otherwise
170+
*/
171+
public boolean isWhiteListed(K key) {
172+
return key != null && whitelist.contains(key);
173+
}
117174
}

clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseUtils.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ public static Map<String, String> extractParameters(String query, Map<String, St
305305
public static Path getFile(String file) {
306306
if (file == null || file.isEmpty()) {
307307
throw new IllegalArgumentException("Non-empty file is required");
308+
} else if (file.startsWith("~/")) {
309+
return Paths.get(System.getProperty("user.home"), file.substring(2)).normalize();
308310
}
309311
return Paths.get(file).toAbsolutePath().normalize();
310312
}
@@ -322,19 +324,31 @@ public static Path getFile(String file) {
322324
public static List<Path> findFiles(String pattern, String... paths) throws IOException {
323325
if (pattern == null || pattern.isEmpty()) {
324326
throw new IllegalArgumentException("Non-empty pattern is required");
327+
} else if (pattern.startsWith("~/")) {
328+
return Collections
329+
.singletonList(Paths.get(System.getProperty("user.home"), pattern.substring(2)).normalize());
325330
}
326331

327-
// FIXME not good for windows
328-
if (pattern.indexOf(':') < 0) {
329-
pattern = "glob:" + pattern;
332+
if (!pattern.startsWith("glob:") && !pattern.startsWith("regex:")) {
333+
Path path = Paths.get(pattern);
334+
if (path.isAbsolute()) {
335+
return Collections.singletonList(path);
336+
} else {
337+
pattern = "glob:" + pattern;
338+
}
330339
}
331340

332341
final Path searchPath;
333342
if (paths == null || paths.length == 0) {
334343
searchPath = Paths.get("");
335344
} else {
336-
searchPath = paths.length < 2 ? Paths.get(paths[0])
337-
: Paths.get(paths[0], Arrays.copyOfRange(paths, 1, paths.length)).toAbsolutePath().normalize();
345+
String root = paths[0];
346+
Path rootPath = root.startsWith("~/")
347+
? Paths.get(System.getProperty("user.home"), root.substring(2)).normalize()
348+
: Paths.get(root);
349+
searchPath = paths.length < 2 ? rootPath
350+
: Paths.get(rootPath.toFile().getAbsolutePath(), Arrays.copyOfRange(paths, 1, paths.length))
351+
.normalize();
338352
}
339353

340354
final List<Path> files = new ArrayList<>();
@@ -343,7 +357,7 @@ public static List<Path> findFiles(String pattern, String... paths) throws IOExc
343357
@Override
344358
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
345359
if (matcher.matches(path)) {
346-
files.add((path.isAbsolute() ? path : path.toAbsolutePath()).normalize());
360+
files.add(path.normalize());
347361
}
348362
return FileVisitResult.CONTINUE;
349363
}

0 commit comments

Comments
 (0)