Skip to content

Commit c25103a

Browse files
authored
Merge pull request #1285 from zhicwu/main
TCL support
2 parents d7fd089 + d39550c commit c25103a

File tree

11 files changed

+275
-29
lines changed

11 files changed

+275
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 0.4.2
22

33
### New Features
4+
* support `BEGIN TRANSACTION`, `COMMIT`, and `ROLLBACK` in JDBC driver. [#975](https://github.com/ClickHouse/clickhouse-java/issues/975)
45
* new options for JDBC driver
56
* databaseTerm(catalog or schema, defaults to schema) [#1273](https://github.com/ClickHouse/clickhouse-java/issues/1273)
67
* externalDatabase(true or false, defaults to true) [#1245](https://github.com/ClickHouse/clickhouse-java/issues/1245)

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ public String toString() {
119119

120120
static final String QUERY_SELECT_TX_ID = "SELECT transactionID()";
121121

122+
public static final String COMMAND_BEGIN = "BEGIN";
123+
public static final String COMMAND_COMMIT = "COMMIT";
124+
public static final String COMMAND_ROLLBACK = "ROLLBACK";
125+
122126
// transaction state
123127
public static final int NEW = 0;
124128
public static final int ACTIVE = 1;
@@ -526,7 +530,7 @@ public void commit(Map<String, Serializable> settings) throws ClickHouseExceptio
526530
boolean success = false;
527531
try {
528532
ensureTransactionId();
529-
issue("COMMIT", true, settings);
533+
issue(COMMAND_COMMIT, true, settings);
530534
success = state.compareAndSet(currentState, COMMITTED);
531535
return x;
532536
} catch (ClickHouseException e) {
@@ -582,7 +586,7 @@ public void rollback(Map<String, Serializable> settings) throws ClickHouseExcept
582586
boolean success = false;
583587
try {
584588
ensureTransactionId();
585-
issue("ROLLBACK", true, settings);
589+
issue(COMMAND_ROLLBACK, true, settings);
586590
success = state.compareAndSet(currentState, ROLLED_BACK);
587591
return x;
588592
} catch (ClickHouseException e) {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ default PreparedStatement prepareStatement(String sql, int resultSetType, int re
151151
return prepareStatement(sql, resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT);
152152
}
153153

154+
/**
155+
* Starts a new transaction. It's no-op for a newly started transaction.
156+
*
157+
* @throws SQLException when current transaction is active state or not able to
158+
* start new transaction
159+
*/
160+
void begin() throws SQLException;
161+
154162
/**
155163
* Gets configuration tied to this connection.
156164
*

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ public void setAutoCommit(boolean autoCommit) throws SQLException {
436436
}
437437
} else { // start new transaction
438438
if (!txRef.compareAndSet(null, createTransaction())) {
439-
log.warn("[JDBC Compliant Mode] not able to start a new transaction, reuse the exist one");
439+
log.warn("Not able to start a new transaction, reuse the exist one: %s", txRef.get());
440440
}
441441
}
442442
}
@@ -449,9 +449,22 @@ public boolean getAutoCommit() throws SQLException {
449449
}
450450

451451
@Override
452-
public void commit() throws SQLException {
453-
ensureOpen();
452+
public void begin() throws SQLException {
453+
if (getAutoCommit()) {
454+
throw SqlExceptionUtils.clientError("Cannot start new transaction in auto-commit mode");
455+
}
454456

457+
ensureTransactionSupport();
458+
459+
JdbcTransaction tx = txRef.get();
460+
if (tx == null || !tx.isNew()) {
461+
// invalid transaction state
462+
throw new SQLException(JdbcTransaction.ERROR_TX_STARTED, SqlExceptionUtils.SQL_STATE_INVALID_TX_STATE);
463+
}
464+
}
465+
466+
@Override
467+
public void commit() throws SQLException {
455468
if (getAutoCommit()) {
456469
throw SqlExceptionUtils.clientError("Cannot commit in auto-commit mode");
457470
}
@@ -475,8 +488,6 @@ public void commit() throws SQLException {
475488

476489
@Override
477490
public void rollback() throws SQLException {
478-
ensureOpen();
479-
480491
if (getAutoCommit()) {
481492
throw SqlExceptionUtils.clientError("Cannot rollback in auto-commit mode");
482493
}

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.Serializable;
66
import java.sql.ResultSet;
77
import java.sql.SQLException;
8+
import java.sql.SQLFeatureNotSupportedException;
89
import java.sql.SQLWarning;
910
import java.sql.Statement;
1011
import java.util.ArrayList;
@@ -22,6 +23,7 @@
2223
import com.clickhouse.client.ClickHouseRequest;
2324
import com.clickhouse.client.ClickHouseResponse;
2425
import com.clickhouse.client.ClickHouseResponseSummary;
26+
import com.clickhouse.client.ClickHouseTransaction;
2527
import com.clickhouse.client.ClickHouseRequest.Mutation;
2628
import com.clickhouse.client.config.ClickHouseClientOption;
2729
import com.clickhouse.config.ClickHouseConfigChangeListener;
@@ -93,6 +95,12 @@ private ClickHouseResponse getLastResponse(Map<ClickHouseOption, Serializable> o
9395
ClickHouseResponse response = null;
9496
for (int i = 0, len = parsedStmts.length; i < len; i++) {
9597
ClickHouseSqlStatement stmt = parsedStmts[i];
98+
response = processSqlStatement(stmt);
99+
if (response != null) {
100+
updateResult(stmt, response);
101+
continue;
102+
}
103+
96104
if (stmt.hasFormat()) {
97105
request.format(ClickHouseFormat.valueOf(stmt.getFormat()));
98106
}
@@ -124,6 +132,23 @@ protected void ensureOpen() throws SQLException {
124132
}
125133
}
126134

135+
protected ClickHouseResponse processSqlStatement(ClickHouseSqlStatement stmt) throws SQLException {
136+
if (stmt.isTCL()) {
137+
if (stmt.containsKeyword(ClickHouseTransaction.COMMAND_BEGIN)) {
138+
connection.begin();
139+
} else if (stmt.containsKeyword(ClickHouseTransaction.COMMAND_COMMIT)) {
140+
connection.commit();
141+
} else if (stmt.containsKeyword(ClickHouseTransaction.COMMAND_ROLLBACK)) {
142+
connection.rollback();
143+
} else {
144+
throw new SQLFeatureNotSupportedException("Unsupported TCL: " + stmt.getSQL());
145+
}
146+
return ClickHouseResponse.EMPTY;
147+
}
148+
149+
return null;
150+
}
151+
127152
protected ClickHouseResponse executeStatement(String stmt, Map<ClickHouseOption, Serializable> options,
128153
List<ClickHouseExternalTable> tables, Map<String, String> settings) throws SQLException {
129154
boolean autoTx = connection.getAutoCommit() && connection.isTransactionSupported();
@@ -175,6 +200,10 @@ protected ClickHouseResponse executeStatement(String stmt, Map<ClickHouseOption,
175200
protected ClickHouseResponse executeStatement(ClickHouseSqlStatement stmt,
176201
Map<ClickHouseOption, Serializable> options, List<ClickHouseExternalTable> tables,
177202
Map<String, String> settings) throws SQLException {
203+
ClickHouseResponse resp = processSqlStatement(stmt);
204+
if (resp != null) {
205+
return resp;
206+
}
178207
return executeStatement(stmt.getSQL(), options, tables, settings);
179208
}
180209

@@ -261,7 +290,7 @@ protected ResultSet updateResult(ClickHouseSqlStatement stmt, ClickHouseResponse
261290
stmt.getTable(), this, response);
262291
} else {
263292
response.close();
264-
currentUpdateCount = stmt.isDDL() ? 0L
293+
currentUpdateCount = stmt.isDDL() || stmt.isTCL() ? 0L
265294
: (response.getSummary().isEmpty() ? 1L : response.getSummary().getWrittenRows());
266295
currentResult = null;
267296
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class JdbcTransaction {
1919
static final String ACTION_ROLLBACK = "rolled back";
2020

2121
static final String ERROR_TX_NOT_STARTED = "Transaction not started";
22+
static final String ERROR_TX_STARTED = "Transaction has been started";
2223

2324
protected final ClickHouseTransaction tx;
2425
protected final String id;
@@ -36,6 +37,11 @@ public JdbcTransaction(ClickHouseTransaction tx) {
3637
this.savepoints = new LinkedList<>();
3738
}
3839

40+
public boolean isNew() {
41+
return this.queries.isEmpty() && this.savepoints.isEmpty()
42+
&& (this.tx == null || this.tx.isNew() || this.tx.isActive());
43+
}
44+
3945
public void commit(Logger log) throws SQLException {
4046
if (this.tx != null) {
4147
try {

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/parser/ClickHouseSqlStatement.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ public boolean isMutation() {
130130
return this.stmtType.getOperationType() == OperationType.WRITE || this.hasOutfile();
131131
}
132132

133+
public boolean isTCL() {
134+
return this.stmtType.getLanguageType() == LanguageType.TCL;
135+
}
136+
133137
public boolean isIdemponent() {
134138
boolean result = this.stmtType.isIdempotent() && !this.hasOutfile();
135139

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/parser/StatementType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public enum StatementType {
2727
TRUNCATE(LanguageType.DDL, OperationType.UNKNOWN, true), // truncate statement
2828
UPDATE(LanguageType.DML, OperationType.WRITE, false), // the upcoming light-weight update statement
2929
USE(LanguageType.DDL, OperationType.UNKNOWN, true), // use statement
30-
WATCH(LanguageType.DDL, OperationType.UNKNOWN, true); // watch statement
30+
WATCH(LanguageType.DDL, OperationType.UNKNOWN, true), // watch statement
31+
TRANSACTION(LanguageType.TCL, OperationType.WRITE, true); // TCL statement
3132

3233
private LanguageType langType;
3334
private OperationType opType;

clickhouse-jdbc/src/main/javacc/ClickHouseSqlParser.jj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ void stmt(): {} {
382382
| updateStmt() { token_source.stmtType = StatementType.UPDATE; }
383383
| useStmt() { token_source.stmtType = StatementType.USE; }
384384
| watchStmt() { token_source.stmtType = StatementType.WATCH; }
385+
| txStmt() { token_source.stmtType = StatementType.TRANSACTION; }
385386
}
386387

387388
// https://clickhouse.tech/docs/en/sql-reference/statements/alter/
@@ -641,6 +642,13 @@ void watchStmt(): {} { // not interested
641642
<WATCH> anyExprList()
642643
}
643644

645+
// TCL
646+
void txStmt(): {} { // not interested
647+
<COMMIT> { token_source.addPosition(token); }
648+
| <ROLLBACK> { token_source.addPosition(token); }
649+
| (<BEGIN> { token_source.addPosition(token); } <TRANSACTION>)
650+
}
651+
644652
// columns
645653
void columnExprList(): {} {
646654
columnsExpr() (<COMMA> columnsExpr())*
@@ -995,6 +1003,9 @@ TOKEN: {
9951003
| <UPDATE : <U> <P> <D> <A> <T> <E> >
9961004
| <USE : <U> <S> <E> >
9971005
| <WATCH : <W> <A> <T> <C> <H> >
1006+
| <BEGIN : <B> <E> <G> <I> <N> >
1007+
| <COMMIT : <C> <O> <M> <M> <I> <T> >
1008+
| <ROLLBACK : <R> <O> <L> <L> <B> <A> <C> <K>>
9981009

9991010
| <ALL : <A> <L> <L> >
10001011
| <AND : <A> <N> <D> >
@@ -1060,6 +1071,7 @@ TOKEN: {
10601071
| <TIMESTAMP: <T> <I> <M> <E> <S> <T> <A> <M> <P>>
10611072
| <TOP : <T> <O> <P> >
10621073
| <TOTALS : <T> <O> <T> <A> <L> <S> >
1074+
| <TRANSACTION : <T> <R> <A> <N> <S> <A> <C> <T> <I> <O> <N>>
10631075
| <UNION : <U> <N> <I> <O> <N> >
10641076
| <USER : <U> <S> <E> <R> >
10651077
| <USING : <U> <S> <I> <N> <G> >

0 commit comments

Comments
 (0)