From 5f35a7fb9573acf0684d13dec5df08a5901a6b23 Mon Sep 17 00:00:00 2001 From: Sharang Gupta Date: Wed, 28 Oct 2026 22:12:26 +0000 Subject: [PATCH] Do not require an end handler on a RowStream Since 5.1.0 the end of a RowStream called its end handler without checking that one was set, so a stream with only a row handler failed with a NullPointerException after its last row. Signed-off-by: Sharang Gupta Assisted-by: Anthropic Claude (claude-opus-5-5) --- .../tck/ProxySQLPreparedQueryTest.java | 7 +++++++ .../vertx/sqlclient/impl/RowStreamImpl.java | 4 +++- .../sqlclient/tck/PreparedQueryTestBase.java | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/vertx-mysql-client/src/test/java/io/vertx/tests/mysqlclient/tck/ProxySQLPreparedQueryTest.java b/vertx-mysql-client/src/test/java/io/vertx/tests/mysqlclient/tck/ProxySQLPreparedQueryTest.java index 5cb766839d..f6fa96e388 100644 --- a/vertx-mysql-client/src/test/java/io/vertx/tests/mysqlclient/tck/ProxySQLPreparedQueryTest.java +++ b/vertx-mysql-client/src/test/java/io/vertx/tests/mysqlclient/tck/ProxySQLPreparedQueryTest.java @@ -66,6 +66,13 @@ public void testStreamQuery(TestContext ctx) { super.testStreamQuery(ctx); } + @Test + @Ignore("Fetch command not supported by ProxySQL") + @Override + public void testStreamQueryWithoutEndHandler(TestContext ctx) { + super.testStreamQueryWithoutEndHandler(ctx); + } + @Test @Ignore("Fetch command not supported by ProxySQL") @Override diff --git a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/RowStreamImpl.java b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/RowStreamImpl.java index eb300068d8..77e199c9ee 100644 --- a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/RowStreamImpl.java +++ b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/RowStreamImpl.java @@ -205,7 +205,9 @@ private void checkPending() { cursor.close(); Handler eh = endHandler; handler = v -> { - eh.handle(null); + if (eh != null) { + eh.handle(null); + } synchronized (RowStreamImpl.this) { cursor = null; } diff --git a/vertx-sql-client/src/test/java/io/vertx/tests/sqlclient/tck/PreparedQueryTestBase.java b/vertx-sql-client/src/test/java/io/vertx/tests/sqlclient/tck/PreparedQueryTestBase.java index bc25f78b71..75771d9cd7 100644 --- a/vertx-sql-client/src/test/java/io/vertx/tests/sqlclient/tck/PreparedQueryTestBase.java +++ b/vertx-sql-client/src/test/java/io/vertx/tests/sqlclient/tck/PreparedQueryTestBase.java @@ -383,6 +383,25 @@ public void testStreamQuery(TestContext ctx) { }); } + @Test + public void testStreamQueryWithoutEndHandler(TestContext ctx) { + vertx.exceptionHandler(ctx::fail); + Async async = ctx.async(); + testCursor(ctx, conn -> { + conn.prepare("SELECT * FROM immutable").onComplete(ctx.asyncAssertSuccess(ps -> { + RowStream stream = ps.createStream(4, Tuple.tuple()); + AtomicInteger rows = new AtomicInteger(); + stream.exceptionHandler(ctx::fail); + stream.handler(row -> { + if (rows.incrementAndGet() == 12) { + // The stream ends after the last row: give it time to do so without an end handler + vertx.setTimer(100, id -> async.complete()); + } + }); + })); + }); + } + @Test public void testStreamQueryNoResults(TestContext ctx) { Async async = ctx.async();