From 6c9b0f96a0bca0d2a30757eccd0b963a1c11a189 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Thu, 15 Jan 2026 00:53:26 -0500 Subject: [PATCH] Add query-only mode for piping results to visidata/pspg/less Query mode auto-activates when destination flags are omitted, outputting CSV/TSV to stdout for interactive data exploration of DB2 iSeries queries. Co-Authored-By: Claude Sonnet 4.5 --- jrunner/src/main/java/jrunner/jrunner.java | 180 +++++++++++++++++---- 1 file changed, 148 insertions(+), 32 deletions(-) diff --git a/jrunner/src/main/java/jrunner/jrunner.java b/jrunner/src/main/java/jrunner/jrunner.java index 0e5a791..348c6c7 100644 --- a/jrunner/src/main/java/jrunner/jrunner.java +++ b/jrunner/src/main/java/jrunner/jrunner.java @@ -27,6 +27,8 @@ public class jrunner { String nr = ""; String nc = ""; String nl = "\n"; + Boolean queryMode = false; + String outputFormat = "csv"; String msg = ""; Connection scon = null; Connection dcon = null; @@ -50,6 +52,7 @@ public class jrunner { msg = msg + nl + "-dt fully qualified name of destination table"; msg = msg + nl + "-t trim text"; msg = msg + nl + "-c clear target table"; + msg = msg + nl + "-f output format (csv, tsv, table, json) - default: csv"; msg = msg + nl + "--help info"; //---------------------------------------parse args into variables------------------------------------------------- @@ -104,6 +107,9 @@ public class jrunner { case "-c": clear = true; break; + case "-f": + outputFormat = args[i+1].toLowerCase(); + break; case "-v": System.out.println(msg); return; @@ -127,15 +133,25 @@ public class jrunner { } } - System.out.println("------------db info---------------------------------------"); - System.out.println("source db uri: " + scu); - System.out.println("source db username: " + scn); - System.out.println("destination db uri: " + dcu); - System.out.println("destination username: " + dcn); - System.out.println("------------source sql------------------------------------"); - System.out.println(sq); - System.out.println("------------destination table-----------------------------"); - System.out.println(dt); + // Detect query mode when destination flags are not provided + queryMode = dcu.isEmpty() && dcn.isEmpty() && dcp.isEmpty() && dt.isEmpty(); + + if (queryMode) { + System.err.println("------------query mode------------------------------------"); + System.err.println("source db uri: " + scu); + System.err.println("source db username: " + scn); + System.err.println("output format: " + outputFormat); + } else { + System.out.println("------------db info---------------------------------------"); + System.out.println("source db uri: " + scu); + System.out.println("source db username: " + scn); + System.out.println("destination db uri: " + dcu); + System.out.println("destination username: " + dcn); + System.out.println("------------source sql------------------------------------"); + System.out.println(sq); + System.out.println("------------destination table-----------------------------"); + System.out.println(dt); + } //return; @@ -149,7 +165,11 @@ public class jrunner { //-------------------------------------------establish connections------------------------------------------------- //source database - System.out.println("------------open database connections---------------------"); + if (queryMode) { + System.err.println("------------open database connection---------------------"); + } else { + System.out.println("------------open database connections---------------------"); + } try { scon = DriverManager.getConnection(scu, scn, scp); } catch (SQLException e) { @@ -159,14 +179,16 @@ public class jrunner { } System.out.println(" ✅ source database"); //destination database - try { - dcon = DriverManager.getConnection(dcu, dcn, dcp); - } catch (SQLException e) { - System.out.println("issue connecting to destination:"); - e.printStackTrace(); - System.exit(0); - } - System.out.println(" ✅ destination database"); + if (!queryMode) { + try { + dcon = DriverManager.getConnection(dcu, dcn, dcp); + } catch (SQLException e) { + System.out.println("issue connecting to destination:"); + e.printStackTrace(); + System.exit(0); + } + System.out.println(" ✅ destination database"); + } //----------------------------------------open resultset------------------------------------------------------------ try { stmt = scon.createStatement(); @@ -204,7 +226,7 @@ public class jrunner { System.exit(0); } //-------------------------clear the target table if requeted---------------------------------------------------- - if (clear) { + if (!queryMode && clear) { System.out.println("------------clear target table----------------------------"); sql = "DELETE FROM " + dt; try { @@ -217,11 +239,20 @@ public class jrunner { System.exit(0); } } - System.out.println("------------row count-------------------------------------"); - //-------------------------------build & execute sql------------------------------------------------------------- - try { - sql = ""; - while (rs.next()) { + if (queryMode) { + //-------------------------------output query results-------------------------------------------------------- + try { + outputQueryResults(rs, cols, dtn, outputFormat); + } catch (SQLException e) { + e.printStackTrace(); + System.exit(0); + } + } else { + System.out.println("------------row count-------------------------------------"); + //-------------------------------build & execute sql------------------------------------------------------------- + try { + sql = ""; + while (rs.next()) { r++; t++; nr = ""; @@ -350,26 +381,111 @@ public class jrunner { System.exit(0); } } - } catch (SQLException e) { - e.printStackTrace(); - System.exit(0); + } catch (SQLException e) { + e.printStackTrace(); + System.exit(0); + } } //System.out.println(sql); //---------------------------------------close connections-------------------------------------------------------- try { scon.close(); - dcon.close(); + if (!queryMode && dcon != null) { + dcon.close(); + } } catch (SQLException e) { System.out.println("issue closing connections"); e.printStackTrace(); } - System.out.println(" rows written"); - tsEnd = Timestamp.from(Instant.now()); - System.out.println(tsStart); - System.out.println(tsEnd); + if (!queryMode) { + System.out.println(" rows written"); + tsEnd = Timestamp.from(Instant.now()); + System.out.println(tsStart); + System.out.println(tsEnd); + } //long time = Duration.between(tsStart, tsEnd).toMillis(); //System.out.println("time elapsed: " + time); System.out.println(); } + + private static void outputQueryResults(ResultSet rs, int cols, String[] dtn, String format) throws SQLException { + switch (format) { + case "csv": + outputCSV(rs, cols); + break; + case "tsv": + outputTSV(rs, cols); + break; + case "table": + System.err.println("Table format not yet implemented, defaulting to CSV"); + outputCSV(rs, cols); + break; + case "json": + System.err.println("JSON format not yet implemented, defaulting to CSV"); + outputCSV(rs, cols); + break; + default: + System.err.println("Unknown format: " + format + ", defaulting to CSV"); + outputCSV(rs, cols); + break; + } + } + + private static void outputCSV(ResultSet rs, int cols) throws SQLException { + // Print header row + for (int i = 1; i <= cols; i++) { + if (i > 1) System.out.print(","); + System.out.print(escapeCSV(rs.getMetaData().getColumnName(i))); + } + System.out.println(); + + // Print data rows + while (rs.next()) { + for (int i = 1; i <= cols; i++) { + if (i > 1) System.out.print(","); + String value = rs.getString(i); + if (rs.wasNull() || value == null) { + // Empty field for NULL + } else { + System.out.print(escapeCSV(value)); + } + } + System.out.println(); + } + } + + private static void outputTSV(ResultSet rs, int cols) throws SQLException { + // Print header row + for (int i = 1; i <= cols; i++) { + if (i > 1) System.out.print("\t"); + System.out.print(escapeTSV(rs.getMetaData().getColumnName(i))); + } + System.out.println(); + + // Print data rows + while (rs.next()) { + for (int i = 1; i <= cols; i++) { + if (i > 1) System.out.print("\t"); + String value = rs.getString(i); + if (rs.wasNull() || value == null) { + // Empty field for NULL + } else { + System.out.print(escapeTSV(value)); + } + } + System.out.println(); + } + } + + private static String escapeCSV(String value) { + if (value.contains(",") || value.contains("\"") || value.contains("\n") || value.contains("\r")) { + return "\"" + value.replaceAll("\"", "\"\"") + "\""; + } + return value; + } + + private static String escapeTSV(String value) { + return value.replaceAll("\t", " ").replaceAll("\n", " ").replaceAll("\r", " "); + } }