PageRenderTime 65ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/h2/src/main/org/h2/tools/RunScript.java

http://h2database.googlecode.com/
Java | 338 lines | 239 code | 13 blank | 86 comment | 67 complexity | c03559ddf90cf41d7291b7d9468efb56 MD5 | raw file
  1. /*
  2. * Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
  3. * Version 1.0, and under the Eclipse Public License, Version 1.0
  4. * (http://h2database.com/html/license.html).
  5. * Initial Developer: H2 Group
  6. */
  7. package org.h2.tools;
  8. import java.io.BufferedInputStream;
  9. import java.io.IOException;
  10. import java.io.InputStream;
  11. import java.io.InputStreamReader;
  12. import java.io.Reader;
  13. import java.nio.charset.Charset;
  14. import java.sql.Connection;
  15. import java.sql.DriverManager;
  16. import java.sql.ResultSet;
  17. import java.sql.SQLException;
  18. import java.sql.Statement;
  19. import org.h2.engine.Constants;
  20. import org.h2.engine.SysProperties;
  21. import org.h2.message.DbException;
  22. import org.h2.store.fs.FileUtils;
  23. import org.h2.util.Utils;
  24. import org.h2.util.IOUtils;
  25. import org.h2.util.JdbcUtils;
  26. import org.h2.util.ScriptReader;
  27. import org.h2.util.StringUtils;
  28. import org.h2.util.Tool;
  29. /**
  30. * Runs a SQL script against a database.
  31. * @h2.resource
  32. */
  33. public class RunScript extends Tool {
  34. private boolean showResults;
  35. private boolean checkResults;
  36. /**
  37. * Options are case sensitive. Supported options are:
  38. * <table>
  39. * <tr><td>[-help] or [-?]</td>
  40. * <td>Print the list of options</td></tr>
  41. * <tr><td>[-url "&lt;url&gt;"]</td>
  42. * <td>The database URL (jdbc:...)</td></tr>
  43. * <tr><td>[-user &lt;user&gt;]</td>
  44. * <td>The user name (default: sa)</td></tr>
  45. * <tr><td>[-password &lt;pwd&gt;]</td>
  46. * <td>The password</td></tr>
  47. * <tr><td>[-script &lt;file&gt;]</td>
  48. * <td>The script file to run (default: backup.sql)</td></tr>
  49. * <tr><td>[-driver &lt;class&gt;]</td>
  50. * <td>The JDBC driver class to use (not required in most cases)</td></tr>
  51. * <tr><td>[-showResults]</td>
  52. * <td>Show the statements and the results of queries</td></tr>
  53. * <tr><td>[-checkResults]</td>
  54. * <td>Check if the query results match the expected results</td></tr>
  55. * <tr><td>[-continueOnError]</td>
  56. * <td>Continue even if the script contains errors</td></tr>
  57. * <tr><td>[-options ...]</td>
  58. * <td>RUNSCRIPT options (embedded H2; -*Results not supported)</td></tr>
  59. * </table>
  60. * @h2.resource
  61. *
  62. * @param args the command line arguments
  63. */
  64. public static void main(String... args) throws SQLException {
  65. new RunScript().runTool(args);
  66. }
  67. /**
  68. * Executes the contents of a SQL script file against a database.
  69. * This tool is usually used to create a database from script.
  70. * It can also be used to analyze performance problems by running
  71. * the tool using Java profiler settings such as:
  72. * <pre>
  73. * java -Xrunhprof:cpu=samples,depth=16 ...
  74. * </pre>
  75. * To include local files when using remote databases, use the special
  76. * syntax:
  77. * <pre>
  78. * &#064;INCLUDE fileName
  79. * </pre>
  80. * This syntax is only supported by this tool. Embedded RUNSCRIPT SQL
  81. * statements will be executed by the database.
  82. *
  83. * @param args the command line arguments
  84. */
  85. @Override
  86. public void runTool(String... args) throws SQLException {
  87. String url = null;
  88. String user = "";
  89. String password = "";
  90. String script = "backup.sql";
  91. String options = null;
  92. boolean continueOnError = false;
  93. boolean showTime = false;
  94. for (int i = 0; args != null && i < args.length; i++) {
  95. String arg = args[i];
  96. if (arg.equals("-url")) {
  97. url = args[++i];
  98. } else if (arg.equals("-user")) {
  99. user = args[++i];
  100. } else if (arg.equals("-password")) {
  101. password = args[++i];
  102. } else if (arg.equals("-continueOnError")) {
  103. continueOnError = true;
  104. } else if (arg.equals("-checkResults")) {
  105. checkResults = true;
  106. } else if (arg.equals("-showResults")) {
  107. showResults = true;
  108. } else if (arg.equals("-script")) {
  109. script = args[++i];
  110. } else if (arg.equals("-time")) {
  111. showTime = true;
  112. } else if (arg.equals("-driver")) {
  113. String driver = args[++i];
  114. Utils.loadUserClass(driver);
  115. } else if (arg.equals("-options")) {
  116. StringBuilder buff = new StringBuilder();
  117. i++;
  118. for (; i < args.length; i++) {
  119. buff.append(' ').append(args[i]);
  120. }
  121. options = buff.toString();
  122. } else if (arg.equals("-help") || arg.equals("-?")) {
  123. showUsage();
  124. return;
  125. } else {
  126. showUsageAndThrowUnsupportedOption(arg);
  127. }
  128. }
  129. if (url == null) {
  130. showUsage();
  131. throw new SQLException("URL not set");
  132. }
  133. long time = System.currentTimeMillis();
  134. if (options != null) {
  135. processRunscript(url, user, password, script, options);
  136. } else {
  137. process(url, user, password, script, null, continueOnError);
  138. }
  139. if (showTime) {
  140. time = System.currentTimeMillis() - time;
  141. out.println("Done in " + time + " ms");
  142. }
  143. }
  144. /**
  145. * Executes the SQL commands read from the reader against a database.
  146. *
  147. * @param conn the connection to a database
  148. * @param reader the reader
  149. * @return the last result set
  150. */
  151. public static ResultSet execute(Connection conn, Reader reader)
  152. throws SQLException {
  153. // can not close the statement because we return a result set from it
  154. Statement stat = conn.createStatement();
  155. ResultSet rs = null;
  156. ScriptReader r = new ScriptReader(reader);
  157. while (true) {
  158. String sql = r.readStatement();
  159. if (sql == null) {
  160. break;
  161. }
  162. if (sql.trim().length() == 0) {
  163. continue;
  164. }
  165. boolean resultSet = stat.execute(sql);
  166. if (resultSet) {
  167. if (rs != null) {
  168. rs.close();
  169. rs = null;
  170. }
  171. rs = stat.getResultSet();
  172. }
  173. }
  174. return rs;
  175. }
  176. private void process(Connection conn, String fileName,
  177. boolean continueOnError, Charset charset) throws SQLException,
  178. IOException {
  179. InputStream in = FileUtils.newInputStream(fileName);
  180. String path = FileUtils.getParent(fileName);
  181. try {
  182. in = new BufferedInputStream(in, Constants.IO_BUFFER_SIZE);
  183. Reader reader = new InputStreamReader(in, charset);
  184. process(conn, continueOnError, path, reader, charset);
  185. } finally {
  186. IOUtils.closeSilently(in);
  187. }
  188. }
  189. private void process(Connection conn, boolean continueOnError, String path,
  190. Reader reader, Charset charset) throws SQLException, IOException {
  191. Statement stat = conn.createStatement();
  192. ScriptReader r = new ScriptReader(reader);
  193. while (true) {
  194. String sql = r.readStatement();
  195. if (sql == null) {
  196. break;
  197. }
  198. String trim = sql.trim();
  199. if (trim.length() == 0) {
  200. continue;
  201. }
  202. if (trim.startsWith("@") && StringUtils.toUpperEnglish(trim).
  203. startsWith("@INCLUDE")) {
  204. sql = trim;
  205. sql = sql.substring("@INCLUDE".length()).trim();
  206. if (!FileUtils.isAbsolute(sql)) {
  207. sql = path + SysProperties.FILE_SEPARATOR + sql;
  208. }
  209. process(conn, sql, continueOnError, charset);
  210. } else {
  211. try {
  212. if (showResults && !trim.startsWith("-->")) {
  213. out.print(sql + ";");
  214. }
  215. if (showResults || checkResults) {
  216. boolean query = stat.execute(sql);
  217. if (query) {
  218. ResultSet rs = stat.getResultSet();
  219. int columns = rs.getMetaData().getColumnCount();
  220. StringBuilder buff = new StringBuilder();
  221. while (rs.next()) {
  222. buff.append("\n-->");
  223. for (int i = 0; i < columns; i++) {
  224. String s = rs.getString(i + 1);
  225. if (s != null) {
  226. s = StringUtils.replaceAll(s, "\r\n", "\n");
  227. s = StringUtils.replaceAll(s, "\n", "\n--> ");
  228. s = StringUtils.replaceAll(s, "\r", "\r--> ");
  229. }
  230. buff.append(' ').append(s);
  231. }
  232. }
  233. buff.append("\n;");
  234. String result = buff.toString();
  235. if (showResults) {
  236. out.print(result);
  237. }
  238. if (checkResults) {
  239. String expected = r.readStatement() + ";";
  240. expected = StringUtils.replaceAll(expected, "\r\n", "\n");
  241. expected = StringUtils.replaceAll(expected, "\r", "\n");
  242. if (!expected.equals(result)) {
  243. expected = StringUtils.replaceAll(expected, " ", "+");
  244. result = StringUtils.replaceAll(result, " ", "+");
  245. throw new SQLException(
  246. "Unexpected output for:\n" + sql.trim() +
  247. "\nGot:\n" + result + "\nExpected:\n" + expected);
  248. }
  249. }
  250. }
  251. } else {
  252. stat.execute(sql);
  253. }
  254. } catch (Exception e) {
  255. if (continueOnError) {
  256. e.printStackTrace(out);
  257. } else {
  258. throw DbException.toSQLException(e);
  259. }
  260. }
  261. }
  262. }
  263. }
  264. private static void processRunscript(String url, String user, String password,
  265. String fileName, String options) throws SQLException {
  266. Connection conn = null;
  267. Statement stat = null;
  268. try {
  269. org.h2.Driver.load();
  270. conn = DriverManager.getConnection(url, user, password);
  271. stat = conn.createStatement();
  272. String sql = "RUNSCRIPT FROM '" + fileName + "' " + options;
  273. stat.execute(sql);
  274. } finally {
  275. JdbcUtils.closeSilently(stat);
  276. JdbcUtils.closeSilently(conn);
  277. }
  278. }
  279. /**
  280. * Executes the SQL commands in a script file against a database.
  281. *
  282. * @param url the database URL
  283. * @param user the user name
  284. * @param password the password
  285. * @param fileName the script file
  286. * @param charset the character set or null for UTF-8
  287. * @param continueOnError if execution should be continued if an error
  288. * occurs
  289. */
  290. public static void execute(String url, String user, String password,
  291. String fileName, Charset charset, boolean continueOnError)
  292. throws SQLException {
  293. new RunScript().process(url, user, password, fileName, charset,
  294. continueOnError);
  295. }
  296. /**
  297. * Executes the SQL commands in a script file against a database.
  298. *
  299. * @param url the database URL
  300. * @param user the user name
  301. * @param password the password
  302. * @param fileName the script file
  303. * @param charset the character set or null for UTF-8
  304. * @param continueOnError if execution should be continued if an error
  305. * occurs
  306. */
  307. void process(String url, String user, String password,
  308. String fileName, Charset charset,
  309. boolean continueOnError) throws SQLException {
  310. try {
  311. org.h2.Driver.load();
  312. Connection conn = DriverManager.getConnection(url, user, password);
  313. if (charset == null) {
  314. charset = Constants.UTF8;
  315. }
  316. try {
  317. process(conn, fileName, continueOnError, charset);
  318. } finally {
  319. conn.close();
  320. }
  321. } catch (IOException e) {
  322. throw DbException.convertIOException(e, fileName);
  323. }
  324. }
  325. }