From 8d7797b7693a78f689fbce6147d6c0751649a6e4 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Tue, 11 Aug 2026 13:00:21 -0400 Subject: [PATCH] Walk past update counts to find a procedure's result set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /opt/sync uses CALL statements as migration sources — rlarp.QUOTE_REBUILD, SB_UD_R2, SB_GJ_R1 — and a stored procedure may report update counts before it opens its cursor. The previous commit concluded "no result set" on the first false from execute(), which would have silently migrated nothing for such a procedure. Now the canonical JDBC walk: step through update counts via getMoreResults() until a result set appears, and only then treat the statement as producing none. The last update count is retained so plain DML still reports rows affected. This is strictly more capable than the executeQuery() it replaced, which threw outright in this situation. Verified against the Postgres analogue (UPDATE followed by SELECT in one batch): the SELECT's rows come through, pure DML still reports its count, plain SELECT is unaffected. The DB2 procedures themselves were not invoked — they have production side effects. Co-Authored-By: Claude Opus 5 --- jrunner/src/main/java/jrunner/jrunner.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/jrunner/src/main/java/jrunner/jrunner.java b/jrunner/src/main/java/jrunner/jrunner.java index 080a22a..aef92aa 100644 --- a/jrunner/src/main/java/jrunner/jrunner.java +++ b/jrunner/src/main/java/jrunner/jrunner.java @@ -288,8 +288,20 @@ public class jrunner { // those messages to tell a real failure from a working TRUNCATE, // which made a non-zero exit code impossible to adopt. boolean hasResultSet = stmt.execute(sq); + // A stored procedure may report update counts before opening its + // cursor, and /opt/sync uses CALL statements as migration sources + // (rlarp.QUOTE_REBUILD and friends). Walk past any update counts to + // the first real result set instead of concluding there is none — + // executeQuery() would simply have thrown here. The last count is + // retained so a plain DML statement can still report rows affected. + int updated = -1; + while (!hasResultSet) { + int uc = stmt.getUpdateCount(); + if (uc == -1) break; // no further results of any kind + updated = uc; + hasResultSet = stmt.getMoreResults(); + } if (!hasResultSet) { - int updated = stmt.getUpdateCount(); if (!queryMode) { System.out.println("------------no result set---------------------------------"); System.out.println("rows affected: " + updated);