Skip to content

Commit 2562606

Browse files
committed
Add ClickHouseStatement.setMirroredOutput() for dumping ResultSet
1 parent 9a8f7c9 commit 2562606

File tree

5 files changed

+79
-15
lines changed

5 files changed

+79
-15
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## 0.4.6, 2023-04-26
2+
### New Features
3+
* ClickHouseStatement.setMirroredOutput() for dumping ResultSet.
4+
25
### Bug Fixes
6+
* NoClassDefFoundError with clickhouse-apache-http-client-jdbc. [#1319](https://github.com/ClickHouse/clickhouse-java/issues/1319)
37
* client certificate password exposure in exception. [#1331](https://github.com/ClickHouse/clickhouse-java/issues/1331)
48

59
## 0.4.5, 2023-04-25

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/ClickHouseResultSet.java

+11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.clickhouse.jdbc;
22

33
import java.io.ByteArrayInputStream;
4+
import java.io.IOException;
45
import java.io.InputStream;
6+
import java.io.OutputStream;
57
import java.io.Reader;
68
import java.io.StringReader;
79
import java.io.UncheckedIOException;
@@ -110,6 +112,15 @@ public ClickHouseResultSet(String database, String table, ClickHouseStatement st
110112
this.wrapObject = statement.getConnection().getJdbcConfig().useWrapperObject();
111113
this.defaultCalendar = conn.getDefaultCalendar();
112114

115+
OutputStream output = statement.getMirroredOutput();
116+
if (output != null) {
117+
try {
118+
response.getInputStream().setCopyToTarget(output);
119+
} catch (IOException e) {
120+
throw SqlExceptionUtils.clientError(e);
121+
}
122+
}
123+
113124
this.mapper = statement.getConnection().getJdbcTypeMapping();
114125
Map<String, Class<?>> typeMap = conn.getTypeMap();
115126
this.defaultTypeMap = typeMap != null && !typeMap.isEmpty() ? Collections.unmodifiableMap(typeMap)

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/ClickHouseStatement.java

+17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.clickhouse.jdbc;
22

3+
import java.io.OutputStream;
34
import java.sql.SQLException;
45
import java.sql.Statement;
56

@@ -13,6 +14,22 @@ public interface ClickHouseStatement extends Statement {
1314

1415
ClickHouseConfig getConfig();
1516

17+
/**
18+
* Gets mirrored output stream.
19+
*
20+
* @return mirrored output stream, could be null
21+
*/
22+
OutputStream getMirroredOutput();
23+
24+
/**
25+
* Sets mirrored output stream, which will be later used for dumping all
26+
* {@link java.sql.ResultSet} generated by this statement, via
27+
* {@link com.clickhouse.data.ClickHouseInputStream#setCopyToTarget(OutputStream)}.
28+
*
29+
* @param out mirrored output stream, could be null
30+
*/
31+
void setMirroredOutput(OutputStream out);
32+
1633
int getNullAsDefault();
1734

1835
void setNullAsDefault(int level);

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/Main.java

+28-15
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.sql.PreparedStatement;
1111
import java.sql.ResultSet;
1212
import java.sql.SQLException;
13-
import java.sql.Statement;
1413
import java.util.Arrays;
1514
import java.util.List;
1615
import java.util.Locale;
@@ -75,7 +74,11 @@ private Options(String url, String query, String file) {
7574
this.query = query;
7675
}
7776
if (file == null || file.isEmpty()) {
78-
this.file = requiresJdbc ? "jdbc.out" : "java.out";
77+
if (output) {
78+
this.file = requiresJdbc ? "jdbc.out" : "java.out";
79+
} else {
80+
this.file = "";
81+
}
7982
} else {
8083
this.file = file;
8184
}
@@ -105,6 +108,10 @@ int getSamples() {
105108
return s;
106109
}
107110

111+
boolean hasFile() {
112+
return !file.isEmpty();
113+
}
114+
108115
boolean isDumpAction() {
109116
return "dump".equals(action);
110117
}
@@ -331,7 +338,7 @@ final long dump() throws ClickHouseException, SQLException {
331338
try (ClickHouseConnection conn = new ClickHouseConnectionImpl(options.url)) {
332339
ClickHouseRequest<?> request = conn.unwrap(ClickHouseRequest.class).query(options.query);
333340
if (!request.getServer().getConfig().hasOption(ClickHouseClientOption.FORMAT)) {
334-
request.format(defaultFormat.defaultInputFormat());
341+
request.format(defaultFormat);
335342
}
336343
request.output(options.file);
337344
try (ClickHouseResponse response = request.executeAndWait()) {
@@ -343,7 +350,7 @@ final long dump() throws ClickHouseException, SQLException {
343350
try (ClickHouseClient client = ClickHouseClient.newInstance(server.getProtocol())) {
344351
ClickHouseRequest<?> request = client.read(server).query(options.query);
345352
if (!server.getConfig().hasOption(ClickHouseClientOption.FORMAT)) {
346-
request.format(defaultFormat.defaultInputFormat());
353+
request.format(defaultFormat);
347354
}
348355
request.output(options.file);
349356
try (ClickHouseResponse response = request.query(options.query).executeAndWait()) {
@@ -389,14 +396,16 @@ final long read(Options options) throws ClickHouseException, SQLException {
389396
final long rows;
390397
if (options.requiresJdbc) {
391398
try (ClickHouseConnection conn = new ClickHouseConnectionImpl(options.url);
392-
Statement stmt = conn.createStatement()) {
393-
try (ResultSet rs = stmt.executeQuery(options.query)) {
399+
ClickHouseStatement stmt = conn.createStatement()) {
400+
if (options.hasFile()) {
394401
try {
395-
rs.unwrap(ClickHouseResponse.class).getInputStream()
396-
.setCopyToTarget(new FileOutputStream(options.file, false)); // NOSONAR
402+
stmt.setMirroredOutput(
403+
!"-".equals(options.file) ? new FileOutputStream(options.file, false) : System.out); // NOSONAR
397404
} catch (IOException e) {
398405
throw SqlExceptionUtils.clientError(e);
399406
}
407+
}
408+
try (ResultSet rs = stmt.executeQuery(options.query)) {
400409
rows = read(rs);
401410
}
402411
}
@@ -408,10 +417,14 @@ final long read(Options options) throws ClickHouseException, SQLException {
408417
request.format(defaultFormat);
409418
}
410419
try (ClickHouseResponse response = request.executeAndWait()) {
411-
try {
412-
response.getInputStream().setCopyToTarget(new FileOutputStream(options.file, false)); // NOSONAR
413-
} catch (IOException e) {
414-
throw ClickHouseException.of(e, server);
420+
if (options.hasFile()) {
421+
try {
422+
response.getInputStream().setCopyToTarget(
423+
!"-".equals(options.file) ? new FileOutputStream(options.file, false)
424+
: System.out); // NOSONAR
425+
} catch (IOException e) {
426+
throw ClickHouseException.of(e, server);
427+
}
415428
}
416429
rows = read(response);
417430
}
@@ -764,14 +777,14 @@ private static void printUsage() {
764777
println("Properties: -Dkey=value [-Dkey=value]*");
765778
println(" action \tAction, one of read(default), write, dump(no deserialization), and load(no serialization)");
766779
println(" batch \tBatch size for JDBC writing, defaults to 1000");
767-
println(" output \tWhether to write raw response into a file(java.out or jdbc.out), defaults to false");
780+
println(" output \tWhether to write raw response into stdout or a file(java.out or jdbc.out), defaults to false");
768781
println(" samples\tSamples, defaults to 500000000");
769782
println(" serde \tWhether to use default serialization/deserializion mechanism in Java client, defaults to true");
770783
println(" type \tPredefined QUERY, one of Int8, UInt64, String, Array, Tuple, Nested, and Mixed");
771784
println(" verbose\tWhether to show logs, defaults to false");
772785
println();
773786
println("Examples:");
774-
println(" - %s 'https://localhost?sslmode=none' 'select 1'",
787+
println(" - %s 'https://localhost?sslmode=none' 'select 1' -",
775788
index > 0 ? (execFile.substring(0, index) + " -Dverbose=true" + execFile.substring(index))
776789
: (execFile + " -Dverbose=true"));
777790
println(" - %s 'jdbc:ch://user:password@localhost:8123/default' 'select 1' output.file", execFile);
@@ -802,7 +815,7 @@ public static void main(String[] args) throws Exception {
802815
final long rows = query.run();
803816
if (options.verbose) {
804817
long elapsedNanos = System.nanoTime() - startTime;
805-
println("Processed %,d rows in %,.2f ms (%,.2f rows/s)", rows, elapsedNanos / 1_000_000D,
818+
println("\nProcessed %,d rows in %,.2f ms (%,.2f rows/s)", rows, elapsedNanos / 1_000_000D,
806819
rows * 1_000_000_000D / elapsedNanos);
807820
}
808821
System.exit(rows > 0L ? 0 : 1);

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/internal/ClickHouseStatementImpl.java

+19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.io.IOException;
44
import java.io.InputStream;
5+
import java.io.OutputStream;
56
import java.io.Serializable;
67
import java.nio.file.Path;
78
import java.sql.ResultSet;
@@ -77,6 +78,7 @@ public class ClickHouseStatementImpl extends JdbcWrapper
7778
private int fetchSize;
7879
private int maxFieldSize;
7980
private long maxRows;
81+
private OutputStream mirroredOutput;
8082
private int nullAsDefault;
8183
private boolean poolable;
8284
private volatile String queryId;
@@ -902,6 +904,23 @@ public ClickHouseConfig getConfig() {
902904
return request.getConfig();
903905
}
904906

907+
@Override
908+
public OutputStream getMirroredOutput() {
909+
return mirroredOutput;
910+
}
911+
912+
@Override
913+
public void setMirroredOutput(OutputStream out) {
914+
if (this.mirroredOutput != null) {
915+
try {
916+
this.mirroredOutput.flush();
917+
} catch (IOException e) {
918+
// ignore
919+
}
920+
}
921+
this.mirroredOutput = out;
922+
}
923+
905924
@Override
906925
public int getNullAsDefault() {
907926
return nullAsDefault;

0 commit comments

Comments
 (0)