Compare commits

..

2 Commits

Author SHA1 Message Date
f632a77e8e Add SQL Server datetime type variants to type handling
Adds DATETIME, DATETIME2, SMALLDATETIME, and DATETIMEOFFSET cases to
the TIMESTAMP branch so SQL Server datetime columns are handled correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 02:23:07 -04:00
ff4cf25585 Add ~/.jrunnerpass named connection profile support
Implements .pgpass-style credential file for jrunner. Named aliases can
be used with -sc and -dc flags instead of spelling out -scu/-scn/-scp
for each invocation. Explicit flags still take priority over the file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 15:59:04 -05:00

View File

@ -5,6 +5,7 @@ import java.nio.file.Files;
import java.nio.file.Path ;
import java.nio.file.Paths;
import java.time.*;
import java.io.IOException;
public class jrunner {
//static final String QUERY = "SELECT * from rlarp.osm LIMIT 100";
@ -14,9 +15,11 @@ public class jrunner {
String scu = "";
String scn = "";
String scp = "";
String scAlias = "";
String dcu = "";
String dcn = "";
String dcp = "";
String dcAlias = "";
String sq = "";
String dt = "";
Boolean trim = true;
@ -41,24 +44,36 @@ public class jrunner {
Timestamp tsStart = null;
Timestamp tsEnd = null;
msg = "jrunner version 1.1";
msg = "jrunner version 1.2";
msg = msg + nl + "-sc source connection alias (from ~/.jrunnerpass)";
msg = msg + nl + "-scu source jdbc url";
msg = msg + nl + "-scn source username";
msg = msg + nl + "-scp source passowrd";
msg = msg + nl + "-scp source password";
msg = msg + nl + "-dc destination connection alias (from ~/.jrunnerpass)";
msg = msg + nl + "-dcu destination jdbc url";
msg = msg + nl + "-dcn destination username";
msg = msg + nl + "-dcp destination passowrd";
msg = msg + nl + "-dcp destination password";
msg = msg + nl + "-sq path to source query";
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";
msg = msg + nl + "";
msg = msg + nl + "~/.jrunnerpass format:";
msg = msg + nl + " [alias]";
msg = msg + nl + " url=jdbc:...";
msg = msg + nl + " user=username";
msg = msg + nl + " pass=password";
//---------------------------------------parse args into variables-------------------------------------------------
for (int i = 0; i < args.length; i = i +1 ){
switch (args[i]) {
//source connection alias
case "-sc":
scAlias = args[i+1];
break;
//source connection string
case "-scu":
scu = args[i+1];
@ -68,10 +83,13 @@ public class jrunner {
scn = args[i+1];
break;
//source password
//import java.time.*;
case "-scp":
scp = args[i+1];
break;
//destination connection alias
case "-dc":
dcAlias = args[i+1];
break;
//destination connection string
case "-dcu":
dcu = args[i+1];
@ -133,6 +151,31 @@ public class jrunner {
}
}
// Resolve connection aliases from ~/.jrunnerpass
if (!scAlias.isEmpty() || !dcAlias.isEmpty()) {
Map<String, String[]> connections = loadPassFile();
if (!scAlias.isEmpty()) {
String[] sc = connections.get(scAlias);
if (sc == null) {
System.err.println("Error: source alias '" + scAlias + "' not found in ~/.jrunnerpass");
System.exit(1);
}
if (scu.isEmpty()) scu = sc[0];
if (scn.isEmpty()) scn = sc[1];
if (scp.isEmpty()) scp = sc[2];
}
if (!dcAlias.isEmpty()) {
String[] dc = connections.get(dcAlias);
if (dc == null) {
System.err.println("Error: destination alias '" + dcAlias + "' not found in ~/.jrunnerpass");
System.exit(1);
}
if (dcu.isEmpty()) dcu = dc[0];
if (dcn.isEmpty()) dcn = dc[1];
if (dcp.isEmpty()) dcp = dc[2];
}
}
// Detect query mode when destination flags are not provided
queryMode = dcu.isEmpty() && dcn.isEmpty() && dcp.isEmpty() && dt.isEmpty();
@ -320,6 +363,10 @@ public class jrunner {
nc = "'" + nc + "'";
break;
case "TIMESTAMP":
case "DATETIME":
case "DATETIME2":
case "SMALLDATETIME":
case "DATETIMEOFFSET":
nc = rs.getString(i);
if (rs.wasNull() || nc == null) {
nc = "NULL";
@ -482,4 +529,47 @@ public class jrunner {
private static String escapeTSV(String value) {
return value.replaceAll("\t", " ").replaceAll("\n", " ").replaceAll("\r", " ");
}
// Loads ~/.jrunnerpass and returns a map of alias -> {url, user, pass}
// Format:
// [alias]
// url=jdbc:...
// user=username
// pass=password
private static Map<String, String[]> loadPassFile() {
Map<String, String[]> connections = new LinkedHashMap<>();
Path passFile = Paths.get(System.getProperty("user.home"), ".jrunnerpass");
if (!Files.exists(passFile)) {
System.err.println("Error: ~/.jrunnerpass not found");
System.exit(1);
}
try {
String currentAlias = null;
String url = "", user = "", pass = "";
for (String line : Files.readAllLines(passFile)) {
line = line.trim();
if (line.isEmpty() || line.startsWith("#")) continue;
if (line.startsWith("[") && line.endsWith("]")) {
if (currentAlias != null) {
connections.put(currentAlias, new String[]{url, user, pass});
}
currentAlias = line.substring(1, line.length() - 1).trim();
url = ""; user = ""; pass = "";
} else if (line.startsWith("url=")) {
url = line.substring(4);
} else if (line.startsWith("user=")) {
user = line.substring(5);
} else if (line.startsWith("pass=")) {
pass = line.substring(5);
}
}
if (currentAlias != null) {
connections.put(currentAlias, new String[]{url, user, pass});
}
} catch (IOException e) {
System.err.println("Error reading ~/.jrunnerpass: " + e.getMessage());
System.exit(1);
}
return connections;
}
}