org.glowroot.shaded.h2.tools.RunScript Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.glowroot.shaded.h2.tools;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.glowroot.shaded.h2.engine.Constants;
import org.glowroot.shaded.h2.engine.SysProperties;
import org.glowroot.shaded.h2.message.DbException;
import org.glowroot.shaded.h2.store.fs.FileUtils;
import org.glowroot.shaded.h2.util.Utils;
import org.glowroot.shaded.h2.util.IOUtils;
import org.glowroot.shaded.h2.util.JdbcUtils;
import org.glowroot.shaded.h2.util.ScriptReader;
import org.glowroot.shaded.h2.util.StringUtils;
import org.glowroot.shaded.h2.util.Tool;
/**
* Runs a SQL script against a database.
* @h2.resource
*/
public class RunScript extends Tool {
private boolean showResults;
private boolean checkResults;
/**
* Options are case sensitive. Supported options are:
*
* [-help] or [-?]
* Print the list of options
* [-url "<url>"]
* The database URL (jdbc:...)
* [-user <user>]
* The user name (default: sa)
* [-password <pwd>]
* The password
* [-script <file>]
* The script file to run (default: backup.sql)
* [-driver <class>]
* The JDBC driver class to use (not required in most cases)
* [-showResults]
* Show the statements and the results of queries
* [-checkResults]
* Check if the query results match the expected results
* [-continueOnError]
* Continue even if the script contains errors
* [-options ...]
* RUNSCRIPT options (embedded H2; -*Results not supported)
*
* @h2.resource
*
* @param args the command line arguments
*/
public static void main(String... args) throws SQLException {
new RunScript().runTool(args);
}
/**
* Executes the contents of a SQL script file against a database.
* This tool is usually used to create a database from script.
* It can also be used to analyze performance problems by running
* the tool using Java profiler settings such as:
*
* java -Xrunhprof:cpu=samples,depth=16 ...
*
* To include local files when using remote databases, use the special
* syntax:
*
* @INCLUDE fileName
*
* This syntax is only supported by this tool. Embedded RUNSCRIPT SQL
* statements will be executed by the database.
*
* @param args the command line arguments
*/
@Override
public void runTool(String... args) throws SQLException {
String url = null;
String user = "";
String password = "";
String script = "backup.sql";
String options = null;
boolean continueOnError = false;
boolean showTime = false;
for (int i = 0; args != null && i < args.length; i++) {
String arg = args[i];
if (arg.equals("-url")) {
url = args[++i];
} else if (arg.equals("-user")) {
user = args[++i];
} else if (arg.equals("-password")) {
password = args[++i];
} else if (arg.equals("-continueOnError")) {
continueOnError = true;
} else if (arg.equals("-checkResults")) {
checkResults = true;
} else if (arg.equals("-showResults")) {
showResults = true;
} else if (arg.equals("-script")) {
script = args[++i];
} else if (arg.equals("-time")) {
showTime = true;
} else if (arg.equals("-driver")) {
String driver = args[++i];
Utils.loadUserClass(driver);
} else if (arg.equals("-options")) {
StringBuilder buff = new StringBuilder();
i++;
for (; i < args.length; i++) {
buff.append(' ').append(args[i]);
}
options = buff.toString();
} else if (arg.equals("-help") || arg.equals("-?")) {
showUsage();
return;
} else {
showUsageAndThrowUnsupportedOption(arg);
}
}
if (url == null) {
showUsage();
throw new SQLException("URL not set");
}
long time = System.currentTimeMillis();
if (options != null) {
processRunscript(url, user, password, script, options);
} else {
process(url, user, password, script, null, continueOnError);
}
if (showTime) {
time = System.currentTimeMillis() - time;
out.println("Done in " + time + " ms");
}
}
/**
* Executes the SQL commands read from the reader against a database.
*
* @param conn the connection to a database
* @param reader the reader
* @return the last result set
*/
public static ResultSet execute(Connection conn, Reader reader)
throws SQLException {
// can not close the statement because we return a result set from it
Statement stat = conn.createStatement();
ResultSet rs = null;
ScriptReader r = new ScriptReader(reader);
while (true) {
String sql = r.readStatement();
if (sql == null) {
break;
}
if (sql.trim().length() == 0) {
continue;
}
boolean resultSet = stat.execute(sql);
if (resultSet) {
if (rs != null) {
rs.close();
rs = null;
}
rs = stat.getResultSet();
}
}
return rs;
}
private void process(Connection conn, String fileName,
boolean continueOnError, Charset charset) throws SQLException,
IOException {
InputStream in = FileUtils.newInputStream(fileName);
String path = FileUtils.getParent(fileName);
try {
in = new BufferedInputStream(in, Constants.IO_BUFFER_SIZE);
Reader reader = new InputStreamReader(in, charset);
process(conn, continueOnError, path, reader, charset);
} finally {
IOUtils.closeSilently(in);
}
}
private void process(Connection conn, boolean continueOnError, String path,
Reader reader, Charset charset) throws SQLException, IOException {
Statement stat = conn.createStatement();
ScriptReader r = new ScriptReader(reader);
while (true) {
String sql = r.readStatement();
if (sql == null) {
break;
}
String trim = sql.trim();
if (trim.length() == 0) {
continue;
}
if (trim.startsWith("@") && StringUtils.toUpperEnglish(trim).
startsWith("@INCLUDE")) {
sql = trim;
sql = sql.substring("@INCLUDE".length()).trim();
if (!FileUtils.isAbsolute(sql)) {
sql = path + SysProperties.FILE_SEPARATOR + sql;
}
process(conn, sql, continueOnError, charset);
} else {
try {
if (showResults && !trim.startsWith("-->")) {
out.print(sql + ";");
}
if (showResults || checkResults) {
boolean query = stat.execute(sql);
if (query) {
ResultSet rs = stat.getResultSet();
int columns = rs.getMetaData().getColumnCount();
StringBuilder buff = new StringBuilder();
while (rs.next()) {
buff.append("\n-->");
for (int i = 0; i < columns; i++) {
String s = rs.getString(i + 1);
if (s != null) {
s = StringUtils.replaceAll(s, "\r\n", "\n");
s = StringUtils.replaceAll(s, "\n", "\n--> ");
s = StringUtils.replaceAll(s, "\r", "\r--> ");
}
buff.append(' ').append(s);
}
}
buff.append("\n;");
String result = buff.toString();
if (showResults) {
out.print(result);
}
if (checkResults) {
String expected = r.readStatement() + ";";
expected = StringUtils.replaceAll(expected, "\r\n", "\n");
expected = StringUtils.replaceAll(expected, "\r", "\n");
if (!expected.equals(result)) {
expected = StringUtils.replaceAll(expected, " ", "+");
result = StringUtils.replaceAll(result, " ", "+");
throw new SQLException(
"Unexpected output for:\n" + sql.trim() +
"\nGot:\n" + result + "\nExpected:\n" + expected);
}
}
}
} else {
stat.execute(sql);
}
} catch (Exception e) {
if (continueOnError) {
e.printStackTrace(out);
} else {
throw DbException.toSQLException(e);
}
}
}
}
}
private static void processRunscript(String url, String user, String password,
String fileName, String options) throws SQLException {
Connection conn = null;
Statement stat = null;
try {
org.glowroot.shaded.h2.Driver.load();
conn = DriverManager.getConnection(url, user, password);
stat = conn.createStatement();
String sql = "RUNSCRIPT FROM '" + fileName + "' " + options;
stat.execute(sql);
} finally {
JdbcUtils.closeSilently(stat);
JdbcUtils.closeSilently(conn);
}
}
/**
* Executes the SQL commands in a script file against a database.
*
* @param url the database URL
* @param user the user name
* @param password the password
* @param fileName the script file
* @param charset the character set or null for UTF-8
* @param continueOnError if execution should be continued if an error
* occurs
*/
public static void execute(String url, String user, String password,
String fileName, Charset charset, boolean continueOnError)
throws SQLException {
new RunScript().process(url, user, password, fileName, charset,
continueOnError);
}
/**
* Executes the SQL commands in a script file against a database.
*
* @param url the database URL
* @param user the user name
* @param password the password
* @param fileName the script file
* @param charset the character set or null for UTF-8
* @param continueOnError if execution should be continued if an error
* occurs
*/
void process(String url, String user, String password,
String fileName, Charset charset,
boolean continueOnError) throws SQLException {
try {
org.glowroot.shaded.h2.Driver.load();
Connection conn = DriverManager.getConnection(url, user, password);
if (charset == null) {
charset = Constants.UTF8;
}
try {
process(conn, fileName, continueOnError, charset);
} finally {
conn.close();
}
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
}