From dfb0ae87173b412e5ebfb791bb7155a837db75fa Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Fri, 19 Oct 2012 16:38:55 +0200 Subject: [PATCH] [#1855] Add Query.cancel() to support for interrupting statements prematurely --- .../jooq/test/_/testcases/StatementTests.java | 46 ++++++++++++++++++- .../src/org/jooq/test/jOOQAbstractTest.java | 5 ++ jOOQ/src/main/java/org/jooq/Query.java | 15 +++++- jOOQ/src/main/java/org/jooq/ResultQuery.java | 6 --- .../jooq/impl/AbstractDelegatingSelect.java | 9 +++- .../java/org/jooq/impl/AbstractQuery.java | 39 +++++++++++----- .../org/jooq/impl/AbstractResultQuery.java | 6 --- .../main/java/org/jooq/impl/DeleteImpl.java | 9 +++- .../main/java/org/jooq/impl/InsertImpl.java | 9 +++- .../main/java/org/jooq/impl/UpdateImpl.java | 9 +++- 10 files changed, 118 insertions(+), 35 deletions(-) diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/StatementTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/StatementTests.java index 910349f723..1cb95918f4 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/StatementTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/StatementTests.java @@ -38,6 +38,7 @@ package org.jooq.test._.testcases; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; import static org.jooq.impl.Factory.val; import java.lang.reflect.InvocationHandler; @@ -51,9 +52,11 @@ import org.jooq.Cursor; import org.jooq.ExecuteContext; import org.jooq.Record; import org.jooq.ResultQuery; +import org.jooq.Select; import org.jooq.TableRecord; import org.jooq.UpdatableRecord; import org.jooq.conf.Settings; +import org.jooq.exception.DataAccessException; import org.jooq.impl.DefaultExecuteListener; import org.jooq.test.BaseTest; import org.jooq.test.jOOQAbstractTest; @@ -111,8 +114,9 @@ extends BaseTest cursor = query.fetchLazy(); assertEquals(3, cursor.fetchOne().getValue(0)); + assertEquals(3, query.fetchOne(0)); - assertEquals(3, KeepStatementListener.statements.size()); + assertEquals(4, KeepStatementListener.statements.size()); assertEquals(0, KeepStatementListener.closed); assertTrue( KeepStatementListener.statements.get(0) == @@ -154,8 +158,46 @@ extends BaseTest select = + create().selectOne() + .from( + TBook(), TBook(), TBook(), TBook(), + TBook(), TBook(), TBook(), TBook(), + TBook(), TBook(), TBook(), TBook(), + TBook(), TBook(), TBook(), TBook(), + TBook(), TBook(), TBook(), TBook(), + TBook(), TBook(), TBook(), TBook(), + TBook(), TBook(), TBook(), TBook()); + + try { + new Thread(new Runnable() { + + @Override + public void run() { + try { + Thread.sleep(500); + } + catch (InterruptedException ignore) {} + select.cancel(); + } + }).start(); + + // The fetch should never terminate, as the above thread should cancel it + select.fetch(); + fail(); + } + catch (DataAccessException expected) {} + } } diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 5860659089..54d21f1767 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -1781,6 +1781,11 @@ public abstract class jOOQAbstractTest< new StatementTests(this).testKeepStatement(); } + @Test + public void testCancelStatement() throws Exception { + new StatementTests(this).testCancelStatement(); + } + @Test public void testVoid() { // A final test case to clean up the test database diff --git a/jOOQ/src/main/java/org/jooq/Query.java b/jOOQ/src/main/java/org/jooq/Query.java index d11e91b80b..6050bb99f5 100644 --- a/jOOQ/src/main/java/org/jooq/Query.java +++ b/jOOQ/src/main/java/org/jooq/Query.java @@ -226,6 +226,19 @@ public interface Query extends QueryPart, Attachable { * @throws DataAccessException If something went wrong closing the statement * @see Statement#close() */ - Query close() throws DataAccessException; + void close() throws DataAccessException; + + /** + * Cancel the underlying statement + *

+ * This cancels the query's underlying {@link Statement} or + * {@link PreparedStatement}. If there is no underlying open and running + * statement, this call is simply ignored. + * + * @throws DataAccessException If something went wrong cancelling the + * statement + * @see Statement#cancel() + */ + void cancel() throws DataAccessException; } diff --git a/jOOQ/src/main/java/org/jooq/ResultQuery.java b/jOOQ/src/main/java/org/jooq/ResultQuery.java index ea0c638547..a8f1bf68bf 100644 --- a/jOOQ/src/main/java/org/jooq/ResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/ResultQuery.java @@ -934,12 +934,6 @@ public interface ResultQuery extends Query { @Override ResultQuery keepStatement(boolean keepStatement); - /** - * {@inheritDoc} - */ - @Override - ResultQuery close() throws DataAccessException; - /** * Specify the maximum number of rows returned by the underlying * {@link Statement} diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java index f855311292..44d9d54daf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java @@ -95,8 +95,13 @@ abstract class AbstractDelegatingSelect } @Override - public final ResultQuery close() { - return getDelegate().close(); + public final void close() { + getDelegate().close(); + } + + @Override + public final void cancel() { + getDelegate().cancel(); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java index 0e8a5eff44..2c0885f542 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java @@ -52,7 +52,6 @@ import org.jooq.ExecuteContext; import org.jooq.ExecuteListener; import org.jooq.Param; import org.jooq.Query; -import org.jooq.exception.DataAccessException; import org.jooq.exception.DetachedException; import org.jooq.tools.JooqLogger; @@ -169,18 +168,33 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query, Attacha * {@inheritDoc} */ @Override - public Query close() throws DataAccessException { + public final void close() { if (statement != null) { try { statement.close(); statement = null; } catch (SQLException e) { - throw Util.translate(null, e); + throw Util.translate(sql, e); } } + } - return this; + /** + * Subclasses may override this for covariant result types + *

+ * {@inheritDoc} + */ + @Override + public final void cancel() { + if (statement != null) { + try { + statement.cancel(); + } + catch (SQLException e) { + throw Util.translate(sql, e); + } + } } @SuppressWarnings("deprecation") @@ -209,7 +223,7 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query, Attacha try { // [#385] If a statement was previously kept open - if (statement != null) { + if (keepStatement() && statement != null) { ctx.sql(sql); ctx.statement(statement); } @@ -220,17 +234,13 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query, Attacha ctx.sql(getSQL()); listener.renderEnd(ctx); - if (keepStatement) { - sql = ctx.sql(); - } + sql = ctx.sql(); listener.prepareStart(ctx); prepare(ctx); listener.prepareEnd(ctx); - if (keepStatement) { - statement = ctx.statement(); - } + statement = ctx.statement(); } // [#1856] Set the query timeout onto the Statement @@ -257,7 +267,12 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query, Attacha // ResultQuery.fetchLazy() needs to keep open resources if (!keepResult()) { - Util.safeClose(listener, ctx, keepStatement); + Util.safeClose(listener, ctx, keepStatement()); + } + + if (!keepStatement()) { + statement = null; + sql = null; } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index 2151eb5324..33de568e84 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -129,12 +129,6 @@ abstract class AbstractResultQuery extends AbstractQuery imple return (ResultQuery) super.keepStatement(k); } - @SuppressWarnings("unchecked") - @Override - public final ResultQuery close() { - return (ResultQuery) super.close(); - } - @Override public final ResultQuery maxRows(int rows) { this.maxRows = rows; diff --git a/jOOQ/src/main/java/org/jooq/impl/DeleteImpl.java b/jOOQ/src/main/java/org/jooq/impl/DeleteImpl.java index 7c80dca663..0aeff42a6e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DeleteImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DeleteImpl.java @@ -103,8 +103,13 @@ class DeleteImpl } @Override - public final Query close() { - return getDelegate().close(); + public final void close() { + getDelegate().close(); + } + + @Override + public final void cancel() { + getDelegate().cancel(); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertImpl.java index 9906daa56f..56709f4086 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertImpl.java @@ -121,8 +121,13 @@ class InsertImpl } @Override - public final Query close() { - return getDelegate().close(); + public final void close() { + getDelegate().close(); + } + + @Override + public final void cancel() { + getDelegate().cancel(); } // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdateImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdateImpl.java index 47bddbae3b..d2db7e0155 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdateImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdateImpl.java @@ -108,8 +108,13 @@ final class UpdateImpl } @Override - public final Query close() { - return getDelegate().close(); + public final void close() { + getDelegate().close(); + } + + @Override + public final void cancel() { + getDelegate().cancel(); } @Override