diff --git a/jdbc-driver/src/main/java/acolyte/AbstractStatement.java b/jdbc-driver/src/main/java/acolyte/AbstractStatement.java index dcbc68bc..ba41e8ad 100644 --- a/jdbc-driver/src/main/java/acolyte/AbstractStatement.java +++ b/jdbc-driver/src/main/java/acolyte/AbstractStatement.java @@ -31,10 +31,9 @@ abstract class AbstractStatement implements java.sql.Statement { Collections.unmodifiableList(new ArrayList()); /** - * No generated key + * Empty generated keys */ - protected static final ResultSet NO_GENERATED_KEY = - RowLists.stringList().resultSet(); + protected static final RowList>.RowResultSet> EMPTY_GENERATED_KEYS = RowLists.stringList().resultSet(); // --- Properties --- @@ -80,9 +79,10 @@ abstract class AbstractStatement implements java.sql.Statement { /** * Last generated keys - * see #NO_GENERATED_KEY + * see #EMPTY_GENERATED_KEYS */ - protected ResultSet generatedKeys = NO_GENERATED_KEY; + protected ResultSet generatedKeys = + EMPTY_GENERATED_KEYS.withStatement(this); /** * Max rows @@ -156,7 +156,7 @@ public ResultSet executeQuery(final String sql) throws SQLException { checkClosed(); this.updateCount = -1; - this.generatedKeys = NO_GENERATED_KEY; + this.generatedKeys = EMPTY_GENERATED_KEYS.withStatement(this); try { final QueryResult res = this.handler.whenSQLQuery(sql, NO_PARAMS); @@ -496,8 +496,8 @@ private ImmutableTriple update(final String sql, f final SQLWarning w = res.getWarning(); final ResultSet k = (res.generatedKeys == null || autoGeneratedKeys == NO_GENERATED_KEYS) - ? RowLists.stringList().resultSet()/* empty ResultSet */ - : res.generatedKeys.resultSet(); + ? EMPTY_GENERATED_KEYS.withStatement(this) + : res.generatedKeys.resultSet().withStatement(this); return ImmutableTriple.of(res.getUpdateCount(), k, w); } catch (SQLException se) { diff --git a/jdbc-driver/src/main/java/acolyte/CallableStatement.java b/jdbc-driver/src/main/java/acolyte/CallableStatement.java index f2396cf3..95c1b959 100644 --- a/jdbc-driver/src/main/java/acolyte/CallableStatement.java +++ b/jdbc-driver/src/main/java/acolyte/CallableStatement.java @@ -57,13 +57,15 @@ public final class CallableStatement * * @param connection Owner connection * @param sql SQL statement + * @param generatedKeys Generated keys flag * @param handler Statement handler (not null) */ protected CallableStatement(final acolyte.Connection connection, final String sql, + final int generatedKeys, final StatementHandler handler) { - super(connection, sql, handler); + super(connection, sql, generatedKeys, handler); } // end of // --- diff --git a/jdbc-driver/src/main/java/acolyte/Connection.java b/jdbc-driver/src/main/java/acolyte/Connection.java index 6831fb14..8a6537b0 100644 --- a/jdbc-driver/src/main/java/acolyte/Connection.java +++ b/jdbc-driver/src/main/java/acolyte/Connection.java @@ -147,15 +147,15 @@ public Statement createStatement() throws SQLException { /** * {@inheritDoc} + * @see #prepareStatement(String, int) + * @see java.sql.Statement#RETURN_GENERATED_KEYS */ public PreparedStatement prepareStatement(final String sql) throws SQLException { checkClosed(); - return new acolyte. - PreparedStatement(this, sql, this.handler.getStatementHandler()); - + return prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); } // end of prepareStatement /** @@ -165,7 +165,8 @@ public CallableStatement prepareCall(String sql) throws SQLException { checkClosed(); return new acolyte. - CallableStatement(this, sql, this.handler.getStatementHandler()); + CallableStatement(this, sql, Statement.RETURN_GENERATED_KEYS, + this.handler.getStatementHandler()); } // end of prepareCall @@ -535,11 +536,10 @@ public PreparedStatement prepareStatement(final String sql, checkClosed(); - if (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS) { - throw new SQLFeatureNotSupportedException(); - } // end of if + return new acolyte. + PreparedStatement(this, sql, autoGeneratedKeys, + this.handler.getStatementHandler()); - return prepareStatement(sql); } // end of prepareStatement /** diff --git a/jdbc-driver/src/main/java/acolyte/PreparedStatement.java b/jdbc-driver/src/main/java/acolyte/PreparedStatement.java index fb17966d..061b109e 100644 --- a/jdbc-driver/src/main/java/acolyte/PreparedStatement.java +++ b/jdbc-driver/src/main/java/acolyte/PreparedStatement.java @@ -103,6 +103,11 @@ public class PreparedStatement */ private final boolean query; + /** + * Generated key flag + */ + private final int generatedKeysFlag; + /** * Parameters */ @@ -121,10 +126,12 @@ public class PreparedStatement * * @param connection Owner connection * @param sql SQL statement + * @param generatedKeys Generated keys flag * @param handler Statement handler (not null) */ protected PreparedStatement(final acolyte.Connection connection, final String sql, + final int generatedKeys, final StatementHandler handler) { super(connection, handler); @@ -135,6 +142,7 @@ protected PreparedStatement(final acolyte.Connection connection, this.sql = sql; this.query = handler.isQuery(sql); + this.generatedKeysFlag = generatedKeys; this.batch = new ArrayList>>(); } // end of @@ -168,7 +176,7 @@ public ResultSet executeQuery() throws SQLException { this.updateCount = -1; this.warning = res.getWarning(); - this.generatedKeys = NO_GENERATED_KEY; + this.generatedKeys = EMPTY_GENERATED_KEYS.withStatement(this); return (this.result = res.getRowList().resultSet().withStatement(this)); @@ -225,8 +233,8 @@ private ImmutableTriple update(final TreeMap rows, this.columnClasses = getColumnClasses(); this.columnLabels = getColumnLabels(); this.rows = Collections.unmodifiableList(rows); + this.statement = statement; this.last = null; super.fetchSize = rows.size(); diff --git a/jdbc-driver/src/test/scala/acolyte/CallableStatementSpec.scala b/jdbc-driver/src/test/scala/acolyte/CallableStatementSpec.scala index 9fba0ebc..e1e1e62c 100644 --- a/jdbc-driver/src/test/scala/acolyte/CallableStatementSpec.scala +++ b/jdbc-driver/src/test/scala/acolyte/CallableStatementSpec.scala @@ -309,6 +309,6 @@ object CallableStatementSpec } } - override def statement(c: Connection = defaultCon, s: String = "TEST", h: StatementHandler = defaultHandler.getStatementHandler) = new CallableStatement(c, s, h) + override def statement(c: Connection = defaultCon, s: String = "TEST", h: StatementHandler = defaultHandler.getStatementHandler) = new CallableStatement(c, s, java.sql.Statement.NO_GENERATED_KEYS, h) } diff --git a/jdbc-driver/src/test/scala/acolyte/ConnectionSpec.scala b/jdbc-driver/src/test/scala/acolyte/ConnectionSpec.scala index d1092654..8a6ee548 100644 --- a/jdbc-driver/src/test/scala/acolyte/ConnectionSpec.scala +++ b/jdbc-driver/src/test/scala/acolyte/ConnectionSpec.scala @@ -613,6 +613,8 @@ object ConnectionSpec extends Specification with ConnectionFixtures { aka("statement connection") mustEqual c). and(c.prepareStatement("TEST", Statement.NO_GENERATED_KEYS). getConnection aka "statement connection" mustEqual c). + and(c.prepareStatement("TEST", Statement.RETURN_GENERATED_KEYS). + getConnection aka "statement connection" mustEqual c). and(c.prepareStatement("TEST", TYPE_FORWARD_ONLY, CONCUR_READ_ONLY). getConnection aka "statement connection" mustEqual c). and(c.prepareStatement("TEST", @@ -629,6 +631,8 @@ object ConnectionSpec extends Specification with ConnectionFixtures { message = "Connection is closed")). and(c.prepareStatement("TEST", Statement.NO_GENERATED_KEYS). aka("creation") must throwA[SQLException]("Connection is closed")). + and(c.prepareStatement("TEST", Statement.RETURN_GENERATED_KEYS). + aka("creation") must throwA[SQLException]("Connection is closed")). and(c.prepareStatement("TEST", TYPE_FORWARD_ONLY, CONCUR_READ_ONLY). aka("creation") must throwA[SQLException]("Connection is closed")). and(c.prepareStatement("TEST", diff --git a/jdbc-driver/src/test/scala/acolyte/PreparedStatementSpec.scala b/jdbc-driver/src/test/scala/acolyte/PreparedStatementSpec.scala index 4934e34e..d0c5aa25 100644 --- a/jdbc-driver/src/test/scala/acolyte/PreparedStatementSpec.scala +++ b/jdbc-driver/src/test/scala/acolyte/PreparedStatementSpec.scala @@ -16,11 +16,11 @@ import acolyte.StatementHandler.Parameter import acolyte.test.{ EmptyConnectionHandler, Params } object PreparedStatementSpec - extends Specification with StatementSpecification[PreparedStatement] { + extends Specification with StatementSpecification[PreparedStatement] { "Prepared statement specification" title - def statement(c: Connection = defaultCon, s: String = "TEST", h: StatementHandler = defaultHandler.getStatementHandler) = new PreparedStatement(c, s, h) + def statement(c: Connection = defaultCon, s: String = "TEST", h: StatementHandler = defaultHandler.getStatementHandler) = new PreparedStatement(c, s, java.sql.Statement.RETURN_GENERATED_KEYS, h) } @@ -145,7 +145,7 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { s.executeBatch() aka "batch execution" mustEqual Array[Int](1, 2) and ( h.exed aka "executed" must beLike { - case ("TEST", x) :: ("TEST", y) :: Nil => + case ("TEST", x) :: ("TEST", y) :: Nil ⇒ (x aka "x" must_== a) and (y aka "y" must_== b) }) } @@ -161,7 +161,7 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { s.executeBatch() aka "batch execution" must throwA[BatchUpdateException]. like { - case ex: BatchUpdateException => + case ex: BatchUpdateException ⇒ (ex.getUpdateCounts aka "update count" must_== Array[Int]( EXECUTE_FAILED, EXECUTE_FAILED)). and(ex.getCause.getMessage aka "cause" mustEqual "Batch error") @@ -187,7 +187,7 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { s.executeBatch() aka "batch execution" must throwA[BatchUpdateException]. like { - case ex: BatchUpdateException => + case ex: BatchUpdateException ⇒ (ex.getUpdateCounts aka "update count" must_== Array[Int]( EXECUTE_FAILED, 2)). and(ex.getCause.getMessage aka "cause" mustEqual "Batch error: 1") @@ -209,7 +209,7 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { s.executeBatch() aka "batch execution" must throwA[BatchUpdateException]. like { - case ex: BatchUpdateException => + case ex: BatchUpdateException ⇒ (ex.getUpdateCounts aka "update count" must_== Array[Int]( 1, EXECUTE_FAILED)). and(ex.getCause.getMessage aka "cause" mustEqual "Batch error: 2") @@ -235,7 +235,7 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { s.executeBatch() aka "batch execution" must throwA[BatchUpdateException]. like { - case ex: BatchUpdateException => + case ex: BatchUpdateException ⇒ (ex.getUpdateCounts aka "update count" must_== Array[Int]( 1, EXECUTE_FAILED)). and(ex.getCause.getMessage aka "cause" mustEqual "Batch error: 2") @@ -1283,7 +1283,11 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { (query aka "execution" must not(throwA[SQLException])). and(query aka "resultset" must not beNull). - and(s.getGeneratedKeys.next aka "has generated keys" must beFalse) + and(s.getGeneratedKeys aka "generated keys" must beLike { + case genKeys ⇒ + (genKeys.getStatement aka "keys statement" mustEqual s). + and(genKeys.next aka "has keys" must beFalse) + }) } "fail with update statement" in { @@ -1325,7 +1329,8 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { (s.executeUpdate aka "update count" must_== 1). and(s.getGeneratedKeys aka "generated keys" must beLike { - case ks => (ks.next aka "has first key" must beTrue). + case ks ⇒ (ks.getStatement aka "keys statement" mustEqual s). + and(ks.next aka "has first key" must beTrue). and(ks.getInt(1) aka "first key" must_== 200). and(ks.next aka "has second key" must beFalse) })