1212import java .sql .Array ;
1313import java .sql .Blob ;
1414import java .sql .CallableStatement ;
15+ import java .sql .ClientInfoStatus ;
1516import java .sql .Clob ;
1617import java .sql .Connection ;
1718import java .sql .DatabaseMetaData ;
2930import java .sql .Statement ;
3031import java .sql .Struct ;
3132import java .util .Arrays ;
33+ import java .util .Collection ;
34+ import java .util .Collections ;
35+ import java .util .HashMap ;
3236import java .util .List ;
3337import java .util .Map ;
3438import java .util .Properties ;
4044import static org .tarantool .jdbc .SQLDriver .PROP_SOCKET_TIMEOUT ;
4145import static org .tarantool .jdbc .SQLDriver .PROP_USER ;
4246
43- @ SuppressWarnings ("Since15" )
4447public class SQLConnection implements Connection {
4548
4649 private static final int UNSET_HOLDABILITY = 0 ;
50+ private static final String PING_QUERY = "SELECT 1" ;
4751
4852 private final TarantoolConnection connection ;
4953
@@ -91,6 +95,7 @@ public class SQLConnection implements Connection {
9195 * to honor this timeout for the following read/write operations as well.
9296 *
9397 * @return Connected socket.
98+ *
9499 * @throws SQLException if failed.
95100 */
96101 protected Socket getConnectedSocket () throws SQLException {
@@ -140,7 +145,9 @@ protected Socket makeSocket() {
140145 * @param user User name.
141146 * @param pass Password.
142147 * @param socket Connected socket.
148+ *
143149 * @return Native tarantool connection.
150+ *
144151 * @throws IOException if failed.
145152 */
146153 protected TarantoolConnection makeConnection (String user , String pass , Socket socket ) throws IOException {
@@ -161,7 +168,7 @@ public PreparedStatement prepareStatement(String sql) throws SQLException {
161168
162169 @ Override
163170 public CallableStatement prepareCall (String sql ) throws SQLException {
164- throw new SQLFeatureNotSupportedException ( );
171+ return prepareCall ( sql , ResultSet . TYPE_FORWARD_ONLY , ResultSet . CONCUR_READ_ONLY );
165172 }
166173
167174 @ Override
@@ -171,25 +178,38 @@ public String nativeSQL(String sql) throws SQLException {
171178
172179 @ Override
173180 public void setAutoCommit (boolean autoCommit ) throws SQLException {
181+ checkNotClosed ();
174182 if (autoCommit == false ) {
175183 throw new SQLFeatureNotSupportedException ();
176184 }
177185 }
178186
179187 @ Override
180188 public boolean getAutoCommit () throws SQLException {
189+ checkNotClosed ();
181190 return true ;
182191 }
183192
184193 @ Override
185194 public void commit () throws SQLException {
186- throw new SQLFeatureNotSupportedException ();
195+ checkNotClosed ();
196+ if (getAutoCommit ()) {
197+ throw new SQLNonTransientException (
198+ "Cannot commit when auto-commit is enabled." ,
199+ SQLStates .IVALID_TRANSACTION_STATE .getSqlState ()
200+ );
201+ }
187202 }
188203
189204 @ Override
190205 public void rollback () throws SQLException {
191- throw new SQLFeatureNotSupportedException ();
192-
206+ checkNotClosed ();
207+ if (getAutoCommit ()) {
208+ throw new SQLNonTransientException (
209+ "Cannot rollback when auto-commit is enabled." ,
210+ SQLStates .IVALID_TRANSACTION_STATE .getSqlState ()
211+ );
212+ }
193213 }
194214
195215 @ Override
@@ -213,43 +233,50 @@ public DatabaseMetaData getMetaData() throws SQLException {
213233
214234 @ Override
215235 public void setReadOnly (boolean readOnly ) throws SQLException {
216-
236+ checkNotClosed ();
237+ throw new SQLFeatureNotSupportedException ();
217238 }
218239
219240 @ Override
220241 public boolean isReadOnly () throws SQLException {
242+ checkNotClosed ();
221243 return false ;
222244 }
223245
224246 @ Override
225247 public void setCatalog (String catalog ) throws SQLException {
248+ checkNotClosed ();
226249 }
227250
228251 @ Override
229252 public String getCatalog () throws SQLException {
253+ checkNotClosed ();
230254 return null ;
231255 }
232256
233257 @ Override
234258 public void setTransactionIsolation (int level ) throws SQLException {
259+ checkNotClosed ();
235260 if (level != Connection .TRANSACTION_NONE ) {
236261 throw new SQLFeatureNotSupportedException ();
237262 }
238263 }
239264
240265 @ Override
241266 public int getTransactionIsolation () throws SQLException {
267+ checkNotClosed ();
242268 return Connection .TRANSACTION_NONE ;
243269 }
244270
245271 @ Override
246272 public SQLWarning getWarnings () throws SQLException {
273+ checkNotClosed ();
247274 return null ;
248275 }
249276
250277 @ Override
251278 public void clearWarnings () throws SQLException {
252-
279+ checkNotClosed ();
253280 }
254281
255282 @ Override
@@ -265,16 +292,18 @@ public PreparedStatement prepareStatement(String sql, int resultSetType, int res
265292
266293 @ Override
267294 public CallableStatement prepareCall (String sql , int resultSetType , int resultSetConcurrency ) throws SQLException {
268- throw new SQLFeatureNotSupportedException ( );
295+ return prepareCall ( sql , resultSetType , resultSetConcurrency , getHoldability () );
269296 }
270297
271298 @ Override
272299 public Map <String , Class <?>> getTypeMap () throws SQLException {
300+ checkNotClosed ();
273301 throw new SQLFeatureNotSupportedException ();
274302 }
275303
276304 @ Override
277305 public void setTypeMap (Map <String , Class <?>> map ) throws SQLException {
306+ checkNotClosed ();
278307 throw new SQLFeatureNotSupportedException ();
279308 }
280309
@@ -296,21 +325,43 @@ public int getHoldability() throws SQLException {
296325
297326 @ Override
298327 public Savepoint setSavepoint () throws SQLException {
328+ checkNotClosed ();
329+ if (getAutoCommit ()) {
330+ throw new SQLNonTransientException (
331+ "Cannot set a savepoint when auto-commit is enabled." ,
332+ SQLStates .IVALID_TRANSACTION_STATE .getSqlState ()
333+ );
334+ }
299335 throw new SQLFeatureNotSupportedException ();
300336 }
301337
302338 @ Override
303339 public Savepoint setSavepoint (String name ) throws SQLException {
340+ checkNotClosed ();
341+ if (getAutoCommit ()) {
342+ throw new SQLNonTransientException (
343+ "Cannot set a savepoint when auto-commit is enabled." ,
344+ SQLStates .IVALID_TRANSACTION_STATE .getSqlState ()
345+ );
346+ }
304347 throw new SQLFeatureNotSupportedException ();
305348 }
306349
307350 @ Override
308351 public void rollback (Savepoint savepoint ) throws SQLException {
352+ checkNotClosed ();
353+ if (getAutoCommit ()) {
354+ throw new SQLNonTransientException (
355+ "Cannot roll back to a savepoint when auto-commit is enabled." ,
356+ SQLStates .IVALID_TRANSACTION_STATE .getSqlState ()
357+ );
358+ }
309359 throw new SQLFeatureNotSupportedException ();
310360 }
311361
312362 @ Override
313363 public void releaseSavepoint (Savepoint savepoint ) throws SQLException {
364+ checkNotClosed ();
314365 throw new SQLFeatureNotSupportedException ();
315366 }
316367
@@ -336,90 +387,159 @@ public PreparedStatement prepareStatement(String sql,
336387 @ Override
337388 public CallableStatement prepareCall (String sql , int resultSetType , int resultSetConcurrency , int resultSetHoldability )
338389 throws SQLException {
390+ checkNotClosed ();
339391 throw new SQLFeatureNotSupportedException ();
340392 }
341393
342394 @ Override
343395 public PreparedStatement prepareStatement (String sql , int autoGeneratedKeys ) throws SQLException {
396+ checkNotClosed ();
344397 throw new SQLFeatureNotSupportedException ();
345398 }
346399
347400 @ Override
348401 public PreparedStatement prepareStatement (String sql , int [] columnIndexes ) throws SQLException {
402+ checkNotClosed ();
349403 throw new SQLFeatureNotSupportedException ();
350404 }
351405
352406 @ Override
353407 public PreparedStatement prepareStatement (String sql , String [] columnNames ) throws SQLException {
408+ checkNotClosed ();
354409 throw new SQLFeatureNotSupportedException ();
355410 }
356411
357412 @ Override
358413 public Clob createClob () throws SQLException {
414+ checkNotClosed ();
359415 throw new SQLFeatureNotSupportedException ();
360416 }
361417
362418 @ Override
363419 public Blob createBlob () throws SQLException {
420+ checkNotClosed ();
364421 throw new SQLFeatureNotSupportedException ();
365422 }
366423
367424 @ Override
368425 public NClob createNClob () throws SQLException {
426+ checkNotClosed ();
369427 throw new SQLFeatureNotSupportedException ();
370428 }
371429
372430 @ Override
373431 public SQLXML createSQLXML () throws SQLException {
432+ checkNotClosed ();
374433 throw new SQLFeatureNotSupportedException ();
375434 }
376435
436+ /**
437+ * {@inheritDoc}
438+ *
439+ * @param timeout temporally ignored param
440+ *
441+ * @return connection activity status
442+ */
377443 @ Override
378444 public boolean isValid (int timeout ) throws SQLException {
379445 return true ;
380446 }
381447
382448 @ Override
383449 public void setClientInfo (String name , String value ) throws SQLClientInfoException {
384- throw new SQLClientInfoException ();
450+ try {
451+ checkNotClosed ();
452+ throwUnknownClientProperties (Collections .singleton (name ));
453+ } catch (SQLException cause ) {
454+ throwUnknownReasonClientProperties ("Connection is closed" , Collections .singleton (name ), cause );
455+ }
385456 }
386457
387458 @ Override
388459 public void setClientInfo (Properties properties ) throws SQLClientInfoException {
389- throw new SQLClientInfoException ();
460+ try {
461+ checkNotClosed ();
462+ throwUnknownClientProperties (properties .keySet ());
463+ } catch (SQLException cause ) {
464+ throwUnknownReasonClientProperties ("Connection is closed" , properties .keySet (), cause );
465+ }
466+ }
467+
468+ /**
469+ * Throws an exception caused by {@code cause} and marks all properties
470+ * as {@link ClientInfoStatus#REASON_UNKNOWN}
471+ *
472+ * @param reason reason mesage
473+ * @param properties client properties
474+ * @param cause original cause
475+ *
476+ * @throws SQLClientInfoException wrapped exception
477+ */
478+ private void throwUnknownReasonClientProperties (String reason ,
479+ Collection <Object > properties ,
480+ SQLException cause ) throws SQLClientInfoException {
481+ Map <String , ClientInfoStatus > failedProperties = new HashMap <>();
482+ properties .forEach (property -> {
483+ failedProperties .put (property .toString (), ClientInfoStatus .REASON_UNKNOWN );
484+ });
485+ throw new SQLClientInfoException (reason , failedProperties , cause );
486+ }
487+
488+ /**
489+ * Throws exception for unrecognizable properties
490+ *
491+ * @param properties unknown property names
492+ *
493+ * @throws SQLClientInfoException wrapped exception
494+ */
495+ private void throwUnknownClientProperties (Collection <Object > properties ) throws SQLClientInfoException {
496+ Map <String , ClientInfoStatus > failedProperties = new HashMap <>();
497+ properties .forEach (property -> {
498+ failedProperties .put (property .toString (), ClientInfoStatus .REASON_UNKNOWN_PROPERTY );
499+ });
500+ throw new SQLClientInfoException (failedProperties );
390501 }
391502
392503 @ Override
393504 public String getClientInfo (String name ) throws SQLException {
505+ checkNotClosed ();
394506 throw new SQLFeatureNotSupportedException ();
395507 }
396508
397509 @ Override
398510 public Properties getClientInfo () throws SQLException {
511+ checkNotClosed ();
399512 throw new SQLFeatureNotSupportedException ();
400513 }
401514
402515 @ Override
403516 public Array createArrayOf (String typeName , Object [] elements ) throws SQLException {
517+ checkNotClosed ();
404518 throw new SQLFeatureNotSupportedException ();
405519 }
406520
407521 @ Override
408522 public Struct createStruct (String typeName , Object [] attributes ) throws SQLException {
523+ checkNotClosed ();
409524 throw new SQLFeatureNotSupportedException ();
410525 }
411526
412527 @ Override
413528 public void setSchema (String schema ) throws SQLException {
529+ checkNotClosed ();
414530 }
415531
416532 @ Override
417533 public String getSchema () throws SQLException {
534+ checkNotClosed ();
418535 return null ;
419536 }
420537
421538 @ Override
422539 public void abort (Executor executor ) throws SQLException {
540+ if (isClosed ()) {
541+ return ;
542+ }
423543 throw new SQLFeatureNotSupportedException ();
424544 }
425545
@@ -544,8 +664,9 @@ private void handleException(Exception e) {
544664 * Checks whether <code>holdability</code> is supported
545665 *
546666 * @param holdability param to be checked
667+ *
547668 * @throws SQLFeatureNotSupportedException param is not supported
548- * @throws SQLNonTransientException param has invalid value
669+ * @throws SQLNonTransientException param has invalid value
549670 */
550671 private void checkHoldabilitySupport (int holdability ) throws SQLException {
551672 if (holdability != ResultSet .CLOSE_CURSORS_AT_COMMIT
@@ -562,6 +683,7 @@ private void checkHoldabilitySupport(int holdability) throws SQLException {
562683 *
563684 * @param sql SQL Text.
564685 * @param params Parameters of the SQL statement.
686+ *
565687 * @return Formatted error message.
566688 */
567689 private static String formatError (String sql , Object ... params ) {
0 commit comments