Skip to content

Commit 268eafa

Browse files
dponomarevnicktorwald
dponomarev
authored andcommitted
jdbc: add support for a result set holdability
Right now we only can support HOLD_CURSORS_OVER_COMMIT due to lack of Tarantool transaction support and, possibly, cursors. So, in terms of HOLD_CURSORS_OVER_COMMIT we always load a result set completely and it can be used as long as it is opened. Implement the holdability support (next HS) for SQLDatabaseMetaData in part of getting a default holdability and checking proper support. Implement HS for SQLConnection in part of producing new statements and prepared statements (but excluding CallableStatements due to lack of implementation). Implement HS for SQL(Prepared)Statement as well as for SQLResultSet which is produced by the statements. Some part of this feature requires the implementation of java.sql.Wrapper for SQLConnection and SQL(Prepared)Statement. So now they fully implement the interface. Add missed checks for an open status of JDBC components which are required by the specification. Some methods start returning appropriate SQLException subclasses when corresponding errors occur. Add SQLStates enumeration to help to produce the errors with the standard SQL states. Finally, JDBCBridge is no longer aware of the SQLResultSet class. Only Statement implementations are responsible for building of new result sets according to the specification design. Next plan is to completely avoid JDBCBridge logic and transfer it to an inner helper inside the Statement implementations. Closes: #87 Affects: #73, #119
1 parent 9b3f91f commit 268eafa

12 files changed

+544
-187
lines changed

Diff for: src/main/java/org/tarantool/JDBCBridge.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.tarantool.protocol.TarantoolPacket;
1212

1313
public class JDBCBridge {
14+
1415
public static final JDBCBridge EMPTY = new JDBCBridge(Collections.<TarantoolBase.SQLMetaData>emptyList(), Collections.<List<Object>>emptyList());
1516

1617
final List<TarantoolBase.SQLMetaData> sqlMetadata;
@@ -41,7 +42,7 @@ public static int update(TarantoolConnection connection, String sql, Object ...
4142

4243
public static JDBCBridge mock(List<String> fields, List<List<Object>> values) {
4344
List<TarantoolBase.SQLMetaData> meta = new ArrayList<TarantoolBase.SQLMetaData>(fields.size());
44-
for(String field:fields) {
45+
for(String field : fields) {
4546
meta.add(new TarantoolBase.SQLMetaData(field));
4647
}
4748
return new JDBCBridge(meta, values);
@@ -51,7 +52,7 @@ public static Object execute(TarantoolConnection connection, String sql, Object
5152
TarantoolPacket pack = connection.sql(sql, params);
5253
Long rowCount = SqlProtoUtils.getSqlRowCount(pack);
5354
if(rowCount == null) {
54-
return new SQLResultSet(new JDBCBridge(pack));
55+
return new JDBCBridge(pack);
5556
}
5657
return rowCount.intValue();
5758
}

Diff for: src/main/java/org/tarantool/jdbc/SQLConnection.java

+97-41
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package org.tarantool.jdbc;
22

3+
import org.tarantool.CommunicationException;
4+
import org.tarantool.JDBCBridge;
5+
import org.tarantool.TarantoolConnection;
6+
import org.tarantool.util.SQLStates;
7+
38
import java.io.IOException;
49
import java.net.InetSocketAddress;
510
import java.net.Socket;
@@ -16,6 +21,8 @@
1621
import java.sql.SQLClientInfoException;
1722
import java.sql.SQLException;
1823
import java.sql.SQLFeatureNotSupportedException;
24+
import java.sql.SQLNonTransientConnectionException;
25+
import java.sql.SQLNonTransientException;
1926
import java.sql.SQLWarning;
2027
import java.sql.SQLXML;
2128
import java.sql.Savepoint;
@@ -27,10 +34,6 @@
2734
import java.util.Properties;
2835
import java.util.concurrent.Executor;
2936

30-
import org.tarantool.CommunicationException;
31-
import org.tarantool.JDBCBridge;
32-
import org.tarantool.TarantoolConnection;
33-
3437
import static org.tarantool.jdbc.SQLDriver.PROP_HOST;
3538
import static org.tarantool.jdbc.SQLDriver.PROP_PASSWORD;
3639
import static org.tarantool.jdbc.SQLDriver.PROP_PORT;
@@ -39,9 +42,17 @@
3942

4043
@SuppressWarnings("Since15")
4144
public class SQLConnection implements Connection {
45+
46+
private static final int UNSET_HOLDABILITY = 0;
47+
4248
private final TarantoolConnection connection;
43-
final String url;
44-
final Properties properties;
49+
50+
private final String url;
51+
private final Properties properties;
52+
53+
private DatabaseMetaData cachedMetadata;
54+
55+
private int resultSetHoldability = UNSET_HOLDABILITY;
4556

4657
SQLConnection(String url, Properties properties) throws SQLException {
4758
this.url = url;
@@ -62,20 +73,20 @@ public class SQLConnection implements Connection {
6273
}
6374
}
6475
if (e instanceof SQLException)
65-
throw (SQLException)e;
76+
throw (SQLException) e;
6677
throw new SQLException("Couldn't initiate connection using " + SQLDriver.diagProperties(properties), e);
6778
}
6879
}
6980

7081
/**
7182
* Provides a connected socket to be used to initialize a native tarantool
7283
* connection.
73-
*
84+
* <p>
7485
* The implementation assumes that {@link #properties} contains all the
7586
* necessary info extracted from both the URI and connection properties
7687
* provided by the user. However, the overrides are free to also use the
7788
* {@link #url} if required.
78-
*
89+
* <p>
7990
* A connect is guarded with user provided timeout. Socket is configured
8091
* to honor this timeout for the following read/write operations as well.
8192
*
@@ -111,7 +122,7 @@ protected Socket getConnectedSocket() throws SQLException {
111122
/**
112123
* Provides a newly connected socket instance. The method is intended to be
113124
* overridden to enable unit testing of the class.
114-
*
125+
* <p>
115126
* Not supposed to contain any logic other than a call to constructor.
116127
*
117128
* @return socket.
@@ -123,11 +134,11 @@ protected Socket makeSocket() {
123134
/**
124135
* Provides a native tarantool connection instance. The method is intended
125136
* to be overridden to enable unit testing of the class.
126-
*
137+
* <p>
127138
* Not supposed to contain any logic other than a call to constructor.
128139
*
129-
* @param user User name.
130-
* @param pass Password.
140+
* @param user User name.
141+
* @param pass Password.
131142
* @param socket Connected socket.
132143
* @return Native tarantool connection.
133144
* @throws IOException if failed.
@@ -140,14 +151,12 @@ protected TarantoolConnection makeConnection(String user, String pass, Socket so
140151

141152
@Override
142153
public Statement createStatement() throws SQLException {
143-
checkNotClosed();
144-
return new SQLStatement(this);
154+
return createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
145155
}
146156

147157
@Override
148158
public PreparedStatement prepareStatement(String sql) throws SQLException {
149-
checkNotClosed();
150-
return new SQLPreparedStatement(this, sql);
159+
return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
151160
}
152161

153162
@Override
@@ -196,7 +205,10 @@ public boolean isClosed() throws SQLException {
196205
@Override
197206
public DatabaseMetaData getMetaData() throws SQLException {
198207
checkNotClosed();
199-
return new SQLDatabaseMetadata(this);
208+
if (cachedMetadata == null) {
209+
cachedMetadata = new SQLDatabaseMetadata(this);
210+
}
211+
return cachedMetadata;
200212
}
201213

202214
@Override
@@ -242,13 +254,13 @@ public void clearWarnings() throws SQLException {
242254

243255
@Override
244256
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
245-
throw new SQLFeatureNotSupportedException();
257+
return createStatement(resultSetType, resultSetConcurrency, getHoldability());
246258
}
247259

248260
@Override
249261
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
250262
throws SQLException {
251-
throw new SQLFeatureNotSupportedException();
263+
return prepareStatement(sql, resultSetType, resultSetConcurrency, getHoldability());
252264
}
253265

254266
@Override
@@ -268,12 +280,18 @@ public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
268280

269281
@Override
270282
public void setHoldability(int holdability) throws SQLException {
271-
throw new SQLFeatureNotSupportedException();
283+
checkNotClosed();
284+
checkHoldabilitySupport(holdability);
285+
resultSetHoldability = holdability;
272286
}
273287

274288
@Override
275289
public int getHoldability() throws SQLException {
276-
throw new SQLFeatureNotSupportedException();
290+
checkNotClosed();
291+
if (resultSetHoldability == UNSET_HOLDABILITY) {
292+
resultSetHoldability = getMetaData().getResultSetHoldability();
293+
}
294+
return resultSetHoldability;
277295
}
278296

279297
@Override
@@ -297,15 +315,22 @@ public void releaseSavepoint(Savepoint savepoint) throws SQLException {
297315
}
298316

299317
@Override
300-
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
301-
throws SQLException {
302-
throw new SQLFeatureNotSupportedException();
318+
public Statement createStatement(int resultSetType,
319+
int resultSetConcurrency,
320+
int resultSetHoldability) throws SQLException {
321+
checkNotClosed();
322+
checkHoldabilitySupport(resultSetHoldability);
323+
return new SQLStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability);
303324
}
304325

305326
@Override
306-
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
307-
throws SQLException {
308-
throw new SQLFeatureNotSupportedException();
327+
public PreparedStatement prepareStatement(String sql,
328+
int resultSetType,
329+
int resultSetConcurrency,
330+
int resultSetHoldability) throws SQLException {
331+
checkNotClosed();
332+
checkHoldabilitySupport(resultSetHoldability);
333+
return new SQLPreparedStatement(this, sql, resultSetType, resultSetConcurrency, resultSetHoldability);
309334
}
310335

311336
@Override
@@ -423,16 +448,19 @@ public int getNetworkTimeout() throws SQLException {
423448
}
424449

425450
@Override
426-
public <T> T unwrap(Class<T> iface) throws SQLException {
427-
throw new SQLFeatureNotSupportedException();
451+
public <T> T unwrap(Class<T> type) throws SQLException {
452+
if (isWrapperFor(type)) {
453+
return type.cast(this);
454+
}
455+
throw new SQLNonTransientException("Connection does not wrap " + type.getName());
428456
}
429457

430458
@Override
431-
public boolean isWrapperFor(Class<?> iface) throws SQLException {
432-
throw new SQLFeatureNotSupportedException();
459+
public boolean isWrapperFor(Class<?> type) throws SQLException {
460+
return type.isAssignableFrom(this.getClass());
433461
}
434462

435-
protected Object execute(String sql, Object ... args) throws SQLException {
463+
protected Object execute(String sql, Object... args) throws SQLException {
436464
checkNotClosed();
437465
try {
438466
return JDBCBridge.execute(connection, sql, args);
@@ -442,17 +470,17 @@ protected Object execute(String sql, Object ... args) throws SQLException {
442470
}
443471
}
444472

445-
protected ResultSet executeQuery(String sql, Object ... args) throws SQLException {
473+
protected JDBCBridge executeQuery(String sql, Object... args) throws SQLException {
446474
checkNotClosed();
447475
try {
448-
return new SQLResultSet(JDBCBridge.query(connection, sql, args));
476+
return JDBCBridge.query(connection, sql, args);
449477
} catch (Exception e) {
450478
handleException(e);
451479
throw new SQLException(formatError(sql, args), e);
452480
}
453481
}
454482

455-
protected int executeUpdate(String sql, Object ... args) throws SQLException {
483+
protected int executeUpdate(String sql, Object... args) throws SQLException {
456484
checkNotClosed();
457485
try {
458486
return JDBCBridge.update(connection, sql, args);
@@ -463,7 +491,7 @@ protected int executeUpdate(String sql, Object ... args) throws SQLException {
463491
}
464492

465493
protected List<?> nativeSelect(Integer space, Integer index, List<?> key, int offset, int limit, int iterator)
466-
throws SQLException {
494+
throws SQLException {
467495
checkNotClosed();
468496
try {
469497
return connection.select(space, index, key, offset, limit, iterator);
@@ -482,7 +510,18 @@ protected String getServerVersion() {
482510
*/
483511
protected void checkNotClosed() throws SQLException {
484512
if (isClosed())
485-
throw new SQLException("Connection is closed.");
513+
throw new SQLNonTransientConnectionException(
514+
"Connection is closed.",
515+
SQLStates.CONNECTION_DOES_NOT_EXIST.getSqlState()
516+
);
517+
}
518+
519+
String getUrl() {
520+
return url;
521+
}
522+
523+
Properties getProperties() {
524+
return properties;
486525
}
487526

488527
/**
@@ -492,7 +531,7 @@ protected void checkNotClosed() throws SQLException {
492531
*/
493532
private void handleException(Exception e) {
494533
if (CommunicationException.class.isAssignableFrom(e.getClass()) ||
495-
IOException.class.isAssignableFrom(e.getClass())) {
534+
IOException.class.isAssignableFrom(e.getClass())) {
496535
try {
497536
close();
498537
} catch (SQLException ignored) {
@@ -501,14 +540,31 @@ private void handleException(Exception e) {
501540
}
502541
}
503542

543+
/**
544+
* Checks whether <code>holdability</code> is supported
545+
*
546+
* @param holdability param to be checked
547+
* @throws SQLFeatureNotSupportedException param is not supported
548+
* @throws SQLNonTransientException param has invalid value
549+
*/
550+
private void checkHoldabilitySupport(int holdability) throws SQLException {
551+
if (holdability != ResultSet.CLOSE_CURSORS_AT_COMMIT
552+
&& holdability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
553+
throw new SQLNonTransientException("", SQLStates.INVALID_PARAMETER_VALUE.getSqlState());
554+
}
555+
if (!getMetaData().supportsResultSetHoldability(holdability)) {
556+
throw new SQLFeatureNotSupportedException();
557+
}
558+
}
559+
504560
/**
505561
* Provides error message that contains parameters of failed SQL statement.
506562
*
507-
* @param sql SQL Text.
563+
* @param sql SQL Text.
508564
* @param params Parameters of the SQL statement.
509565
* @return Formatted error message.
510566
*/
511-
private static String formatError(String sql, Object ... params) {
567+
private static String formatError(String sql, Object... params) {
512568
return "Failed to execute SQL: " + sql + ", params: " + Arrays.deepToString(params);
513569
}
514570
}

0 commit comments

Comments
 (0)