Skip to content

Commit e5fe79c

Browse files
authored
Merge pull request #2591 from ClickHouse/jdbc_statement_2532
[JDBC] StatementImpl
2 parents 87db147 + 8a369b5 commit e5fe79c

File tree

5 files changed

+259
-64
lines changed

5 files changed

+259
-64
lines changed

jdbc-v2/src/main/java/com/clickhouse/jdbc/DriverProperties.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.clickhouse.jdbc;
22

3+
import com.clickhouse.client.api.internal.ServerSettings;
4+
35
import java.util.Collections;
46
import java.util.List;
57

@@ -11,12 +13,22 @@
1113
*/
1214
public enum DriverProperties {
1315

14-
IGNORE_UNSUPPORTED_VALUES("jdbc_ignore_unsupported_values", ""),
15-
SCHEMA_TERM("jdbc_schema_term", ""),
16+
/**
17+
* Indicates if driver should ignore unsupported values and methods.
18+
* JDBC allows throwing SQLException for unsupported values and methods.
19+
* But driver can ignore them and continue execution. Driver will do no operation in such case.
20+
*/
21+
IGNORE_UNSUPPORTED_VALUES("jdbc_ignore_unsupported_values", String.valueOf(Boolean.FALSE)),
22+
23+
/**
24+
* Schema term to be used in the connection URL. Only `schema` is supported right now.
25+
*/
26+
SCHEMA_TERM("jdbc_schema_term", "schema"),
27+
1628
/**
1729
* Indicates if driver should create a secure connection over SSL/TLS
1830
*/
19-
SECURE_CONNECTION("ssl", "false"),
31+
SECURE_CONNECTION("ssl", String.valueOf(Boolean.FALSE)),
2032

2133
/**
2234
* Query settings to be passed along with query operation.
@@ -29,12 +41,22 @@ public enum DriverProperties {
2941
* PreparedStatement is used. Has limitation and can be used with a simple form of insert like;
3042
* {@code INSERT INTO t VALUES (?, ?, ?...)}
3143
*/
32-
BETA_ROW_BINARY_WRITER("beta.row_binary_for_simple_insert", "false"),
44+
BETA_ROW_BINARY_WRITER("beta.row_binary_for_simple_insert", String.valueOf(Boolean.FALSE)),
3345

3446
/**
3547
* Enables closing result set before
3648
*/
37-
RESULTSET_AUTO_CLOSE("jdbc_resultset_auto_close", "true"),
49+
RESULTSET_AUTO_CLOSE("jdbc_resultset_auto_close", String.valueOf(Boolean.TRUE)),
50+
51+
/**
52+
* Enables using server property `max_result_rows` ({@link ServerSettings#MAX_RESULT_ROWS} to limit number of rows returned by query.
53+
* Enabling this property will override user set overflow mode. It may cause error if server doesn't allow changing properties.
54+
* When this property is not enabled then result set will stop reading data once limit is reached. As server may have
55+
* more in a result set then it will require time to read all data to make HTTP connection usable again. In most cases
56+
* this is fine. It is recommended to set limit in SQL query.
57+
*
58+
*/
59+
USE_MAX_RESULT_ROWS("jdbc_use_max_result_rows", String.valueOf(Boolean.FALSE)),
3860
;
3961

4062

jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.io.Reader;
1717
import java.io.StringReader;
1818
import java.math.BigDecimal;
19-
import java.net.SocketTimeoutException;
2019
import java.net.URL;
2120
import java.nio.charset.StandardCharsets;
2221
import java.sql.Blob;
@@ -57,6 +56,10 @@ public class ResultSetImpl implements ResultSet, JdbcV2Wrapper {
5756
private int rowPos;
5857

5958
private int fetchSize;
59+
private int fetchDirection;
60+
@SuppressWarnings("unused")
61+
private final int maxFieldSize;
62+
private final int maxRows;
6063

6164
private Consumer<Exception> onDataTransferException;
6265

@@ -77,6 +80,9 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic
7780
this.defaultCalendar = parentStatement.getConnection().defaultCalendar;
7881
this.rowPos = BEFORE_FIRST;
7982
this.fetchSize = parentStatement.getFetchSize();
83+
this.fetchDirection = parentStatement.getFetchDirection();
84+
this.maxFieldSize = parentStatement.getMaxFieldSize();
85+
this.maxRows = parentStatement.getMaxRows();
8086
this.onDataTransferException = onDataTransferException;
8187
}
8288

@@ -103,6 +109,16 @@ private String columnIndexToName(int index) throws SQLException {
103109
public boolean next() throws SQLException {
104110
checkClosed();
105111

112+
if (rowPos == AFTER_LAST) {
113+
return false;
114+
}
115+
116+
if (maxRows > 0 && rowPos == maxRows) {
117+
// rowPos is at current position. if we reached here it means we stepped over maxRows
118+
rowPos = AFTER_LAST;
119+
return false;
120+
}
121+
106122
try {
107123
Object readerRow = reader.next();
108124
if (readerRow != null) {
@@ -543,7 +559,7 @@ public boolean isFirst() throws SQLException {
543559
@Override
544560
public boolean isLast() throws SQLException {
545561
checkClosed();
546-
return !reader.hasNext() && rowPos != AFTER_LAST && rowPos != BEFORE_FIRST;
562+
return (!reader.hasNext() || rowPos == maxRows) && rowPos != AFTER_LAST && rowPos != BEFORE_FIRST;
547563
}
548564

549565
@Override
@@ -607,15 +623,16 @@ public boolean previous() throws SQLException {
607623
@Override
608624
public int getFetchDirection() throws SQLException {
609625
checkClosed();
610-
return FETCH_FORWARD;
626+
return fetchDirection;
611627
}
612628

613629
@Override
614630
public void setFetchDirection(int direction) throws SQLException {
615631
checkClosed();
616-
if (direction != ResultSet.FETCH_FORWARD) {
632+
if (getType() == TYPE_FORWARD_ONLY && direction != ResultSet.FETCH_FORWARD) {
617633
throw new SQLException("This result set object is of FORWARD ONLY type. Only ResultSet.FETCH_FORWARD is allowed as fetchDirection.");
618634
}
635+
fetchDirection = direction;
619636
}
620637

621638
@Override

jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.clickhouse.client.api.query.QuerySettings;
88
import com.clickhouse.client.api.sql.SQLUtils;
99
import com.clickhouse.jdbc.internal.ExceptionUtils;
10+
import com.clickhouse.jdbc.internal.FeatureManager;
1011
import com.clickhouse.jdbc.internal.ParsedStatement;
1112
import org.slf4j.Logger;
1213
import org.slf4j.LoggerFactory;
@@ -29,6 +30,7 @@ public class StatementImpl implements Statement, JdbcV2Wrapper {
2930
ConnectionImpl connection;
3031
protected int queryTimeout;
3132
protected boolean isPoolable = false; // Statement is not poolable by default
33+
private final FeatureManager featureManager;
3234

3335
// State
3436
private volatile boolean closed;
@@ -41,7 +43,7 @@ public class StatementImpl implements Statement, JdbcV2Wrapper {
4143
protected volatile String lastQueryId;
4244
private long maxRows;
4345
private boolean closeOnCompletion;
44-
private boolean resultSetAutoClose;
46+
private final boolean resultSetAutoClose;
4547
private int maxFieldSize;
4648
private boolean escapeProcessingEnabled;
4749

@@ -60,6 +62,7 @@ public StatementImpl(ConnectionImpl connection) throws SQLException {
6062
this.resultSets= new ConcurrentLinkedQueue<>();
6163
this.resultSetAutoClose = connection.getJdbcConfig().isSet(DriverProperties.RESULTSET_AUTO_CLOSE);
6264
this.escapeProcessingEnabled = true;
65+
this.featureManager = new FeatureManager(connection.getJdbcConfig());
6366
}
6467

6568
protected void ensureOpen() throws SQLException {
@@ -323,6 +326,7 @@ public void clearWarnings() throws SQLException {
323326

324327
@Override
325328
public void setCursorName(String name) throws SQLException {
329+
featureManager.unsupportedFeatureThrow("setCursorName(String)", true);
326330
ensureOpen();
327331
}
328332

@@ -458,22 +462,22 @@ public boolean getMoreResults(int current) throws SQLException {
458462
return false; // false indicates that no more results (or it is an update count)
459463
}
460464

461-
// @Override
465+
// @Override -- because doesn't exist in Java 8
462466
public String enquoteLiteral(String val) throws SQLException {
463467
return SQLUtils.enquoteLiteral(val);
464468
}
465469

466-
// @Override
470+
// @Override -- because doesn't exist in Java 8
467471
public String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException {
468472
return SQLUtils.enquoteIdentifier(identifier, alwaysQuote);
469473
}
470474

471-
// @Override
475+
// @Override -- because doesn't exist in Java 8
472476
public boolean isSimpleIdentifier(String identifier) throws SQLException {
473477
return SQLUtils.isSimpleIdentifier(identifier);
474478
}
475479

476-
// @Override
480+
// @Override -- because doesn't exist in Java 8
477481
public String enquoteNCharLiteral(String val) throws SQLException {
478482
if (val == null) {
479483
throw new NullPointerException();
@@ -489,31 +493,37 @@ public ResultSet getGeneratedKeys() throws SQLException {
489493

490494
@Override
491495
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
496+
featureManager.unsupportedFeatureThrow("executeUpdate(String, int)", autoGeneratedKeys != Statement.NO_GENERATED_KEYS);
492497
return executeUpdate(sql);
493498
}
494499

495500
@Override
496501
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
502+
featureManager.unsupportedFeatureThrow("executeUpdate(String, int[])");
497503
return executeUpdate(sql);
498504
}
499505

500506
@Override
501507
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
508+
featureManager.unsupportedFeatureThrow("executeUpdate(String, String[])");
502509
return executeUpdate(sql);
503510
}
504511

505512
@Override
506513
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
514+
featureManager.unsupportedFeatureThrow("execute(String, int)", autoGeneratedKeys != Statement.NO_GENERATED_KEYS);
507515
return execute(sql);
508516
}
509517

510518
@Override
511519
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
520+
featureManager.unsupportedFeatureThrow("execute(String, int[])");
512521
return execute(sql);
513522
}
514523

515524
@Override
516525
public boolean execute(String sql, String[] columnNames) throws SQLException {
526+
featureManager.unsupportedFeatureThrow("execute(String, String[])");
517527
return execute(sql);
518528
}
519529

@@ -573,20 +583,23 @@ public long getLargeUpdateCount() throws SQLException {
573583
public void setLargeMaxRows(long max) throws SQLException {
574584
ensureOpen();
575585
maxRows = max;
576-
// This method override user set overflow mode on purpose:
577-
// 1. Spec clearly states that after calling this method with a limit > 0 all rows over limit are dropped.
578-
// 2. Calling this method should not cause throwing exception for future queries what only `break` can guarantee
579-
// 3. If user wants different behavior then they are can use connection properties.
580-
if (max > 0) {
581-
localSettings.setOption(ClientConfigProperties.serverSetting(ServerSettings.MAX_RESULT_ROWS), maxRows);
582-
localSettings.setOption(ClientConfigProperties.serverSetting(ServerSettings.RESULT_OVERFLOW_MODE),
583-
ServerSettings.RESULT_OVERFLOW_MODE_BREAK);
584-
} else {
585-
// overriding potential client settings (set thru connection setup)
586-
// there is no no limit value so we use very large limit.
587-
localSettings.setOption(ClientConfigProperties.serverSetting(ServerSettings.MAX_RESULT_ROWS), Long.MAX_VALUE);
588-
localSettings.setOption(ClientConfigProperties.serverSetting(ServerSettings.RESULT_OVERFLOW_MODE),
589-
ServerSettings.RESULT_OVERFLOW_MODE_BREAK);
586+
587+
if (connection.getJdbcConfig().isFlagSet(DriverProperties.USE_MAX_RESULT_ROWS)) {
588+
// This method override user set overflow mode on purpose:
589+
// 1. Spec clearly states that after calling this method with a limit > 0 all rows over limit are dropped.
590+
// 2. Calling this method should not cause throwing exception for future queries what only `break` can guarantee
591+
// 3. If user wants different behavior then they are can use connection properties.
592+
if (max > 0) {
593+
localSettings.setOption(ClientConfigProperties.serverSetting(ServerSettings.MAX_RESULT_ROWS), maxRows);
594+
localSettings.setOption(ClientConfigProperties.serverSetting(ServerSettings.RESULT_OVERFLOW_MODE),
595+
ServerSettings.RESULT_OVERFLOW_MODE_BREAK);
596+
} else {
597+
// overriding potential client settings (set thru connection setup)
598+
// there is no no limit value so we use very large limit.
599+
localSettings.setOption(ClientConfigProperties.serverSetting(ServerSettings.MAX_RESULT_ROWS), Long.MAX_VALUE);
600+
localSettings.setOption(ClientConfigProperties.serverSetting(ServerSettings.RESULT_OVERFLOW_MODE),
601+
ServerSettings.RESULT_OVERFLOW_MODE_BREAK);
602+
}
590603
}
591604
}
592605

@@ -611,16 +624,19 @@ public long executeLargeUpdate(String sql) throws SQLException {
611624

612625
@Override
613626
public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
627+
featureManager.unsupportedFeatureThrow("executeLargeUpdate(String, int)", autoGeneratedKeys != Statement.NO_GENERATED_KEYS);
614628
return executeLargeUpdate(sql);
615629
}
616630

617631
@Override
618632
public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException {
633+
featureManager.unsupportedFeatureThrow("executeLargeUpdate(String, int[])");
619634
return executeLargeUpdate(sql);
620635
}
621636

622637
@Override
623638
public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
639+
featureManager.unsupportedFeatureThrow("executeLargeUpdate(String, String[])");
624640
return executeLargeUpdate(sql);
625641
}
626642

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcConfiguration.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ public static String getDefaultClientName() {
309309
}
310310

311311
public boolean isBetaFeatureEnabled(DriverProperties prop) {
312+
return isFlagSet(prop);
313+
}
314+
315+
public boolean isFlagSet(DriverProperties prop) {
312316
String value = driverProperties.getOrDefault(prop.getKey(), prop.getDefaultValue());
313317
return Boolean.parseBoolean(value);
314318
}
@@ -318,4 +322,8 @@ private Map<ClickHouseDataType, Class<?>> defaultTypeHintMapping() {
318322
mapping.put(ClickHouseDataType.Array, List.class);
319323
return mapping;
320324
}
325+
326+
public boolean useMaxResultRows() {
327+
return isFlagSet(DriverProperties.USE_MAX_RESULT_ROWS);
328+
}
321329
}

0 commit comments

Comments
 (0)