11package 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+
38import java .io .IOException ;
49import java .net .InetSocketAddress ;
510import java .net .Socket ;
1621import java .sql .SQLClientInfoException ;
1722import java .sql .SQLException ;
1823import java .sql .SQLFeatureNotSupportedException ;
24+ import java .sql .SQLNonTransientConnectionException ;
25+ import java .sql .SQLNonTransientException ;
1926import java .sql .SQLWarning ;
2027import java .sql .SQLXML ;
2128import java .sql .Savepoint ;
2734import java .util .Properties ;
2835import java .util .concurrent .Executor ;
2936
30- import org .tarantool .CommunicationException ;
31- import org .tarantool .JDBCBridge ;
32- import org .tarantool .TarantoolConnection ;
33-
3437import static org .tarantool .jdbc .SQLDriver .PROP_HOST ;
3538import static org .tarantool .jdbc .SQLDriver .PROP_PASSWORD ;
3639import static org .tarantool .jdbc .SQLDriver .PROP_PORT ;
3942
4043@ SuppressWarnings ("Since15" )
4144public 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