You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

SQLExec.java 19 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import org.apache.tools.ant.*;
  56. import org.apache.tools.ant.types.*;
  57. import java.io.*;
  58. import java.util.Enumeration;
  59. import java.util.StringTokenizer;
  60. import java.util.Vector;
  61. import java.util.Properties;
  62. import java.util.zip.*;
  63. import java.sql.*;
  64. /**
  65. * Reads in a text file containing SQL statements seperated with semicolons
  66. * and executes it in a given db.
  67. * Both -- and // maybe used as comments.
  68. *
  69. * @author <a href="mailto:jeff@custommonkey.org">Jeff Martin</a>
  70. */
  71. public class SQLExec extends Task {
  72. private int goodSql = 0, totalSql = 0;
  73. private Path classpath;
  74. private AntClassLoader loader;
  75. /**
  76. * Database connection
  77. */
  78. private Connection conn = null;
  79. /**
  80. * Autocommit flag. Default value is false
  81. */
  82. private boolean autocommit=false;
  83. /**
  84. * SQL statement
  85. */
  86. private Statement statement = null;
  87. /**
  88. * DB driver.
  89. */
  90. private String driver = null;
  91. /**
  92. * DB url.
  93. */
  94. private String url = null;
  95. /**
  96. * User name.
  97. */
  98. private String userId = null;
  99. /**
  100. * Password
  101. */
  102. private String password = null;
  103. /**
  104. * SQL input file
  105. */
  106. private File srcFile = null;
  107. /**
  108. * SQL input command
  109. */
  110. private String sqlCommand = "";
  111. /**
  112. * SQL transactions to perform
  113. */
  114. private Vector transactions = new Vector();
  115. /**
  116. * Print SQL results.
  117. */
  118. private boolean print = false;
  119. /**
  120. * Print header columns.
  121. */
  122. private boolean showheaders = true;
  123. /**
  124. * Results Output file.
  125. */
  126. private File output = null;
  127. /**
  128. * RDBMS Product needed for this SQL.
  129. **/
  130. private String rdbms = null;
  131. /**
  132. * RDBMS Version needed for this SQL.
  133. **/
  134. private String version = null;
  135. /**
  136. * Action to perform if an error is found
  137. **/
  138. private String onError = "abort";
  139. /**
  140. * Set the classpath for loading the driver.
  141. */
  142. public void setClasspath(Path classpath) {
  143. if (this.classpath == null) {
  144. this.classpath = classpath;
  145. } else {
  146. this.classpath.append(classpath);
  147. }
  148. }
  149. /**
  150. * Create the classpath for loading the driver.
  151. */
  152. public Path createClasspath() {
  153. if (this.classpath == null) {
  154. this.classpath = new Path(project);
  155. }
  156. return this.classpath.createPath();
  157. }
  158. /**
  159. * Set the classpath for loading the driver using the classpath reference.
  160. */
  161. public void setClasspathRef(Reference r) {
  162. createClasspath().setRefid(r);
  163. }
  164. /**
  165. * Set the name of the sql file to be run.
  166. */
  167. public void setSrc(File srcFile) {
  168. this.srcFile = srcFile;
  169. }
  170. /**
  171. * Set the sql command to execute
  172. */
  173. public void addText(String sql) {
  174. this.sqlCommand += sql;
  175. }
  176. /**
  177. * Set the sql command to execute
  178. */
  179. public Transaction createTransaction() {
  180. Transaction t = new Transaction();
  181. transactions.addElement(t);
  182. return t;
  183. }
  184. /**
  185. * Set the JDBC driver to be used.
  186. */
  187. public void setDriver(String driver) {
  188. this.driver = driver;
  189. }
  190. /**
  191. * Set the DB connection url.
  192. */
  193. public void setUrl(String url) {
  194. this.url = url;
  195. }
  196. /**
  197. * Set the user name for the DB connection.
  198. */
  199. public void setUserid(String userId) {
  200. this.userId = userId;
  201. }
  202. /**
  203. * Set the password for the DB connection.
  204. */
  205. public void setPassword(String password) {
  206. this.password = password;
  207. }
  208. /**
  209. * Set the autocommit flag for the DB connection.
  210. */
  211. public void setAutocommit(boolean autocommit) {
  212. this.autocommit = autocommit;
  213. }
  214. /**
  215. * Set the print flag.
  216. */
  217. public void setPrint(boolean print) {
  218. this.print = print;
  219. }
  220. /**
  221. * Set the showheaders flag.
  222. */
  223. public void setShowheaders(boolean showheaders) {
  224. this.showheaders = showheaders;
  225. }
  226. /**
  227. * Set the output file.
  228. */
  229. public void setOutput(File output) {
  230. this.output = output;
  231. }
  232. /**
  233. * Set the rdbms required
  234. */
  235. public void setRdbms(String vendor) {
  236. this.rdbms = vendor.toLowerCase();
  237. }
  238. /**
  239. * Set the version required
  240. */
  241. public void setVersion(String version) {
  242. this.version = version.toLowerCase();
  243. }
  244. /**
  245. * Set the action to perform onerror
  246. */
  247. public void setOnerror(OnError action) {
  248. this.onError = action.getValue();
  249. }
  250. /**
  251. * Load the sql file and then execute it
  252. */
  253. public void execute() throws BuildException {
  254. sqlCommand = sqlCommand.trim();
  255. if (srcFile == null && sqlCommand.length() == 0) {
  256. if (transactions.size() == 0) {
  257. throw new BuildException("Source file, transactions or sql statement must be set!", location);
  258. }
  259. } else {
  260. // Make a transaction group for the outer command
  261. Transaction t = createTransaction();
  262. t.setSrc(srcFile);
  263. t.addText(sqlCommand);
  264. }
  265. if (driver == null) {
  266. throw new BuildException("Driver attribute must be set!", location);
  267. }
  268. if (userId == null) {
  269. throw new BuildException("User Id attribute must be set!", location);
  270. }
  271. if (password == null) {
  272. throw new BuildException("Password attribute must be set!", location);
  273. }
  274. if (url == null) {
  275. throw new BuildException("Url attribute must be set!", location);
  276. }
  277. if (srcFile != null && !srcFile.exists()) {
  278. throw new BuildException("Source file does not exist!", location);
  279. }
  280. Driver driverInstance = null;
  281. // Load the driver using the
  282. try {
  283. Class dc;
  284. if (classpath != null) {
  285. log("Loading " + driver + " using AntClassLoader with classpath " + classpath, Project.MSG_VERBOSE);
  286. loader = new AntClassLoader(project, classpath, false);
  287. dc = loader.loadClass(driver);
  288. }
  289. else {
  290. log("Loading " + driver + " using system loader.", Project.MSG_VERBOSE);
  291. dc = Class.forName(driver);
  292. }
  293. driverInstance = (Driver) dc.newInstance();
  294. }catch(ClassNotFoundException e){
  295. throw new BuildException("Class Not Found: JDBC driver " + driver + " could not be loaded", location);
  296. }catch(IllegalAccessException e){
  297. throw new BuildException("Illegal Access: JDBC driver " + driver + " could not be loaded", location);
  298. }catch(InstantiationException e) {
  299. throw new BuildException("Instantiation Exception: JDBC driver " + driver + " could not be loaded", location);
  300. }
  301. try{
  302. log("connecting to " + url, Project.MSG_VERBOSE );
  303. Properties info = new Properties();
  304. info.put("user", userId);
  305. info.put("password", password);
  306. conn = driverInstance.connect(url, info);
  307. if (conn == null) {
  308. // Driver doesn't understand the URL
  309. throw new SQLException("No suitable Driver for "+url);
  310. }
  311. if (!isValidRdbms(conn)) return;
  312. conn.setAutoCommit(autocommit);
  313. statement = conn.createStatement();
  314. PrintStream out = System.out;
  315. try {
  316. if (output != null) {
  317. log("Opening PrintStream to output file " + output, Project.MSG_VERBOSE);
  318. out = new PrintStream(new BufferedOutputStream(new FileOutputStream(output)));
  319. }
  320. // Process all transactions
  321. for (Enumeration e = transactions.elements();
  322. e.hasMoreElements();) {
  323. ((Transaction) e.nextElement()).runTransaction(out);
  324. if (!autocommit) {
  325. log("Commiting transaction", Project.MSG_VERBOSE);
  326. conn.commit();
  327. }
  328. }
  329. }
  330. finally {
  331. if (out != null && out != System.out) {
  332. out.close();
  333. }
  334. }
  335. } catch(IOException e){
  336. if (!autocommit && conn != null && onError.equals("abort")) {
  337. try {
  338. conn.rollback();
  339. } catch (SQLException ex) {}
  340. }
  341. throw new BuildException(e, location);
  342. } catch(SQLException e){
  343. if (!autocommit && conn != null && onError.equals("abort")) {
  344. try {
  345. conn.rollback();
  346. } catch (SQLException ex) {}
  347. }
  348. throw new BuildException(e, location);
  349. }
  350. finally {
  351. try {
  352. if (statement != null) {
  353. statement.close();
  354. }
  355. if (conn != null) {
  356. conn.close();
  357. }
  358. }
  359. catch (SQLException e) {}
  360. }
  361. log(goodSql + " of " + totalSql +
  362. " SQL statements executed successfully");
  363. }
  364. protected void runStatements(Reader reader, PrintStream out) throws SQLException, IOException {
  365. String sql = "";
  366. String line = "";
  367. BufferedReader in = new BufferedReader(reader);
  368. try{
  369. while ((line=in.readLine()) != null){
  370. if (line.trim().startsWith("//")) continue;
  371. if (line.trim().startsWith("--")) continue;
  372. sql += " " + line;
  373. sql = sql.trim();
  374. // SQL defines "--" as a comment to EOL
  375. // and in Oracle it may contain a hint
  376. // so we cannot just remove it, instead we must end it
  377. if (line.indexOf("--") >= 0) sql += "\n";
  378. if (sql.endsWith(";")){
  379. log("SQL: " + sql, Project.MSG_VERBOSE);
  380. execSQL(sql.substring(0, sql.length()-1), out);
  381. sql = "";
  382. }
  383. }
  384. // Catch any statements not followed by ;
  385. if(!sql.equals("")){
  386. execSQL(sql, out);
  387. }
  388. }catch(SQLException e){
  389. throw e;
  390. }
  391. }
  392. /**
  393. * Verify if connected to the correct RDBMS
  394. **/
  395. protected boolean isValidRdbms(Connection conn) {
  396. if (rdbms == null && version == null)
  397. return true;
  398. try {
  399. DatabaseMetaData dmd = conn.getMetaData();
  400. if (rdbms != null) {
  401. String theVendor = dmd.getDatabaseProductName().toLowerCase();
  402. log("RDBMS = " + theVendor, Project.MSG_VERBOSE);
  403. if (theVendor == null || theVendor.indexOf(rdbms) < 0) {
  404. log("Not the required RDBMS: "+rdbms, Project.MSG_VERBOSE);
  405. return false;
  406. }
  407. }
  408. if (version != null) {
  409. String theVersion = dmd.getDatabaseProductVersion().toLowerCase();
  410. log("Version = " + theVersion, Project.MSG_VERBOSE);
  411. if (theVersion == null ||
  412. !(theVersion.startsWith(version) ||
  413. theVersion.indexOf(" " + version) >= 0)) {
  414. log("Not the required version: \""+ version +"\"", Project.MSG_VERBOSE);
  415. return false;
  416. }
  417. }
  418. }
  419. catch (SQLException e) {
  420. // Could not get the required information
  421. log("Failed to obtain required RDBMS information", Project.MSG_ERR);
  422. return false;
  423. }
  424. return true;
  425. }
  426. /**
  427. * Exec the sql statement.
  428. */
  429. protected void execSQL(String sql, PrintStream out) throws SQLException {
  430. // Check and ignore empty statements
  431. if ("".equals(sql.trim())) return;
  432. try {
  433. totalSql++;
  434. if (!statement.execute(sql)) {
  435. log(statement.getUpdateCount()+" rows affected",
  436. Project.MSG_VERBOSE);
  437. }
  438. if (print) {
  439. printResults(out);
  440. }
  441. SQLWarning warning = conn.getWarnings();
  442. while(warning!=null){
  443. log(warning + " sql warning", Project.MSG_VERBOSE);
  444. warning=warning.getNextWarning();
  445. }
  446. conn.clearWarnings();
  447. goodSql++;
  448. }
  449. catch (SQLException e) {
  450. log("Failed to execute: " + sql, Project.MSG_ERR);
  451. if (!onError.equals("continue")) throw e;
  452. log(e.toString(), Project.MSG_ERR);
  453. }
  454. }
  455. /**
  456. * print any results in the statement.
  457. */
  458. protected void printResults(PrintStream out) throws java.sql.SQLException {
  459. ResultSet rs = null;
  460. do {
  461. rs = statement.getResultSet();
  462. if (rs != null) {
  463. log("Processing new result set.", Project.MSG_VERBOSE);
  464. ResultSetMetaData md = rs.getMetaData();
  465. int columnCount = md.getColumnCount();
  466. StringBuffer line = new StringBuffer();
  467. if (showheaders) {
  468. for (int col = 1; col < columnCount; col++) {
  469. line.append(md.getColumnName(col));
  470. line.append(",");
  471. }
  472. line.append(md.getColumnName(columnCount));
  473. out.println(line);
  474. line.setLength(0);
  475. }
  476. while (rs.next()) {
  477. boolean first = true;
  478. for (int col = 1; col <= columnCount; col++) {
  479. String columnValue = rs.getString(col);
  480. if (columnValue != null) {
  481. columnValue = columnValue.trim();
  482. }
  483. if (first) {
  484. first = false;
  485. }
  486. else {
  487. line.append(",");
  488. }
  489. line.append(columnValue);
  490. }
  491. out.println(line);
  492. line.setLength(0);
  493. }
  494. }
  495. }
  496. while (statement.getMoreResults());
  497. out.println();
  498. }
  499. /**
  500. * Enumerated attribute with the values "continue", "stop" and "abort"
  501. * for the onerror attribute.
  502. */
  503. public static class OnError extends EnumeratedAttribute {
  504. public String[] getValues() {
  505. return new String[] {"continue", "stop", "abort"};
  506. }
  507. }
  508. /**
  509. * Contains the definition of a new transaction element.
  510. * Transactions allow several files or blocks of statements
  511. * to be executed using the same JDBC connection and commit
  512. * operation in between.
  513. */
  514. public class Transaction {
  515. private File tSrcFile = null;
  516. private String tSqlCommand = "";
  517. public void setSrc(File src) {
  518. this.tSrcFile = src;
  519. }
  520. public void addText(String sql) {
  521. this.tSqlCommand += sql;
  522. }
  523. private void runTransaction(PrintStream out) throws IOException, SQLException {
  524. if (tSqlCommand.length() != 0) {
  525. log("Executing commands", Project.MSG_INFO);
  526. runStatements(new StringReader(tSqlCommand), out);
  527. }
  528. if (tSrcFile != null) {
  529. log("Executing file: " + tSrcFile.getAbsolutePath(),
  530. Project.MSG_INFO);
  531. runStatements(new FileReader(tSrcFile), out);
  532. }
  533. }
  534. }
  535. }