1 package org.apache.torque.task;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.BufferedOutputStream;
23 import java.io.BufferedReader;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.FileReader;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.io.PrintStream;
31 import java.io.Reader;
32 import java.io.StringReader;
33 import java.sql.Connection;
34 import java.sql.DatabaseMetaData;
35 import java.sql.Driver;
36 import java.sql.ResultSet;
37 import java.sql.ResultSetMetaData;
38 import java.sql.SQLException;
39 import java.sql.SQLWarning;
40 import java.sql.Statement;
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.Iterator;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Properties;
47
48 import org.apache.commons.lang.StringUtils;
49 import org.apache.tools.ant.AntClassLoader;
50 import org.apache.tools.ant.BuildException;
51 import org.apache.tools.ant.Project;
52 import org.apache.tools.ant.PropertyHelper;
53 import org.apache.tools.ant.Task;
54 import org.apache.tools.ant.types.EnumeratedAttribute;
55 import org.apache.tools.ant.types.Path;
56 import org.apache.tools.ant.types.Reference;
57
58
59
60
61
62
63
64
65
66
67
68
69 public class TorqueSQLExec extends Task {
70 private int goodSql = 0;
71 private int totalSql = 0;
72 private Path classpath;
73 private AntClassLoader loader;
74
75
76
77
78 public static class DelimiterType extends EnumeratedAttribute {
79 public static final String NORMAL = "normal";
80 public static final String ROW = "row";
81
82 public String[] getValues() {
83 return new String[] { NORMAL, ROW };
84 }
85 }
86
87
88 private Connection conn = null;
89
90
91 private boolean autocommit = false;
92
93
94 private Statement statement = null;
95
96
97 private String driver = null;
98
99
100 private String url = null;
101
102
103 private String userId = null;
104
105
106 private String password = null;
107
108
109 private String delimiter = ";";
110
111
112
113
114 private String delimiterType = DelimiterType.NORMAL;
115
116
117 private boolean print = false;
118
119
120 private boolean showheaders = true;
121
122
123 private File output = null;
124
125
126 private String rdbms = null;
127
128
129 private String version = null;
130
131
132 private String onError = "abort";
133
134
135 private String encoding = null;
136
137
138 private String srcDir;
139
140
141 private File sqldbmap;
142
143
144
145
146
147
148
149 public void setSqlDbMap(String sqldbmap) {
150 this.sqldbmap = getProject().resolveFile(sqldbmap);
151 }
152
153
154
155
156
157
158 public File getSqlDbMap() {
159 return sqldbmap;
160 }
161
162
163
164
165
166
167
168 public void setSrcDir(String srcDir) {
169 this.srcDir = getProject().resolveFile(srcDir).toString();
170 }
171
172
173
174
175
176
177 public String getSrcDir() {
178 return srcDir;
179 }
180
181
182
183
184
185
186
187 public void setClasspath(Path classpath) {
188 if (this.classpath == null) {
189 this.classpath = classpath;
190 } else {
191 this.classpath.append(classpath);
192 }
193 }
194
195
196
197
198
199
200 public Path createClasspath() {
201 if (this.classpath == null) {
202 this.classpath = new Path(getProject());
203 }
204 return this.classpath.createPath();
205 }
206
207
208
209
210
211
212
213 public void setClasspathRef(Reference r) {
214 createClasspath().setRefid(r);
215 }
216
217
218
219
220
221
222
223
224 public void addText(String sql) {
225 }
226
227
228
229
230
231
232
233 public void setDriver(String driver) {
234 this.driver = driver;
235 }
236
237
238
239
240
241
242
243 public void setUrl(String url) {
244 this.url = url;
245 }
246
247
248
249
250
251
252
253 public void setUserid(String userId) {
254 this.userId = userId;
255 }
256
257
258
259
260
261
262
263 public void setEncoding(String encoding) {
264 this.encoding = encoding;
265 }
266
267
268
269
270
271
272
273 public void setPassword(String password) {
274 this.password = password;
275 }
276
277
278
279
280
281
282
283 public void setAutocommit(boolean autocommit) {
284 this.autocommit = autocommit;
285 }
286
287
288
289
290
291
292
293
294
295
296 public void setDelimiter(String delimiter) {
297 this.delimiter = delimiter;
298 }
299
300
301
302
303
304
305
306
307 public void setDelimiterType(DelimiterType delimiterType) {
308 this.delimiterType = delimiterType.getValue();
309 }
310
311
312
313
314
315
316 public void setPrint(boolean print) {
317 this.print = print;
318 }
319
320
321
322
323
324
325 public void setShowheaders(boolean showheaders) {
326 this.showheaders = showheaders;
327 }
328
329
330
331
332
333
334 public void setOutput(File output) {
335 this.output = output;
336 }
337
338
339
340
341
342
343 public void setRdbms(String vendor) {
344 this.rdbms = vendor.toLowerCase();
345 }
346
347
348
349
350
351
352 public void setVersion(String version) {
353 this.version = version.toLowerCase();
354 }
355
356
357
358
359
360
361 public void setOnerror(OnError action) {
362 this.onError = action.getValue();
363 }
364
365
366
367
368
369
370 @SuppressWarnings("unchecked")
371 public void execute() throws BuildException {
372 if (sqldbmap == null || getSqlDbMap().exists() == false) {
373 throw new BuildException("You haven't provided an sqldbmap, or " + "the one you specified doesn't exist: " + sqldbmap);
374 }
375
376 if (driver == null) {
377 throw new BuildException("Driver attribute must be set!", getLocation());
378 }
379 if (userId == null) {
380 throw new BuildException("User Id attribute must be set!", getLocation());
381 }
382 if (password == null) {
383 throw new BuildException("Password attribute must be set!", getLocation());
384 }
385 if (url == null) {
386 throw new BuildException("Url attribute must be set!", getLocation());
387 }
388
389 Properties map = new Properties();
390
391 try {
392 FileInputStream fis = new FileInputStream(getSqlDbMap());
393 map.load(fis);
394 fis.close();
395 } catch (IOException ioe) {
396 throw new BuildException("Cannot open and process the sqldbmap!");
397 }
398
399 Map<Object, Object> databases = new HashMap<Object, Object>();
400
401 Iterator<?> eachFileName = map.keySet().iterator();
402 while (eachFileName.hasNext()) {
403 String sqlfile = (String) eachFileName.next();
404 String database = map.getProperty(sqlfile);
405
406 List<Object> files = (List<Object>) databases.get(database);
407
408 if (files == null) {
409 files = new ArrayList<Object>();
410 databases.put(database, files);
411 }
412
413
414
415 if (sqlfile.indexOf("schema.sql") != -1) {
416 files.add(0, sqlfile);
417 } else {
418 files.add(sqlfile);
419 }
420 }
421
422 Iterator<?> eachDatabase = databases.keySet().iterator();
423 while (eachDatabase.hasNext()) {
424 String db = (String) eachDatabase.next();
425 List<Object> transactions = new ArrayList<Object>();
426 eachFileName = ((List<?>) databases.get(db)).iterator();
427 while (eachFileName.hasNext()) {
428 String fileName = (String) eachFileName.next();
429 File file = new File(srcDir, fileName);
430
431 if (file.exists()) {
432 Transaction transaction = new Transaction();
433 transaction.setSrc(file);
434 transactions.add(transaction);
435 } else {
436 System.out.println("File '" + file.getAbsolutePath() + "' in sqldbmap does not exist, so skipping it.");
437 }
438 }
439
440 insertDatabaseSqlFiles(url, db, transactions);
441 }
442 }
443
444
445
446
447
448
449
450
451 private void insertDatabaseSqlFiles(String url, String database, List<?> transactions) {
452 url = StringUtils.replace(url, "@DB@", database);
453 System.out.println("Our new url -> " + url);
454
455 Driver driverInstance = null;
456 try {
457 Class<?> dc;
458 if (classpath != null) {
459 log("Loading " + driver + " using AntClassLoader with classpath " + classpath, Project.MSG_VERBOSE);
460
461 loader = new AntClassLoader(getProject(), classpath);
462 dc = loader.loadClass(driver);
463 } else {
464 log("Loading " + driver + " using system loader.", Project.MSG_VERBOSE);
465 dc = Class.forName(driver);
466 }
467 driverInstance = (Driver) dc.newInstance();
468 } catch (ClassNotFoundException e) {
469 throw new BuildException("Class Not Found: JDBC driver " + driver + " could not be loaded", getLocation());
470 } catch (IllegalAccessException e) {
471 throw new BuildException("Illegal Access: JDBC driver " + driver + " could not be loaded", getLocation());
472 } catch (InstantiationException e) {
473 throw new BuildException("Instantiation Exception: JDBC driver " + driver + " could not be loaded", getLocation());
474 }
475
476 try {
477 log("connecting to " + url, Project.MSG_VERBOSE);
478 Properties info = new Properties();
479 info.put("user", userId);
480 info.put("password", password);
481 conn = driverInstance.connect(url, info);
482
483 if (conn == null) {
484
485 throw new SQLException("No suitable Driver for " + url);
486 }
487
488 if (!isValidRdbms(conn)) {
489 return;
490 }
491
492 conn.setAutoCommit(autocommit);
493 statement = conn.createStatement();
494 PrintStream out = System.out;
495 try {
496 if (output != null) {
497 log("Opening PrintStream to output file " + output, Project.MSG_VERBOSE);
498 out = new PrintStream(new BufferedOutputStream(new FileOutputStream(output)));
499 }
500
501
502 for (Iterator<?> it = transactions.iterator(); it.hasNext();) {
503 Transaction transaction = (Transaction) it.next();
504 transaction.runTransaction(out);
505 if (!autocommit) {
506 log("Commiting transaction", Project.MSG_VERBOSE);
507 conn.commit();
508 }
509 }
510 } finally {
511 if (out != null && out != System.out) {
512 out.close();
513 }
514 }
515 } catch (IOException e) {
516 if (!autocommit && conn != null && onError.equals("abort")) {
517 try {
518 conn.rollback();
519 } catch (SQLException ex) {
520
521 }
522 }
523 throw new BuildException(e, getLocation());
524 } catch (SQLException e) {
525 if (!autocommit && conn != null && onError.equals("abort")) {
526 try {
527 conn.rollback();
528 } catch (SQLException ex) {
529
530 }
531 }
532 throw new BuildException(e, getLocation());
533 } finally {
534 try {
535 if (statement != null) {
536 statement.close();
537 }
538 if (conn != null) {
539 conn.close();
540 }
541 } catch (SQLException e) {
542 }
543 }
544
545 System.out.println(goodSql + " of " + totalSql + " SQL statements executed successfully");
546 }
547
548
549
550
551
552
553
554
555
556 protected void runStatements(Reader reader, PrintStream out) throws SQLException, IOException {
557 String sql = "";
558 String line = "";
559
560 BufferedReader in = new BufferedReader(reader);
561 PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
562
563 try {
564 while ((line = in.readLine()) != null) {
565 line = line.trim();
566 line = ph.replaceProperties("", line, getProject().getProperties());
567 if (line.startsWith("//") || line.startsWith("--")) {
568 continue;
569 }
570 if (line.length() > 4 && line.substring(0, 4).equalsIgnoreCase("REM ")) {
571 continue;
572 }
573
574 sql += " " + line;
575 sql = sql.trim();
576
577
578
579
580 if (line.indexOf("--") >= 0) {
581 sql += "\n";
582 }
583
584 if (delimiterType.equals(DelimiterType.NORMAL) && sql.endsWith(delimiter) || delimiterType.equals(DelimiterType.ROW) && line.equals(delimiter)) {
585 log("SQL: " + sql, Project.MSG_VERBOSE);
586 execSQL(sql.substring(0, sql.length() - delimiter.length()), out);
587 sql = "";
588 }
589 }
590
591
592 if (!sql.equals("")) {
593 execSQL(sql, out);
594 }
595 } catch (SQLException e) {
596 throw e;
597 }
598 }
599
600
601
602
603
604
605 protected boolean isValidRdbms(Connection conn) {
606 if (rdbms == null && version == null) {
607 return true;
608 }
609
610 try {
611 DatabaseMetaData dmd = conn.getMetaData();
612
613 if (rdbms != null) {
614 String theVendor = dmd.getDatabaseProductName().toLowerCase();
615
616 log("RDBMS = " + theVendor, Project.MSG_VERBOSE);
617 if (theVendor == null || theVendor.indexOf(rdbms) < 0) {
618 log("Not the required RDBMS: " + rdbms, Project.MSG_VERBOSE);
619 return false;
620 }
621 }
622
623 if (version != null) {
624 String theVersion = dmd.getDatabaseProductVersion().toLowerCase();
625
626 log("Version = " + theVersion, Project.MSG_VERBOSE);
627 if (theVersion == null || !(theVersion.startsWith(version) || theVersion.indexOf(" " + version) >= 0)) {
628 log("Not the required version: \"" + version + "\"", Project.MSG_VERBOSE);
629 return false;
630 }
631 }
632 } catch (SQLException e) {
633
634 log("Failed to obtain required RDBMS information", Project.MSG_ERR);
635 return false;
636 }
637
638 return true;
639 }
640
641
642
643
644
645
646
647
648 protected void execSQL(String sql, PrintStream out) throws SQLException {
649
650 if ("".equals(sql.trim())) {
651 return;
652 }
653
654 try {
655 totalSql++;
656 if (!statement.execute(sql)) {
657 log(statement.getUpdateCount() + " rows affected", Project.MSG_VERBOSE);
658 } else {
659 if (print) {
660 printResults(out);
661 }
662 }
663
664 SQLWarning warning = conn.getWarnings();
665 while (warning != null) {
666 log(warning + " sql warning", Project.MSG_VERBOSE);
667 warning = warning.getNextWarning();
668 }
669 conn.clearWarnings();
670 goodSql++;
671 } catch (SQLException e) {
672 System.out.println("Failed to execute: " + sql);
673 if (!onError.equals("continue")) {
674 throw e;
675 }
676 log(e.toString(), Project.MSG_ERR);
677 }
678 }
679
680
681
682
683
684
685
686 protected void printResults(PrintStream out) throws java.sql.SQLException {
687 ResultSet rs = null;
688 do {
689 rs = statement.getResultSet();
690 if (rs != null) {
691 log("Processing new result set.", Project.MSG_VERBOSE);
692 ResultSetMetaData md = rs.getMetaData();
693 int columnCount = md.getColumnCount();
694 StringBuffer line = new StringBuffer();
695 if (showheaders) {
696 for (int col = 1; col < columnCount; col++) {
697 line.append(md.getColumnName(col));
698 line.append(",");
699 }
700 line.append(md.getColumnName(columnCount));
701 out.println(line);
702 line.setLength(0);
703 }
704 while (rs.next()) {
705 boolean first = true;
706 for (int col = 1; col <= columnCount; col++) {
707 String columnValue = rs.getString(col);
708 if (columnValue != null) {
709 columnValue = columnValue.trim();
710 }
711
712 if (first) {
713 first = false;
714 } else {
715 line.append(",");
716 }
717 line.append(columnValue);
718 }
719 out.println(line);
720 line.setLength(0);
721 }
722 }
723 } while (statement.getMoreResults());
724 out.println();
725 }
726
727
728
729
730 public static class OnError extends EnumeratedAttribute {
731 public static final String CONTINUE = "continue";
732
733 public static final String STOP = "stop";
734
735 public static final String ABORT = "abort";
736
737 public String[] getValues() {
738 return new String[] { CONTINUE, STOP, ABORT };
739 }
740 }
741
742
743
744
745
746 public class Transaction {
747 private File tSrcFile = null;
748 private String tSqlCommand = "";
749
750 public void setSrc(File src) {
751 this.tSrcFile = src;
752 }
753
754 public void addText(String sql) {
755 this.tSqlCommand += sql;
756 }
757
758 private void runTransaction(PrintStream out) throws IOException, SQLException {
759 if (tSqlCommand.length() != 0) {
760 log("Executing commands", Project.MSG_INFO);
761 runStatements(new StringReader(tSqlCommand), out);
762 }
763
764 if (tSrcFile != null) {
765 System.out.println("Executing file: " + tSrcFile.getAbsolutePath());
766 Reader reader = (encoding == null) ? new FileReader(tSrcFile) : new InputStreamReader(new FileInputStream(tSrcFile), encoding);
767 runStatements(reader, out);
768 reader.close();
769 }
770 }
771 }
772 }