nutools/sqlcsv

1462 lines
59 KiB
Plaintext
Raw Permalink Normal View History

2016-02-17 07:59:34 +04:00
#!/usr/bin/env compileAndGo
# -*- coding: utf-8 mode: java -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
compiler=javac
mainClass=sqlcsv
compileAndGo
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
2017-09-07 15:32:23 +04:00
import java.util.prefs.Preferences;
2016-02-17 07:59:34 +04:00
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class sqlcsv {
static final String CLASS_NAME = sqlcsv.class.getName();
static final Logger log = Logger.getLogger(CLASS_NAME);
static {
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
log.addHandler(handler);
log.setUseParentHandlers(false);
}
static final Charset UTF8;
static {
UTF8 = Charset.forName("UTF-8");
}
static final String USER_CONFDIR = System.getProperty("user.home") + "/." + CLASS_NAME;
static final String SYSTEM_CONFDIR = "/etc/" + CLASS_NAME;
static final String DEFAULT_CONFIG = CLASS_NAME + ".properties";
static final String USER_CONFIG = USER_CONFDIR + "/" + DEFAULT_CONFIG,
SYSTEM_CONFIG = SYSTEM_CONFDIR + "/" + DEFAULT_CONFIG;
// ------------------------------------------------------------------------
public static final File getCanonical(File file) {
try {
return file.getCanonicalFile();
} catch (IOException e) {
return file.getAbsoluteFile();
}
}
private static final File[] ROOTS = File.listRoots();
public static final boolean isRoot(File file) {
if (file == null) return false;
for (File root : ROOTS) {
if (root.equals(file)) return true;
}
return false;
}
2016-02-17 07:59:34 +04:00
// ------------------------------------------------------------------------
public static class ResultSetHelper {
public static final int CLOBBUFFERSIZE = 2048;
// note: we want to maintain compatibility with Java 5 VM's
// These types don't exist in Java 5
protected static final int NVARCHAR = -9, NCHAR = -15, LONGNVARCHAR = -16, NCLOB = 2011;
public static final String[] getColumnNames(ResultSet rs) throws SQLException {
List<String> names = new ArrayList<String>();
ResultSetMetaData metadata = rs.getMetaData();
for (int i = 0; i < metadata.getColumnCount(); i++) {
names.add(metadata.getColumnName(i + 1));
}
String[] nameArray = new String[names.size()];
return names.toArray(nameArray);
}
public String[] getColumnValues(ResultSet rs) throws SQLException, IOException {
List<String> values = new ArrayList<String>();
ResultSetMetaData metadata = rs.getMetaData();
for (int i = 0; i < metadata.getColumnCount(); i++) {
values.add(getColumnValue(rs, metadata.getColumnType(i + 1), i + 1));
}
String[] valueArray = new String[values.size()];
return values.toArray(valueArray);
}
private String handleObject(Object obj) {
return obj == null? "": String.valueOf(obj);
}
private String handleBigDecimal(BigDecimal decimal) {
return decimal == null? "": decimal.toString();
}
private String handleLong(ResultSet rs, int columnIndex) throws SQLException {
long lv = rs.getLong(columnIndex);
return rs.wasNull()? "": Long.toString(lv);
}
private String handleInteger(ResultSet rs, int columnIndex) throws SQLException {
int i = rs.getInt(columnIndex);
return rs.wasNull()? "": Integer.toString(i);
}
private String handleDate(ResultSet rs, int columnIndex) throws SQLException {
java.sql.Date date = rs.getDate(columnIndex);
String value = null;
if (date != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
value = dateFormat.format(date);
}
return value;
}
private String handleTime(Time time) {
return time == null? null: time.toString();
}
private String handleTimestamp(Timestamp timestamp) {
SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return timestamp == null? null: timeFormat.format(timestamp);
}
private String getColumnValue(ResultSet rs, int colType, int colIndex) throws SQLException,
IOException {
String value = "";
switch (colType) {
case Types.BIT:
case Types.JAVA_OBJECT:
value = handleObject(rs.getObject(colIndex));
break;
case Types.BOOLEAN:
boolean b = rs.getBoolean(colIndex);
value = Boolean.valueOf(b).toString();
break;
case NCLOB: // todo : use rs.getNClob
case Types.CLOB:
Clob c = rs.getClob(colIndex);
if (c != null) value = read(c);
break;
case Types.BIGINT:
value = handleLong(rs, colIndex);
break;
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.REAL:
case Types.NUMERIC:
value = handleBigDecimal(rs.getBigDecimal(colIndex));
break;
case Types.INTEGER:
case Types.TINYINT:
case Types.SMALLINT:
value = handleInteger(rs, colIndex);
break;
case Types.DATE:
value = handleDate(rs, colIndex);
break;
case Types.TIME:
value = handleTime(rs.getTime(colIndex));
break;
case Types.TIMESTAMP:
value = handleTimestamp(rs.getTimestamp(colIndex));
break;
case NVARCHAR: // todo : use rs.getNString
case NCHAR: // todo : use rs.getNString
case LONGNVARCHAR: // todo : use rs.getNString
case Types.LONGVARCHAR:
case Types.VARCHAR:
case Types.CHAR:
value = rs.getString(colIndex);
break;
default:
value = "";
}
if (value == null) value = "";
return value;
}
private static String read(Clob c) throws SQLException, IOException {
StringBuilder sb = new StringBuilder((int)c.length());
Reader r = c.getCharacterStream();
char[] cbuf = new char[CLOBBUFFERSIZE];
int n;
while ((n = r.read(cbuf, 0, cbuf.length)) != -1) {
sb.append(cbuf, 0, n);
}
return sb.toString();
}
}
public static class CSVWriter implements Closeable {
public static final int INITIAL_STRING_SIZE = 128;
public static final char DEFAULT_ESCAPE_CHARACTER = '"';
public static final char DEFAULT_SEPARATOR = ',';
public static final char DEFAULT_QUOTE_CHARACTER = '"';
public static final char NO_QUOTE_CHARACTER = '\u0000';
public static final char NO_ESCAPE_CHARACTER = '\u0000';
public static final String DEFAULT_LINE_END = "\n";
public static final StringBuilder quoteCsv(String s, char escapechar, char quotechar,
StringBuilder sb) {
if (sb == null) sb = new StringBuilder(INITIAL_STRING_SIZE);
for (int j = 0; j < s.length(); j++) {
char nextChar = s.charAt(j);
if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) {
sb.append(escapechar).append(nextChar);
} else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) {
sb.append(escapechar).append(nextChar);
} else {
sb.append(nextChar);
}
}
return sb;
}
public static final String quoteCsv(String s) {
StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
sb.append(DEFAULT_QUOTE_CHARACTER);
quoteCsv(s, DEFAULT_ESCAPE_CHARACTER, DEFAULT_QUOTE_CHARACTER, sb);
sb.append(DEFAULT_QUOTE_CHARACTER);
return sb.toString();
}
public Writer out;
protected char separator;
protected char quotechar;
protected char escapechar;
protected String lineEnd;
private ResultSetHelper resultSetHelper = new ResultSetHelper();
public CSVWriter(Writer writer) {
this(writer, DEFAULT_SEPARATOR);
}
public CSVWriter(Writer writer, char separator) {
this(writer, separator, DEFAULT_QUOTE_CHARACTER);
}
public CSVWriter(Writer writer, char separator, char quotechar) {
this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER);
}
public CSVWriter(Writer writer, char separator, char quotechar, char escapechar) {
this(writer, separator, quotechar, escapechar, DEFAULT_LINE_END);
}
public CSVWriter(Writer writer, char separator, char quotechar, String lineEnd) {
this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER, lineEnd);
}
public CSVWriter(Writer writer, char separator, char quotechar, char escapechar,
String lineEnd) {
this.out = writer;
this.separator = separator;
this.quotechar = quotechar;
this.escapechar = escapechar;
this.lineEnd = lineEnd;
}
public void writeAll(List<String[]> allLines) throws IOException {
for (String[] line : allLines) {
writeNext(line);
}
}
protected void writeColumnNames(ResultSet rs) throws SQLException, IOException {
writeNext(ResultSetHelper.getColumnNames(rs));
}
public void writeAll(java.sql.ResultSet rs, boolean includeColumnNames)
throws SQLException, IOException {
if (includeColumnNames) writeColumnNames(rs);
while (rs.next()) {
writeNext(resultSetHelper.getColumnValues(rs));
}
}
public void writeNext(String[] nextLine) throws IOException {
if (nextLine == null) return;
StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
for (int i = 0; i < nextLine.length; i++) {
if (i != 0) sb.append(separator);
String nextElement = nextLine[i];
if (nextElement == null) continue;
if (quotechar != NO_QUOTE_CHARACTER) sb.append(quotechar);
sb.append(stringContainsSpecialCharacters(nextElement)? processLine(nextElement)
: nextElement);
if (quotechar != NO_QUOTE_CHARACTER) sb.append(quotechar);
}
sb.append(lineEnd);
out.write(sb.toString());
}
protected boolean stringContainsSpecialCharacters(String line) {
return line.indexOf(quotechar) != -1 || line.indexOf(escapechar) != -1;
}
protected StringBuilder processLine(String nextElement) {
StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
for (int j = 0; j < nextElement.length(); j++) {
char nextChar = nextElement.charAt(j);
if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) {
sb.append(escapechar).append(nextChar);
} else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) {
sb.append(escapechar).append(nextChar);
} else {
sb.append(nextChar);
}
}
return sb;
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
flush();
out.close();
}
public void setResultSetHelper(ResultSetHelper resultSetHelper) {
this.resultSetHelper = resultSetHelper;
}
}
public static class SqlResultSetHelper extends ResultSetHelper {
@Override
public String[] getColumnValues(ResultSet rs) throws SQLException, IOException {
List<String> values = new ArrayList<String>();
ResultSetMetaData metadata = rs.getMetaData();
int columnCount = metadata.getColumnCount();
for (int i = 0; i < columnCount; i++) {
values.add(getColumnValue(rs, metadata.getColumnType(i + 1), i + 1));
}
String[] valueArray = new String[values.size()];
return values.toArray(valueArray);
}
private String getColumnValue(ResultSet rs, int colType, int colIndex) throws SQLException,
IOException {
String value = null;
switch (colType) {
case Types.BIT:
case Types.JAVA_OBJECT:
Object ov = rs.getObject(colIndex);
if (!rs.wasNull() && ov != null) value = String.valueOf(ov);
break;
case Types.BOOLEAN:
Boolean bv = rs.getBoolean(colIndex);
if (!rs.wasNull() && bv != null) value = Boolean.toString(bv);
break;
case NCLOB: // todo : use rs.getNClob
case Types.CLOB:
Clob cv = rs.getClob(colIndex);
if (!rs.wasNull() && cv != null) value = read(cv);
break;
case Types.BIGINT:
Long lv = rs.getLong(colIndex);
if (!rs.wasNull() && lv != null) value = Long.toString(lv);
break;
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.REAL:
case Types.NUMERIC:
BigDecimal bdv = rs.getBigDecimal(colIndex);
if (!rs.wasNull() && bdv != null) value = bdv.toString();
break;
case Types.INTEGER:
case Types.TINYINT:
case Types.SMALLINT:
Integer iv = rs.getInt(colIndex);
if (!rs.wasNull() && iv != null) value = Integer.toString(iv);
break;
case Types.DATE:
Date dv = rs.getDate(colIndex);
if (!rs.wasNull() && dv != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
value = dateFormat.format(dv);
}
break;
case Types.TIME:
Time tv = rs.getTime(colIndex);
if (!rs.wasNull() && tv != null) value = tv.toString();
break;
case Types.TIMESTAMP:
Timestamp tsv = rs.getTimestamp(colIndex);
if (!rs.wasNull() && tsv != null) {
SimpleDateFormat timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
value = timestampFormat.format(tsv);
}
break;
case NVARCHAR: // todo : use rs.getNString
case NCHAR: // todo : use rs.getNString
case LONGNVARCHAR: // todo : use rs.getNString
case Types.LONGVARCHAR:
case Types.VARCHAR:
case Types.CHAR:
value = rs.getString(colIndex);
break;
default:
value = "<unknown>";
}
return value;
}
private static final String read(Clob c) throws SQLException, IOException {
StringBuilder sb = new StringBuilder((int)c.length());
Reader r = c.getCharacterStream();
char[] cbuf = new char[CLOBBUFFERSIZE];
int n;
while ((n = r.read(cbuf, 0, cbuf.length)) != -1) {
sb.append(cbuf, 0, n);
}
return sb.toString();
}
}
public static class SqlCSVWriter extends CSVWriter {
public SqlCSVWriter(Writer writer) {
this(writer, DEFAULT_SEPARATOR);
}
public SqlCSVWriter(Writer writer, char separator) {
this(writer, separator, DEFAULT_QUOTE_CHARACTER);
}
public SqlCSVWriter(Writer writer, char separator, char quotechar) {
this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER);
}
public SqlCSVWriter(Writer writer, char separator, char quotechar, char escapechar) {
this(writer, separator, quotechar, escapechar, DEFAULT_LINE_END);
}
public SqlCSVWriter(Writer writer, char separator, char quotechar, String lineEnd) {
this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER, lineEnd);
}
public SqlCSVWriter(Writer writer, char separator, char quotechar, char escapechar,
String lineEnd) {
super(writer, separator, quotechar, escapechar, lineEnd);
setResultSetHelper(new SqlResultSetHelper());
}
@Override
public void writeNext(String[] nextLine) throws IOException {
if (nextLine == null) return;
StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
for (int i = 0; i < nextLine.length; i++) {
if (i != 0) sb.append(separator);
String nextElement = nextLine[i];
if (nextElement != null) {
if (quotechar != NO_QUOTE_CHARACTER) sb.append(quotechar);
if (stringContainsSpecialCharacters(nextElement)) {
sb.append(processLine(nextElement));
} else {
sb.append(nextElement);
}
if (quotechar != NO_QUOTE_CHARACTER) sb.append(quotechar);
} else {
sb.append("NULL");
}
}
sb.append(lineEnd);
out.write(sb.toString());
}
}
// ------------------------------------------------------------------------
static class DelegateDriver implements Driver {
private Driver driver;
DelegateDriver(Driver driver) {
this.driver = driver;
}
public boolean acceptsURL(String u) throws SQLException {
return driver.acceptsURL(u);
}
public Connection connect(String u, Properties p) throws SQLException {
return driver.connect(u, p);
}
public int getMajorVersion() {
return driver.getMajorVersion();
}
public int getMinorVersion() {
return driver.getMinorVersion();
}
public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
return driver.getPropertyInfo(u, p);
}
public boolean jdbcCompliant() {
return driver.jdbcCompliant();
}
public Logger getParentLogger() {
try {
return (Logger)driver.getClass().getMethod("getParentLogger").invoke(driver);
} catch (Exception e) {
Throwable t = e;
if (t instanceof InvocationTargetException) t = t.getCause();
throw new RuntimeException(t);
}
}
}
static final String[] SUPPORTED_DRIVERS = new String[] {
2019-01-17 09:16:01 +04:00
"org.mariadb.jdbc.Driver",
2016-02-17 07:59:34 +04:00
"com.mysql.jdbc.Driver",
"oracle.jdbc.OracleDriver",
"org.hsqldb.jdbcDriver",
"org.postgresql.Driver"};
2017-09-07 15:32:23 +04:00
static final boolean isOracle(String jdbcUrl) {
return jdbcUrl.startsWith("jdbc:oracle:");
}
2016-02-17 07:59:34 +04:00
static final class Exit extends RuntimeException {
private static final long serialVersionUID = 1L;
public Exit(int exitcode, String errorMsg) {
this.exitcode = exitcode;
this.errorMsg = errorMsg;
}
public Exit(String errorMsg) {
this(1, errorMsg);
}
public Exit(int exitcode) {
this(exitcode, null);
}
public Exit() {
this(0, null);
}
private int exitcode;
public int getExitcode() {
return exitcode;
}
private String errorMsg;
public String getErrorMsg() {
return errorMsg;
}
public void exit(String prefix) {
StringBuilder sb = new StringBuilder();
if (prefix != null) sb.append(prefix);
if (errorMsg != null) sb.append(errorMsg);
if (sb.length() > 0) {
System.err.println(sb.toString());
System.err.flush();
}
System.exit(exitcode);
}
public void exit() {
if (exitcode == 0) exit(null);
else exit("ERROR: ");
}
}
static final String getArg(String[] args, int index, String errorMsg) {
if (args.length > index) return args[index];
if (errorMsg == null) return null;
throw new Exit(1, errorMsg);
}
static final String getExceptionAndMessage(String msg, Throwable t) {
if (t == null) return msg;
StringBuilder sb = new StringBuilder();
if (msg != null) sb.append(msg);
sb.append(t.getClass().getName());
sb.append(": ");
sb.append(t.getMessage());
return sb.toString();
}
static final String getSummary(String msg, Throwable t) {
if (t == null) return msg;
StringWriter sw = new StringWriter();
if (msg != null) sw.append(msg);
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
pw.flush();
return sw.toString();
}
static final void logIgnoredError(Throwable t) {
log.info(getExceptionAndMessage("Erreur ignorée: ", t));
log.finer(getSummary(null, t));
}
static final void println(String msg) {
System.out.println(msg);
System.out.flush();
}
static final void die(String msg, Throwable t) {
if (msg != null) System.err.println(msg);
if (t != null) t.printStackTrace(System.err);
System.err.flush();
System.exit(1);
}
static final boolean strIsempty(String str) {
return str == null || str.length() == 0;
}
static final boolean strEquals(String s1, String s2, boolean ignoreCase) {
if (ignoreCase) return s1 == s2 || (s1 != null && s1.equalsIgnoreCase(s2));
else return s1 == s2 || (s1 != null && s1.equals(s2));
}
static final boolean strEquals(String[] a1, String[] a2, boolean ignoreCase) {
if (a1 == null) return a2 == null;
else if (a2 == null) return false;
if (a1.length != a2.length) return false;
for (int i = 0; i < a1.length; i++) {
if (!strEquals(a1[i], a2[i], ignoreCase)) return false;
}
return true;
}
static final boolean strEquals(String s1, String s2) {
return strEquals(s1, s2, false);
}
static final String strNotnull(String str) {
return str == null? "": str;
}
static final String strSubstr(String str, int start, int end) {
if (str == null) return null;
int l = str.length();
if (l > 0) {
while (start < 0)
start += l;
while (end < 0)
end += l;
} else {
if (start < 0) start = 0;
if (end < 0) end = 0;
}
if (start >= l || start >= end) return "";
if (end >= l + 1) end = l;
return str.substring(start, end);
}
static final String strSubstr(String str, int start) {
if (str == null) return null;
return strSubstr(str, start, str.length());
}
static void close(InputStream is) {
try {
if (is != null) is.close();
} catch (IOException e) {
}
}
// static void close(OutputStream os) {
// try {
// if (os != null) os.close();
// } catch (IOException e) {
// }
// }
static void close(Reader r) {
try {
if (r != null) r.close();
} catch (IOException e) {
}
}
static void close(Writer w) {
try {
if (w != null) w.close();
} catch (IOException e) {
}
}
static void close(CSVWriter csv, String output) {
try {
if (csv != null && output != null) csv.close();
} catch (IOException e) {
}
}
static void close(Connection conn) {
try {
if (conn != null) conn.close();
} catch (SQLException e) {
}
}
static void close(PreparedStatement ps) {
try {
if (ps != null) ps.close();
} catch (SQLException e) {
}
}
static void close(ResultSet rs) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
}
}
static void rollback(Connection conn) {
try {
if (!conn.getAutoCommit()) conn.rollback();
} catch (SQLException e) {
}
}
static final Pattern RE_COMMENT = Pattern.compile("--|#|//");
static String nextLine(BufferedReader inf) throws IOException {
while (true) {
String line = inf.readLine();
if (line == null) return null;
line = line.trim();
if (!line.equals("") && !RE_COMMENT.matcher(line).lookingAt()) return line;
}
}
2017-09-07 15:32:23 +04:00
// requêtes à ignorer, en l'occurence lignes spéciques à sqlplus pour une connexion à Oracle
static final Pattern RE_ORACLE_IGNORE_STMT = Pattern.compile("(?i)set\\b");
2016-02-17 07:59:34 +04:00
static final Pattern RE_CREATE_STMT = Pattern
.compile("(?i)create\\s+(?:or\\s+replace)?(?:procedure|trigger|function|package)");
static final Pattern RE_SEPARATOR = Pattern.compile(";$");
2017-09-07 15:32:23 +04:00
static String nextStatement(BufferedReader inf, String jdbcUrl) throws IOException {
boolean ignore;
String line;
do {
ignore = false;
line = nextLine(inf);
if (line == null) return null;
if (isOracle(jdbcUrl) && RE_ORACLE_IGNORE_STMT.matcher(line).lookingAt()) {
ignore = true;
}
} while (ignore);
2016-02-17 07:59:34 +04:00
StringBuilder sb = new StringBuilder();
boolean first = true;
if (RE_CREATE_STMT.matcher(line).lookingAt()) {
// Ici, lire jusqu'à une ligne contenant simplement '/'
do {
if (line.equals("/")) break;
if (first) first = false;
else sb.append("\n");
sb.append(line);
line = nextLine(inf);
} while (line != null);
} else {
// Ici, lire jusqu'à une ligne se *terminant* par ';'
do {
if (first) first = false;
else sb.append(" ");
boolean lastLine = line.endsWith(";");
sb.append(RE_SEPARATOR.matcher(line).replaceFirst(""));
if (lastLine) break;
line = nextLine(inf);
} while (line != null);
}
return sb.toString();
}
static PreparedStatement getPreparedStatement(String query, Object[] args, Connection conn)
throws SQLException {
PreparedStatement ps = conn.prepareStatement(query);
if (args != null) {
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg instanceof java.util.Date && !(arg instanceof Date)
&& !(arg instanceof Time) && !(arg instanceof Timestamp)) {
// corriger les instances de java.util.Date en java.sql.Timestamp
if (arg == null || arg instanceof Timestamp) arg = (Timestamp)arg;
else arg = new Timestamp(((java.util.Date)arg).getTime());
}
ps.setObject(i + 1, arg);
}
}
return ps;
}
SqlCSVWriter getCSVWriter(String output, boolean append) throws FileNotFoundException {
Writer outf;
if (output != null) {
outf = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(output, append),
UTF8));
} else {
outf = new OutputStreamWriter(System.out);
}
return new SqlCSVWriter(outf);
}
static class Options {
public Options(boolean ignoreIoError, boolean ignoreAnyError, boolean noHeaders,
boolean append, boolean sameOutput, boolean forceHeaders, boolean outputUc) {
this.ignoreIoErrors = ignoreIoError;
this.ignoreAnyErrors = ignoreIoError;
this.noHeaders = noHeaders;
this.append = append;
this.sameOutput = sameOutput;
this.forceHeaders = forceHeaders;
this.outputUc = outputUc;
}
public Options() {
this(false, false, false, false, false, false, false);
}
public boolean ignoreIoErrors;
public boolean ignoreAnyErrors;
public boolean noHeaders;
public boolean append;
public boolean sameOutput;
public boolean forceHeaders;
public boolean outputUc;
}
static final String[] UC_FIELDS = new String[] {"updateCount"};
static final Pattern RE_NAME_EXT = Pattern.compile("(.+?)(?:(\\d+))?(?:(\\.[^.]+))?");
void executeQueries(Connection conn, Object[] args, ArrayList<BufferedReader> infs,
2017-09-07 15:32:23 +04:00
ArrayList<String> outputs, Options o, String jdbcUrl) throws IOException, SQLException {
2016-02-17 07:59:34 +04:00
int stcount = 0;
Iterator<String> outputIt = outputs.iterator();
String output = null, prevOutput = null, actualOutput = null;
String[] fields = null, prevFields = null;
boolean firstWrite = true;
for (BufferedReader inf : infs) {
try {
while (true) {
2017-09-07 15:32:23 +04:00
String statement = nextStatement(inf, jdbcUrl);
2016-02-17 07:59:34 +04:00
if (statement == null) break;
stcount++;
log.finer(statement);
try {
PreparedStatement ps = getPreparedStatement(statement, args, conn);
try {
boolean rt = ps.execute();
while (true) {
int uc = 0;
boolean writeOutput;
if (rt) {
writeOutput = true;
} else {
uc = ps.getUpdateCount();
if (uc == -1) break;
writeOutput = o.outputUc;
}
SqlCSVWriter csv = null;
prevOutput = output;
prevFields = fields;
boolean sameOutput = false, append = false;
if (writeOutput) {
if (outputIt.hasNext()) output = outputIt.next();
if (!strEquals(output, prevOutput)) {
sameOutput = false;
firstWrite = true;
actualOutput = output;
} else if (o.sameOutput) {
sameOutput = true;
append = true;
} else if (output != null) {
String dir, basename, sindex, ext;
int index = 0;
sameOutput = true;
File file = new File(actualOutput);
dir = file.getParent();
Matcher m = RE_NAME_EXT.matcher(file.getName());
if (m.matches()) {
basename = m.group(1);
sindex = m.group(2);
ext = m.group(3);
if (sindex != null) index = Integer.valueOf(sindex);
index++;
StringBuilder sb = new StringBuilder();
if (dir != null) {
sb.append(dir);
sb.append("/");
}
sb.append(basename);
sb.append(index);
if (ext != null) sb.append(ext);
actualOutput = sb.toString();
firstWrite = true;
sameOutput = false;
}
}
}
log.fine("stmt#" + stcount);
ResultSet rs = null;
if (rt) {
rs = ps.getResultSet();
fields = ResultSetHelper.getColumnNames(rs);
} else if (o.outputUc) {
fields = UC_FIELDS;
}
boolean sameFields = strEquals(fields, prevFields, true);
boolean writeHeaders = !o.noHeaders
&& !(sameFields && sameOutput && !o.forceHeaders);
if (writeOutput) {
csv = getCSVWriter(actualOutput, append | o.append);
if (sameOutput && !firstWrite
&& (!sameFields || o.forceHeaders)) {
csv.out.write("\n");
}
try {
if (rt) {
csv.writeAll(rs, writeHeaders);
} else {
if (writeHeaders) csv.writeNext(UC_FIELDS);
csv.writeNext(new String[] {String.valueOf(uc)});
}
csv.flush();
} finally {
close(csv, actualOutput);
close(rs);
}
}
firstWrite = false;
rt = ps.getMoreResults();
}
} finally {
close(ps);
}
} catch (SQLException e) {
if (o.ignoreAnyErrors) {
logIgnoredError(e);
} else {
rollback(conn);
throw e;
}
}
}
} catch (IOException e) {
if (o.ignoreIoErrors) {
logIgnoredError(e);
} else {
rollback(conn);
throw e;
}
}
}
if (!conn.getAutoCommit()) conn.commit();
}
static final Pattern RE_URL_PROP = Pattern.compile("(.+)\\.url$");
void run(String[] args) throws Exception {
if (args.length == 1 && strEquals(args[0], "--help")) {
println("USAGE:" //
+ "\n "
+ CLASS_NAME
+ " [query]"
+ "\n\nquery est la requête SQL à exécuter. Si query n'est pas spécifiée ou si elle"
+ "\n vaut '-', la requête SQL est lue sur l'entrée standard, ou depuis un"
+ "\n fichier si l'option -f est spécifiée."
+ "\n\nDEMARRAGE"
+ "\n\nAu démarrage, les répertoires de configuration (utilisateur ~/.sqlcsv et système"
+ "\n/etc/sqlcsv) sont analysés. Les fichiers *.jar situés dans ces répertoires sont"
2016-02-17 07:59:34 +04:00
+ "\najoutés au CLASSPATH. La présence de certains fichiers est testée pour activer"
+ "\néventuellement les logs détaillés."
+ "\n\nOPTIONS"
+ "\n -J<JARDIR>"
+ "\n +J<JARDIR>"
+ "\n Spécifier un répertoire de chargement des drivers JDBC."
+ "\n Avec l'option -J, le répertoire spécifié est ajouté aux répertoires de"
+ "\n configuration par défaut ~/.sqlcsv et /etc/sqlcsv. Avec la variante +J,"
+ "\n seul le répertoire spécifié est analysé."
+ "\n Cette option *doit* être spécifiée en premier et il ne doit pas y avoir"
+ "\n d'espace entre l'option et son argument."
+ "\n"
2016-02-17 07:59:34 +04:00
+ "\n -C, --config CONFIG"
+ "\n Prendre les informations de connexion depuis le fichier de propriété"
+ "\n spécifié. Pour l'identifiant CONN, la propriété 'CONN.url' doit exister"
+ "\n dans ce fichier avec la valeur de l'url jdbc de connexion. De plus, les"
+ "\n propriétés 'CONN.user' et 'CONN.password' contiennent respectivement si"
+ "\n nécessaire le nom et le mot de passe de connexion. La propriété"
+ "\n 'loglevel', si elle existe, est utilisée pour configurer le niveau"
+ "\n d'affichage des logs, comme avec l'option --loglevel"
+ "\n Si cette option n'est pas spécifiée, un fichier nommé sqlcsv.properties"
+ "\n est recherché dans l'ordre: dans le répertoire courant, dans le"
+ "\n répertoire de configuration utilisateur, puis dans le répertoire de"
+ "\n configuration système. Si le fichier est trouvé, il est chargé"
+ "\n automatiquement."
+ "\n -l, --conn CONN"
+ "\n Spécifier l'identifiant (ou l'url) de connexion. Cette information est"
+ "\n obligatoire. Si cette option n'est pas fournie, il faut spécifier un"
+ "\n fichier de configuration avec l'option -C dans lequel *une seule*"
+ "\n propriété 'CONN.url' est définie."
2017-09-07 15:32:23 +04:00
+ "\n Si -C n'est pas spécifié et que -l est spécifié, alors l'identifiant de"
+ "\n connexion est enregistré dans les préférences utilisateur. Si ni -C"
+ "\n ni -l ne sont spécifiés, alors le dernier identifiant est utilisé s'il"
+ "\n existe. L'option -z permet de démarrer comme si aucun identifiant"
+ "\n n'avait jamais été enregistré."
2018-01-24 10:12:55 +04:00
+ "\n -j, --nop"
+ "\n Ne rien faire. Utile par exemple pour enregistrer l'identifiant de"
+ "\n connexion par défaut avec l'option -l"
2016-02-17 07:59:34 +04:00
+ "\n -u, --user USER"
+ "\n -p, --password PASSWORD"
+ "\n Spécifier un nom de connexion et un mot de passe si l'url ne le fournit"
+ "\n pas. Ces valeurs ont la priorité sur les valeurs éventuellement déjà"
+ "\n présentes dans le fichier de propriété."
+ "\n -f, --input INPUT"
+ "\n Lire la requête depuis le fichier INPUT au lieu de la lire depuis la"
+ "\n ligne de commande ou l'entrée standard. Ne pas spécifier cette option ou"
+ "\n utiliser '-' pour lire depuis l'entrée standard. Cette option est"
+ "\n ignorée si la requête est fournie sur la ligne de commande."
+ "\n -o, --output OUTPUT"
+ "\n Ecrire le résultat dans le fichier OUTPUT. Utiliser '-' pour spécifier"
+ "\n la sortie standard (c'est la valeur par défaut). S'il y a plusieurs"
+ "\n requêtes et que le fichier de sortie n'est pas la sortie standard,"
+ "\n ajouter un numéro incrémental au nom du fichier en sortie pour chaque"
+ "\n requête. Sinon, il est possible de spécifier plusieurs fois cette option"
+ "\n pour nommer les fichiers correspondant à chaque requête."
+ "\n -t, --autocommit"
+ "\n Activer le mode autocommit. Par défaut, le mode autocommit n'est pas activé"
2016-02-17 07:59:34 +04:00
+ "\n -c, --ignore-io-error"
+ "\n Continuer le traitement même en cas d'erreur du système de fichiers."
+ "\n Cependant le traitement s'arrête et la transaction est annulée si une"
+ "\n autre erreur se produit."
+ "\n -y, --ignore-any-error"
+ "\n Continuer le traitement même en cas d'erreur quelconque."
+ "\n -n, --no-headers"
+ "\n Ne JAMAIS inclure les en-têtes dans la sortie, même avec l'option -h"
+ "\n -a, --append"
+ "\n Ajouter le résultat au fichier OUTPUT au lieu de l'écraser."
+ "\n -A, --auto-na"
+ "\n Activer les option -n -a si le fichier OUTPUT existe et qu'il est non"
+ "\n vide. Le test n'est effectué que pour le premier fichier spécifié."
+ "\n -s, --same-output"
+ "\n Utiliser le même fichier pour écrire le résultat de toutes les requêtes."
+ "\n Normalement, un numéro incrémental est ajouté au fichier en sortie si"
+ "\n plusieurs requêtes sont spécifiées. Si les en-têtes sont les mêmes,"
+ "\n ajouter le résultat au fichier directement à la suite. Sinon, sauter une"
+ "\n ligne blanche et afficher les nouveaux en-têtes."
+ "\n -h, --force-headers"
+ "\n En cas d'écriture du résultat de plusieurs requêtes dans un même"
+ "\n fichier, ne pas tenter de concaténer les résultats même si les en-têtes"
+ "\n sont les mêmes."
+ "\n --uc-output"
+ "\n Ajouter dans la sortie les résultat de toutes les requêtes, pas"
+ "\n seulement celles de type DQML"
+ "\n --loglevel LOGLEVEL"
+ "\n Spécifier le niveau de logs à afficher. Les valeurs valides sont à"
+ "\n choisir parmi ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, ERROR"
+ "\n La présence de certains fichiers dans les répertoires de configuration"
+ "\n utilisateur ou système configure les logs avant que les options de la"
+ "\n ligne de commande ne soient analysés: un fichier DEBUG fait démarrer"
+ "\n l'application avec le niveau de log ALL ce qui permet de voir les logs"
+ "\n concernant le chargement des jar. Un fichier SQL_DEBUG permet d'activer"
+ "\n la trace de DriverManager. Exemple:"
2017-09-07 15:32:23 +04:00
+ "\n mkdir -p ~/.sqlcsv && touch ~/.sqlcsv/{DEBUG,SQL_DEBUG}"
+ "\n -v, --verbose"
+ "\n Equivalent à --loglevel FINER pour afficher le maximum de messages"
+ "\n -z, --reset-prefs"
+ "\n Effacer les préférences avant de faire le calcul de l'identifiant par"
+ "\n défaut");
2016-02-17 07:59:34 +04:00
return;
}
String config = null, connid = null, user = null, password = null;
Options o = new Options();
2018-01-24 10:12:55 +04:00
boolean nop = false, autocommit = false, autoNa = false, resetPrefs = false;
2016-02-17 07:59:34 +04:00
String loglevel = null;
ArrayList<String> inputs = new ArrayList<String>();
ArrayList<String> outputs = new ArrayList<String>();
ArrayList<String> remainArgs = new ArrayList<String>();
int i = 0, max = args.length;
while (i < max) {
String arg = args[i];
2019-07-13 04:57:58 +04:00
if (arg.length() >= 2 && (arg.substring(0, 2).equals("-J") || arg.substring(0, 2).equals("+J"))) {
die("L'option -J doit être spécifiée en premier", null);
} else if (arg.equals("-C") || arg.equals("--config")) {
2016-02-17 07:59:34 +04:00
config = getArg(args, ++i, "Vous devez spécifier le fichier de configuration");
} else if (arg.equals("-l") || arg.equals("--conn")) {
connid = getArg(args, ++i, "Vous devez spécifier l'identifiant de connexion");
2018-01-24 10:12:55 +04:00
} else if (arg.equals("-j") || arg.equals("--nop")) {
nop = true;
2016-02-17 07:59:34 +04:00
} else if (arg.equals("-u") || arg.equals("--user")) {
user = getArg(args, ++i, "Vous devez spécifier l'utilisateur de connexion");
} else if (arg.equals("-p") || arg.equals("--password")) {
password = getArg(args, ++i, "Vous devez spécifier l'utilisateur de connexion");
} else if (arg.equals("-f") || arg.equals("--input")) {
String input = getArg(args, ++i, "Vous devez spécifier le fichier en entrée");
if (strEquals(input, "-") || strEquals(input, "/dev/stdin")) input = null;
inputs.add(input);
} else if (arg.equals("-o") || arg.equals("--output")) {
String output = getArg(args, ++i, "Vous devez spécifier le fichier en sortie");
if (strEquals(output, "-") || strEquals(output, "/dev/stdout")) output = null;
outputs.add(output);
} else if (arg.equals("-t") || arg.equals("--autocommit")) {
autocommit = true;
} else if (arg.equals("-c") || arg.equals("--ignore-io-error")) {
o.ignoreIoErrors = true;
} else if (arg.equals("-y") || arg.equals("--ignore-any-error")) {
o.ignoreAnyErrors = true;
} else if (arg.equals("-n") || arg.equals("--no-headers")) {
o.noHeaders = true;
} else if (arg.equals("-a") || arg.equals("--append")) {
o.append = true;
} else if (arg.equals("-A") || arg.equals("--auto-na")) {
autoNa = true;
} else if (arg.equals("-s") || arg.equals("--same-output")) {
o.sameOutput = true;
} else if (arg.equals("-h") || arg.equals("--force-headers")) {
o.forceHeaders = true;
} else if (arg.equals("--uc-output")) {
o.outputUc = true;
} else if (arg.equals("--loglevel")) {
loglevel = getArg(args, ++i, "Vous devez spécifier le niveau de logs");
2017-09-07 15:32:23 +04:00
} else if (arg.equals("-v") || arg.equals("--verbose")) {
loglevel = "FINER";
} else if (arg.equals("-z") || arg.equals("--reset-prefs")) {
resetPrefs = true;
2016-02-17 07:59:34 +04:00
} else if (arg.equals("--")) {
i++;
break;
} else {
remainArgs.add(arg);
}
i++;
}
args = remainArgs.toArray(new String[0]);
if (loglevel != null) {
log.setLevel(Level.parse(loglevel.toUpperCase()));
}
2017-09-07 15:32:23 +04:00
Preferences prefs = null;
boolean usePrefs = config == null;
2017-09-07 16:11:44 +04:00
if (resetPrefs || usePrefs) {
2017-09-07 15:32:23 +04:00
prefs = Preferences.userNodeForPackage(sqlcsv.class);
if (resetPrefs) prefs.clear();
2017-09-07 16:11:44 +04:00
if (usePrefs && connid == null) {
connid = prefs.get("lastConnid", null);
if (connid != null) {
log.fine("Sélection de la valeur par défaut connid=" + connid);
}
}
2017-09-07 15:32:23 +04:00
}
// Charger les propriétés...
// essayer depuis le répertoire courant et les répertoires parents jusqu'à $HOME
if (config == null) {
String userHome = System.getProperty("user.home");
if (userHome != null && userHome.length() == 0) userHome = null;
File dir = getCanonical(new File("."));
File homedir = null;
if (userHome != null) homedir = getCanonical(new File(userHome));
while (true) {
File file = new File(dir, DEFAULT_CONFIG);
if (file.exists()) {
config = file.getPath();
break;
}
if ((homedir != null && dir.equals(homedir)) || isRoot(dir)) break;
dir = dir.getParentFile();
if (dir == null) break;
}
2016-02-17 07:59:34 +04:00
}
// puis dans le répertoire de configuration utilisateur
if (config == null && new File(USER_CONFIG).exists()) config = USER_CONFIG;
// puis dans le répertoire de configuration système
if (config == null && new File(SYSTEM_CONFIG).exists()) config = SYSTEM_CONFIG;
Properties props = null;
2016-02-17 07:59:34 +04:00
if (config != null) {
log.config("Chargement des propriétés de " + config);
props = new Properties();
FileInputStream inf = null;
try {
inf = new FileInputStream(config);
props.load(inf);
} catch (FileNotFoundException e) {
throw new Exit(config + ": Fichier introuvable");
} finally {
close(inf);
}
}
if (props != null) {
String loglevelKey = "loglevel";
if (props.containsKey(loglevelKey)) {
loglevel = props.getProperty(loglevelKey);
log.setLevel(Level.parse(loglevel.toUpperCase()));
}
}
String jdbcUrl = null;
if (connid != null && connid.startsWith("jdbc:")) {
jdbcUrl = connid;
connid = null;
}
if (jdbcUrl == null && connid == null && props != null) {
2016-02-17 07:59:34 +04:00
// Essayer de deviner connid en parcourant les propriétés de props
@SuppressWarnings("unchecked")
Enumeration<String> en = (Enumeration<String>)props.propertyNames();
int count = 0;
while (en.hasMoreElements()) {
String propertyName = en.nextElement();
Matcher m = RE_URL_PROP.matcher(propertyName);
if (m.matches()) {
connid = m.group(1);
count++;
}
}
if (count > 1) {
// Si plusieurs configurations sont trouvées, ne pas en sélectionner une par
// défaut
connid = null;
}
}
if (jdbcUrl == null && connid != null) {
2016-02-17 07:59:34 +04:00
if (props != null) {
String jdbcUrlKey = connid + ".url";
if (props.containsKey(jdbcUrlKey)) jdbcUrl = props.getProperty(jdbcUrlKey);
String userKey = connid + ".user";
if (props.containsKey(userKey)) user = props.getProperty(userKey);
String passwordKey = connid + ".password";
if (props.containsKey(passwordKey)) password = props.getProperty(passwordKey);
} else {
jdbcUrl = connid;
}
}
2017-09-07 15:32:23 +04:00
if (jdbcUrl == null) {
if (resetPrefs) throw new Exit();
else throw new Exit("Vous devez spécifier l'url de connexion");
}
2019-06-07 15:15:12 +04:00
if (usePrefs && connid != null) {
2017-09-07 16:11:44 +04:00
log.fine("Enregistrement de la valeur connid=" + connid);
prefs.put("lastConnid", connid);
}
2018-01-24 10:12:55 +04:00
if (nop) return;
2016-02-17 07:59:34 +04:00
// Ouvrir les fichiers en entrée
ArrayList<BufferedReader> infs = new ArrayList<BufferedReader>();
boolean parseInputs = true;
if (args.length > 0) {
String query = args[0];
if (!strEquals(query, "-")) {
infs.add(new BufferedReader(new StringReader(query)));
parseInputs = false;
}
// XXX analyser les arguments à transmettre à la requête
}
if (parseInputs) {
if (inputs.size() == 0) inputs.add(null);
BufferedReader inf;
for (String input : inputs) {
if (input == null) {
inf = new BufferedReader(new InputStreamReader(System.in, UTF8));
} else {
inf = new BufferedReader(
new InputStreamReader(new FileInputStream(input), UTF8));
}
infs.add(inf);
}
}
// Préparer la liste des fichiers en sortie
if (outputs.size() == 0) outputs.add(null);
String output = outputs.get(0);
if (autoNa && output != null) {
if (new File(output).exists()) {
o.noHeaders = true;
o.append = true;
}
}
// Ouvrir la connexion
log.config("Connexion à " + jdbcUrl);
Connection conn;
if (user == null && password == null) conn = DriverManager.getConnection(jdbcUrl);
else conn = DriverManager.getConnection(jdbcUrl, user, password);
conn.setAutoCommit(autocommit);
// Exécuter les requêtes
try {
2017-09-07 15:32:23 +04:00
executeQueries(conn, null, infs, outputs, o, jdbcUrl);
2016-02-17 07:59:34 +04:00
} finally {
close(conn);
for (BufferedReader inf : infs) {
close(inf);
}
}
}
static final FilenameFilter JAR_FILTER = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
};
static final boolean findJars(File confdir, ArrayList<URL> urls) {
if (!confdir.isDirectory()) return false;
log.config("Analyse des jars de " + confdir);
boolean foundJar = false;
for (File file : confdir.listFiles(JAR_FILTER)) {
try {
log.config("Ajout de " + file);
urls.add(file.toURI().toURL());
foundJar = true;
} catch (MalformedURLException e) {
}
}
return foundJar;
}
public static void main(String[] args) throws Exception {
if (new File(USER_CONFDIR + "/DEBUG").exists()
|| new File(SYSTEM_CONFDIR + "/DEBUG").exists()) {
log.setLevel(Level.ALL);
}
if (new File(USER_CONFDIR + "/SQL_DEBUG").exists()
|| new File(SYSTEM_CONFDIR + "/SQL_DEBUG").exists()) {
DriverManager.setLogWriter(new PrintWriter(System.err));
}
boolean jardirOption = false;
String jardir = null;
boolean jardirOnly = false;
if (args.length > 0) {
String firstOption = args[0].substring(0, 2);
if (firstOption.equals("-J")) {
jardirOption = true;
jardir = args[0].substring(2);
jardirOnly = false;
} else if (firstOption.equals("+J")) {
jardirOption = true;
jardir = args[0].substring(2);
jardirOnly = true;
}
if (jardirOption) {
if (jardir.length() == 0) die("L'option -J doit avoir un argument", null);
String[] newArgs = new String[args.length - 1];
System.arraycopy(args, 1, newArgs, 0, newArgs.length);
args = newArgs;
}
}
2016-02-17 07:59:34 +04:00
boolean shouldUpdateClasspath = false;
ArrayList<URL> urls = new ArrayList<URL>();
if (jardirOption) {
shouldUpdateClasspath |= findJars(new File(jardir), urls);
}
if (!jardirOption || !jardirOnly) {
shouldUpdateClasspath |= findJars(new File(USER_CONFDIR), urls);
shouldUpdateClasspath |= findJars(new File(SYSTEM_CONFDIR), urls);
}
2016-02-17 07:59:34 +04:00
if (shouldUpdateClasspath) {
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = new URLClassLoader(urls.toArray(new URL[0]), parentLoader);
Thread.currentThread().setContextClassLoader(loader);
for (String driverName : SUPPORTED_DRIVERS) {
try {
@SuppressWarnings("unchecked")
Class<Driver> driverClass = (Class<Driver>)loader.loadClass(driverName);
DriverManager.registerDriver(new DelegateDriver(driverClass.newInstance()));
} catch (Exception e) {
}
}
}
try {
new sqlcsv().run(args);
System.exit(0);
} catch (Throwable t) {
if (t instanceof InvocationTargetException) t = t.getCause();
if (t instanceof Exit) ((Exit)t).exit();
die(null, t);
}
}
}