Clover Coverage Report - SQL Maven Plugin 1.5-SNAPSHOT
Coverage timestamp: Wed Dec 31 1969 19:00:00 EST
../../../../img/srcFileCovDistChart0.png 0% of files have more coverage
338   1,377   166   5.37
158   674   0.49   31.5
63     2.63  
2    
 
  SqlExecMojo       Line # 71 317 0% 156 522 0% 0.0
  SqlExecMojo.Transaction       Line # 1150 21 0% 10 37 0% 0.0
 
No Tests
 
1    package org.codehaus.mojo.sql;
2   
3    /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements. See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership. The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10    * with the License. You may obtain a copy of the License at
11    *
12    * http://www.apache.org/licenses/LICENSE-2.0
13    *
14    * Unless required by applicable law or agreed to in writing,
15    * software distributed under the License is distributed on an
16    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17    * KIND, either express or implied. See the License for the
18    * specific language governing permissions and limitations
19    * under the License.
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.InputStream;
30    import java.io.InputStreamReader;
31    import java.io.OutputStream;
32    import java.io.PrintStream;
33    import java.io.Reader;
34    import java.io.StringReader;
35    import java.sql.Connection;
36    import java.sql.Driver;
37    import java.sql.ResultSet;
38    import java.sql.ResultSetMetaData;
39    import java.sql.SQLException;
40    import java.sql.SQLWarning;
41    import java.sql.Statement;
42    import java.util.Collections;
43    import java.util.Enumeration;
44    import java.util.Properties;
45    import java.util.StringTokenizer;
46    import java.util.Vector;
47   
48    import org.apache.commons.io.IOUtils;
49    import org.apache.commons.lang.StringEscapeUtils;
50    import org.apache.maven.execution.MavenSession;
51    import org.apache.maven.plugin.AbstractMojo;
52    import org.apache.maven.plugin.MojoExecutionException;
53    import org.apache.maven.project.MavenProject;
54    import org.apache.maven.settings.Server;
55    import org.apache.maven.settings.Settings;
56    import org.apache.maven.shared.filtering.MavenFileFilter;
57    import org.apache.maven.shared.filtering.MavenFileFilterRequest;
58    import org.apache.maven.shared.filtering.MavenFilteringException;
59    import org.codehaus.plexus.util.FileUtils;
60    import org.codehaus.plexus.util.IOUtil;
61    import org.codehaus.plexus.util.StringUtils;
62    import org.springframework.core.io.DefaultResourceLoader;
63    import org.springframework.core.io.Resource;
64    import org.springframework.core.io.ResourceLoader;
65   
66    /**
67    * Executes SQL against a database.
68    *
69    * @goal execute
70    */
 
71    public class SqlExecMojo extends AbstractMojo {
72   
73    /**
74    * Call {@link #setOnError(String)} with this value to abort SQL command
75    * execution if an error is found.
76    */
77    public static final String ON_ERROR_ABORT = "abort";
78   
79    /**
80    * Call {@link #setOnError(String)} with this value to continue SQL command
81    * execution until all commands have been attempted, then abort the build if
82    * an SQL error occurred in any of the commands.
83    */
84    public static final String ON_ERROR_ABORT_AFTER = "abortAfter";
85   
86    /**
87    * Call {@link #setOnError(String)} with this value to continue SQL command
88    * execution if an error is found.
89    */
90    public static final String ON_ERROR_CONTINUE = "continue";
91   
92    /**
93    * Call {@link #setOrderFile(String)} with this value to sort in ascendant
94    * order the sql files.
95    */
96    public static final String FILE_SORTING_ASC = "ascending";
97   
98    /**
99    * Call {@link #setOrderFile(String)} with this value to sort in descendant
100    * order the sql files.
101    */
102    public static final String FILE_SORTING_DSC = "descending";
103   
104    // ////////////////////////// User Info ///////////////////////////////////
105   
106    /**
107    * Database username. If not given, it will be looked up through
108    * <code>settings.xml</code>'s server with <code>${settingsKey}</code> as
109    * key.
110    *
111    * @since 1.0
112    * @parameter expression="${username}"
113    */
114    private String username;
115   
116    /**
117    * Database password. If not given, it will be looked up through
118    * <code>settings.xml</code>'s server with <code>${settingsKey}</code> as
119    * key.
120    *
121    * @since 1.0
122    * @parameter expression="${password}"
123    */
124    private String password;
125   
126    /**
127    * Ignore the password and use anonymous access. This may be useful for
128    * databases like MySQL which do not allow empty password parameters in the
129    * connection initialization.
130    *
131    * @since 1.4
132    * @parameter default-value="false"
133    */
134    private boolean enableAnonymousPassword;
135   
136    /**
137    * Additional key=value pairs separated by comma to be passed into JDBC
138    * driver.
139    *
140    * @since 1.0
141    * @parameter expression="${driverProperties}" default-value = ""
142    */
143    private String driverProperties;
144   
145    /**
146    * @parameter expression="${settings}"
147    * @required
148    * @since 1.0
149    * @readonly
150    */
151    private Settings settings;
152   
153    /**
154    * Server's <code>id</code> in <code>settings.xml</code> to look up username
155    * and password. Defaults to <code>${url}</code> if not given.
156    *
157    * @since 1.0
158    * @parameter expression="${settingsKey}"
159    */
160    private String settingsKey;
161   
162    /**
163    * Skip execution when there is an error obtaining a connection. This is a
164    * special case to support databases, such as embedded Derby, that can
165    * shutdown the database via the URL (i.e. <code>shutdown=true</code>).
166    *
167    * @since 1.1
168    * @parameter expression="${skipOnConnectionError}" default-value="false"
169    */
170    private boolean skipOnConnectionError;
171   
172    /**
173    * Setting this parameter to <code>true</code> will force the execution of
174    * this mojo, even if it would get skipped usually.
175    *
176    * @parameter expression="${forceOpenJpaExecution}" default-value=false
177    * @required
178    */
179    private boolean forceMojoExecution;
180   
181    /**
182    * The Maven Project Object
183    *
184    * @parameter default-value="${project}"
185    * @required
186    * @readonly
187    */
188    protected MavenProject project;
189   
190    /**
191    * @parameter default-value="${session}"
192    * @required
193    * @readonly
194    */
195    private MavenSession mavenSession;
196   
197    // ////////////////////////////// Source info /////////////////////////////
198   
199    /**
200    * SQL input commands separated by <code>${delimiter}</code>.
201    *
202    * @since 1.0
203    * @parameter expression="${sqlCommand}" default-value=""
204    */
205    private String sqlCommand = "";
206   
207    /**
208    * List of files containing SQL statements to load.
209    *
210    * @since 1.0
211    * @parameter
212    */
213    private File[] srcFiles;
214   
215    /**
216    * List of resources containing SQL statements
217    *
218    * @since 1.5
219    * @parameter
220    */
221    private String[] resourceLocations;
222   
223    /**
224    * File(s) containing SQL statements to load.
225    *
226    * @since 1.0
227    * @parameter
228    */
229    private Fileset fileset;
230   
231    /**
232    * When <code>true</code>, skip the execution.
233    *
234    * @since 1.0
235    * @parameter default-value="false"
236    */
237    private boolean skip;
238   
239    // //////////////////////////////// Database info /////////////////////////
240    /**
241    * Database URL.
242    *
243    * @parameter expression="${url}"
244    * @required
245    * @since 1.0-beta-1
246    */
247    private String url;
248   
249    /**
250    * Database driver classname.
251    *
252    * @since 1.0
253    * @parameter expression="${driver}"
254    * @required
255    */
256    private String driver;
257   
258    // //////////////////////////// Operation Configuration ////////////////////
259    /**
260    * Set to <code>true</code> to execute none-transactional SQL.
261    *
262    * @since 1.0
263    * @parameter expression="${autocommit}" default-value="false"
264    */
265    private boolean autocommit;
266   
267    /**
268    * Action to perform if an error is found. Possible values are
269    * <code>abort</code> and <code>continue</code>.
270    *
271    * @since 1.0
272    * @parameter expression="${onError}" default-value="abort"
273    */
274    private String onError = ON_ERROR_ABORT;
275   
276    // //////////////////////////// Parser Configuration ////////////////////
277   
278    /**
279    * Set the delimiter that separates SQL statements.
280    *
281    * @since 1.0
282    * @parameter expression="${delimiter}" default-value=";"
283    */
284    private String delimiter = ";";
285   
286    /**
287    * <p>
288    * The delimiter type takes two values - "normal" and "row". Normal means
289    * that any occurrence of the delimiter terminate the SQL command whereas
290    * with row, only a line containing just the delimiter is recognized as the
291    * end of the command.
292    * </p>
293    * <p>
294    * For example, set this to "go" and delimiterType to "row" for Sybase ASE
295    * or MS SQL Server.
296    * </p>
297    *
298    * @since 1.2
299    * @parameter expression="${delimiterType}" default-value="normal"
300    */
301    private String delimiterType = DelimiterType.NORMAL;
302   
303    /**
304    * Set the order in which the SQL files will be executed. Possible values
305    * are <code>ascending</code> and <code>descending</code>. Any other value
306    * means that no sorting will be performed.
307    *
308    * @since 1.1
309    * @parameter expression="${orderFile}"
310    */
311    private String orderFile = null;
312   
313    /**
314    * When <code>true</code>, the whole SQL content in <code>sqlCommand</code>,
315    * <code>srcFiles</code> and <code>fileset</code> are sent directly to JDBC
316    * in one SQL statement. This option is for executing database stored
317    * procedures/functions.
318    *
319    * @deprecated used <i>delimiterType<i> instead.
320    * @since 1.1
321    * @parameter expression="${enableBlockMode}"
322    */
323   
324    private boolean enableBlockMode = false;
325   
326    /**
327    * Keep the format of an SQL block.
328    *
329    * @since 1.1
330    * @parameter expression="${keepFormat}" default-value="false"
331    */
332    private boolean keepFormat = false;
333   
334    // /////////////////////////////////////////////////////////////////////////////////////
335    /**
336    * Print SQL results.
337    *
338    * @parameter
339    * @since 1.3
340    */
341    private boolean printResultSet = false;
342   
343    /**
344    * Print header columns.
345    */
346    private boolean showheaders = true;
347   
348    /**
349    * Dump the SQL exection's output to a file. Default is stdout.
350    *
351    * @parameter
352    * @since 1.3
353    */
354    private File outputFile;
355   
356    /**
357    * @parameter default-value=","
358    * @since 1.4
359    */
360    private String outputDelimiter;
361   
362    /**
363    * Encoding to use when reading SQL statements from a file.
364    *
365    * @parameter expression="${encoding}" default-value=
366    * "${project.build.sourceEncoding}"
367    * @since 1.1
368    */
369    private String encoding = "";
370   
371    /**
372    * Append to an existing file or overwrite it?
373    */
374    private boolean append = false;
375   
376    /**
377    * Argument to Statement.setEscapeProcessing If you want the driver to use
378    * regular SQL syntax then set this to false.
379    *
380    * @since 1.4
381    * @parameter expression="${escapeProcessing}" default-value="true"
382    */
383    private boolean escapeProcessing = true;
384   
385    // //////////////////////////////// Internal
386    // properties//////////////////////
387   
388    /**
389    * number of successful executed statements
390    */
391    private int successfulStatements = 0;
392   
393    /**
394    * number of total executed statements
395    */
396    private int totalStatements = 0;
397   
398    /**
399    * Database connection
400    */
401    private Connection conn = null;
402   
403    /**
404    * SQL statement
405    */
406    private Statement statement = null;
407   
408    /**
409    * SQL transactions to perform
410    */
411    private Vector<Transaction> transactions = new Vector<Transaction>();
412   
413    /**
414    * @component role="org.apache.maven.shared.filtering.MavenFileFilter"
415    * @since 1.4
416    */
417    private MavenFileFilter fileFilter;
418   
419    /**
420    * Set to true if you want to filter the srcFiles using system-, user- and
421    * project properties
422    *
423    * @parameter
424    * @since 1.4
425    */
426    private boolean enableFiltering;
427   
428    /**
429    * Interpolator especially for braceless expressions
430    */
431    // private Interpolator interpolator = new
432    // RegexBasedInterpolator("\\$([^\\s;)]+?)", "(?=[\\s;)])");
433   
434    /**
435    * Add a SQL transaction to execute
436    *
437    * @return a new SqlExecMojo.Transaction
438    */
 
439  0 toggle public Transaction createTransaction() {
440  0 Transaction t = new Transaction();
441  0 transactions.addElement(t);
442  0 return t;
443    }
444   
445    /**
446    * Set an inline SQL command to execute. NB: Properties are not expanded in
447    * this text.
448    *
449    * @param sql
450    * the sql statement to add
451    */
 
452  0 toggle public void addText(String sql) {
453  0 this.sqlCommand += sql;
454    }
455   
456    /**
457    * Set the file encoding to use on the SQL files read in
458    *
459    * @param encoding
460    * the encoding to use on the files
461    */
 
462  0 toggle public void setEncoding(String encoding) {
463  0 this.encoding = encoding;
464    }
465   
466    /**
467    * Set the delimiter that separates SQL statements. Defaults to
468    * &quot;;&quot;;
469    *
470    * @param delimiter
471    * the new delimiter
472    */
 
473  0 toggle public void setDelimiter(String delimiter) {
474  0 this.delimiter = delimiter;
475    }
476   
477    /**
478    * Set the delimiter type: "normal" or "row" (default "normal").
479    *
480    * @param delimiterType
481    * the new delimiterType
482    */
 
483  0 toggle public void setDelimiterType(String delimiterType) {
484  0 this.delimiterType = delimiterType;
485    }
486   
487    /**
488    * Print result sets from the statements; optional, default false
489    *
490    * @param print
491    * <code>true</code> to print the resultset, otherwise
492    * <code>false</code>
493    * @deprecated typo, use setPrintResultSet()
494    */
 
495  0 toggle public void setPrintResutlSet(boolean print) {
496  0 setPrintResultSet(print);
497    }
498   
499    /**
500    * Print result sets from the statements; optional, default false
501    *
502    * @param print
503    * <code>true</code> to print the resultset, otherwise
504    * <code>false</code>
505    */
 
506  0 toggle public void setPrintResultSet(boolean print) {
507  0 this.printResultSet = print;
508    }
509   
510    /**
511    * Print headers for result sets from the statements; optional, default
512    * true.
513    *
514    * @param showheaders
515    * <code>true</code> to show the headers, otherwise
516    * <code>false</code>
517    */
 
518  0 toggle public void setShowheaders(boolean showheaders) {
519  0 this.showheaders = showheaders;
520    }
521   
522    /**
523    * Set the output file;
524    *
525    * @param output
526    * the output file
527    */
 
528  0 toggle public void setOutputFile(File output) {
529  0 this.outputFile = output;
530    }
531   
532    /**
533    * whether output should be appended to or overwrite an existing file.
534    * Defaults to false.
535    *
536    * @param append
537    * <code>true</code> to append, otherwise <code>false</code> to
538    * overwrite
539    */
 
540  0 toggle public void setAppend(boolean append) {
541  0 this.append = append;
542    }
543   
544    /**
545    * whether or not format should be preserved. Defaults to false.
546    *
547    * @param keepformat
548    * The keepformat to set
549    */
 
550  0 toggle public void setKeepFormat(boolean keepformat) {
551  0 this.keepFormat = keepformat;
552    }
553   
554    /**
555    * Set escape processing for statements.
556    *
557    * @param enable
558    * <code>true</code> to escape, otherwiser <code>false</code>
559    */
 
560  0 toggle public void setEscapeProcessing(boolean enable) {
561  0 escapeProcessing = enable;
562    }
563   
564    /**
565    * <p>
566    * Determine if the mojo execution should get skipped.
567    * </p>
568    * This is the case if:
569    * <ul>
570    * <li>{@link #skip} is <code>true</code></li>
571    * <li>if the mojo gets executed on a project with packaging type 'pom' and
572    * {@link #forceMojoExecution} is <code>false</code></li>
573    * </ul>
574    *
575    * @return <code>true</code> if the mojo execution should be skipped.
576    */
 
577  0 toggle protected boolean skipMojo() {
578  0 if (skip) {
579  0 getLog().info("Skip sql execution");
580  0 return true;
581    }
582   
583  0 if (!forceMojoExecution && project != null && "pom".equals(project.getPackaging())) {
584  0 getLog().info("Skipping sql execution for project with packaging type 'pom'");
585  0 return true;
586    }
587   
588  0 return false;
589    }
590   
591    /**
592    * Load the sql file and then execute it
593    *
594    * @throws MojoExecutionException
595    */
 
596  0 toggle public void execute() throws MojoExecutionException {
597   
598  0 if (skipMojo()) {
599  0 return;
600    }
601   
602  0 successfulStatements = 0;
603   
604  0 totalStatements = 0;
605   
606  0 loadUserInfoFromSettings();
607   
608  0 addCommandToTransactions();
609   
610  0 addFilesToTransactions();
611   
612  0 addFileSetToTransactions();
613   
614  0 addResourcesToTransactions();
615   
616  0 sortTransactions();
617   
618  0 try {
619  0 conn = getConnection();
620    } catch (SQLException e) {
621  0 if (!this.skipOnConnectionError) {
622  0 throw new MojoExecutionException(e.getMessage(), e);
623    } else {
624    // error on get connection and user asked to skip the rest
625  0 return;
626    }
627    }
628   
629  0 try {
630  0 statement = conn.createStatement();
631  0 statement.setEscapeProcessing(escapeProcessing);
632   
633  0 PrintStream out = System.out;
634  0 try {
635  0 if (outputFile != null) {
636  0 getLog().debug("Opening PrintStream to output file " + outputFile);
637  0 out = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile.getAbsolutePath(),
638    append)));
639    }
640   
641    // Process all transactions
642  0 for (Enumeration<Transaction> e = transactions.elements(); e.hasMoreElements();) {
643  0 Transaction t = e.nextElement();
644   
645  0 t.runTransaction(out);
646   
647  0 if (!autocommit) {
648  0 getLog().debug("Committing transaction");
649  0 conn.commit();
650    }
651    }
652    } finally {
653  0 if (out != null && out != System.out) {
654  0 out.close();
655    }
656    }
657    } catch (IOException e) {
658  0 throw new MojoExecutionException(e.getMessage(), e);
659    } catch (SQLException e) {
660  0 if (!autocommit && conn != null && ON_ERROR_ABORT.equalsIgnoreCase(getOnError())) {
661  0 try {
662  0 conn.rollback();
663    } catch (SQLException ex) {
664    // ignore
665    }
666    }
667  0 throw new MojoExecutionException(e.getMessage(), e);
668    } finally {
669  0 try {
670  0 if (statement != null) {
671  0 statement.close();
672    }
673  0 if (conn != null) {
674  0 conn.close();
675    }
676    } catch (SQLException ex) {
677    // ignore
678    }
679    }
680   
681  0 getLog().info(
682    getSuccessfulStatements() + " of " + getTotalStatements() + " SQL statements executed successfully");
683   
684  0 if (ON_ERROR_ABORT_AFTER.equalsIgnoreCase(getOnError()) && totalStatements != successfulStatements) {
685  0 throw new MojoExecutionException("Some SQL statements failed to execute");
686    }
687   
688    }
689   
690    /**
691    * Add sql command to transactions list.
692    *
693    */
 
694  0 toggle private void addCommandToTransactions() {
695  0 createTransaction().addText(sqlCommand.trim());
696    }
697   
698    /**
699    * Add user sql fileset to transation list
700    *
701    */
 
702  0 toggle private void addFileSetToTransactions() {
703  0 String[] includedFiles;
704  0 if (fileset != null) {
705  0 fileset.scan();
706  0 includedFiles = fileset.getIncludedFiles();
707    } else {
708  0 includedFiles = new String[0];
709    }
710   
711  0 for (int j = 0; j < includedFiles.length; j++) {
712  0 createTransaction().setSrc(new File(fileset.getBasedir(), includedFiles[j]));
713    }
714    }
715   
 
716  0 toggle protected Resource[] getResources(String[] locations) throws MojoExecutionException {
717  0 ResourceLoader loader = new DefaultResourceLoader();
718  0 Resource[] resources = new Resource[locations.length];
719  0 for (int i = 0; i < locations.length; i++) {
720  0 String location = locations[i];
721  0 Resource resource = loader.getResource(location);
722  0 if (!resource.exists()) {
723  0 throw new MojoExecutionException("Resource " + location + " was not found");
724    }
725  0 resources[i] = resource;
726    }
727  0 return resources;
728    }
729   
 
730  0 toggle protected void copy(Resource resource, File file) throws IOException {
731  0 InputStream in = resource.getInputStream();
732  0 OutputStream out = new FileOutputStream(file);
733  0 IOUtils.copyLarge(in, out);
734    }
735   
736    /**
737    * Add user input of srcFiles to transaction list.
738    *
739    * @throws MojoExecutionException
740    */
 
741  0 toggle private void addResourcesToTransactions() throws MojoExecutionException {
742  0 String[] locations = getResourceLocations();
743  0 Resource[] resources = getResources(locations);
744   
745  0 MavenFileFilterRequest request = new MavenFileFilterRequest();
746  0 request.setEncoding(encoding);
747  0 request.setMavenSession(mavenSession);
748  0 request.setMavenProject(project);
749  0 request.setFiltering(enableFiltering);
750  0 for (int i = 0; i < resources.length; i++) {
751  0 Resource resource = resources[i];
752  0 String filename = resource.getFilename();
753  0 String basename = FileUtils.basename(filename);
754  0 String extension = FileUtils.extension(filename);
755  0 File sourceFile = FileUtils.createTempFile(basename, extension, null);
756   
757  0 try {
758  0 copy(resource, sourceFile);
759    } catch (IOException e) {
760  0 throw new MojoExecutionException("Error copying resource " + resource + "to a temp file", e);
761    }
762   
763  0 File targetFile = FileUtils.createTempFile(basename, extension, null);
764  0 if (!getLog().isDebugEnabled()) {
765  0 targetFile.deleteOnExit();
766  0 sourceFile.deleteOnExit();
767    }
768   
769  0 request.setFrom(sourceFile);
770  0 request.setTo(targetFile);
771   
772  0 try {
773  0 fileFilter.copyFile(request);
774    } catch (MavenFilteringException e) {
775  0 throw new MojoExecutionException(e.getMessage());
776    }
777   
778  0 createTransaction().setSrc(targetFile);
779    }
780    }
781   
782    /**
783    * Add user input of srcFiles to transaction list.
784    *
785    * @throws MojoExecutionException
786    */
 
787  0 toggle private void addFilesToTransactions() throws MojoExecutionException {
788  0 File[] files = getSrcFiles();
789   
790  0 MavenFileFilterRequest request = new MavenFileFilterRequest();
791  0 request.setEncoding(encoding);
792  0 request.setMavenSession(mavenSession);
793  0 request.setMavenProject(project);
794  0 request.setFiltering(enableFiltering);
795  0 for (int i = 0; files != null && i < files.length; ++i) {
796  0 if (files[i] != null && !files[i].exists()) {
797  0 throw new MojoExecutionException(files[i].getPath() + " not found.");
798    }
799   
800  0 File sourceFile = files[i];
801  0 String basename = FileUtils.basename(sourceFile.getName());
802  0 String extension = FileUtils.extension(sourceFile.getName());
803  0 File targetFile = FileUtils.createTempFile(basename, extension, null);
804  0 if (!getLog().isDebugEnabled()) {
805  0 targetFile.deleteOnExit();
806    }
807   
808  0 request.setFrom(sourceFile);
809  0 request.setTo(targetFile);
810   
811  0 try {
812  0 fileFilter.copyFile(request);
813    } catch (MavenFilteringException e) {
814  0 throw new MojoExecutionException(e.getMessage());
815    }
816   
817  0 createTransaction().setSrc(targetFile);
818    }
819    }
820   
821    /**
822    * Sort the transaction list.
823    */
 
824  0 toggle private void sortTransactions() {
825  0 if (FILE_SORTING_ASC.equalsIgnoreCase(this.orderFile)) {
826  0 Collections.sort(transactions);
827  0 } else if (FILE_SORTING_DSC.equalsIgnoreCase(this.orderFile)) {
828  0 Collections.sort(transactions, Collections.reverseOrder());
829    }
830    }
831   
832    /**
833    * Load username password from settings if user has not set them in JVM
834    * properties
835    *
836    * @throws MojoExecutionException
837    */
 
838  0 toggle private void loadUserInfoFromSettings() throws MojoExecutionException {
839  0 if (this.settingsKey == null) {
840  0 this.settingsKey = getUrl();
841    }
842   
843  0 if ((getUsername() == null || getPassword() == null) && (settings != null)) {
844  0 Server server = this.settings.getServer(this.settingsKey);
845   
846  0 if (server != null) {
847  0 if (getUsername() == null) {
848  0 setUsername(server.getUsername());
849    }
850   
851  0 if (getPassword() == null) {
852  0 setPassword(server.getPassword());
853    }
854    }
855    }
856   
857  0 if (getUsername() == null) {
858    // allow emtpy username
859  0 setUsername("");
860    }
861   
862  0 if (getPassword() == null) {
863    // allow emtpy password
864  0 setPassword("");
865    }
866    }
867   
868    /**
869    * Creates a new Connection as using the driver, url, userid and password
870    * specified.
871    *
872    * The calling method is responsible for closing the connection.
873    *
874    * @return Connection the newly created connection.
875    * @throws MojoExecutionException
876    * if the UserId/Password/Url is not set or there is no suitable
877    * driver or the driver fails to load.
878    * @throws SQLException
879    * if there is problem getting connection with valid url
880    *
881    */
 
882  0 toggle private Connection getConnection() throws MojoExecutionException, SQLException {
883  0 getLog().debug("connecting to " + getUrl());
884  0 Properties info = new Properties();
885  0 info.put("user", getUsername());
886   
887  0 if (!enableAnonymousPassword) {
888  0 info.put("password", getPassword());
889    }
890   
891  0 info.putAll(this.getDriverProperties());
892   
893  0 Driver driverInstance = null;
894   
895  0 try {
896  0 Class<?> dc = Class.forName(getDriver());
897  0 driverInstance = (Driver) dc.newInstance();
898    } catch (ClassNotFoundException e) {
899  0 throw new MojoExecutionException("Driver class not found: " + getDriver(), e);
900    } catch (Exception e) {
901  0 throw new MojoExecutionException("Failure loading driver: " + getDriver(), e);
902    }
903   
904  0 Connection conn = driverInstance.connect(getUrl(), info);
905   
906  0 if (conn == null) {
907    // Driver doesn't understand the URL
908  0 throw new SQLException("No suitable Driver for " + getUrl());
909    }
910   
911  0 conn.setAutoCommit(autocommit);
912  0 return conn;
913    }
914   
915    /**
916    * parse driverProperties into Properties set
917    *
918    * @return the driver properties
919    * @throws MojoExecutionException
920    */
 
921  0 toggle protected Properties getDriverProperties() throws MojoExecutionException {
922  0 Properties properties = new Properties();
923   
924  0 if (!StringUtils.isEmpty(this.driverProperties)) {
925  0 String[] tokens = StringUtils.split(this.driverProperties, ",");
926  0 for (int i = 0; i < tokens.length; ++i) {
927  0 String[] keyValueTokens = StringUtils.split(tokens[i].trim(), "=");
928  0 if (keyValueTokens.length != 2) {
929  0 throw new MojoExecutionException("Invalid JDBC Driver properties: " + this.driverProperties);
930    }
931   
932  0 properties.setProperty(keyValueTokens[0], keyValueTokens[1]);
933   
934    }
935    }
936   
937  0 return properties;
938    }
939   
940    /**
941    * read in lines and execute them
942    *
943    * @param reader
944    * the reader
945    * @param out
946    * the outputstream
947    * @throws SQLException
948    * @throws IOException
949    */
 
950  0 toggle private void runStatements(Reader reader, PrintStream out) throws SQLException, IOException {
951  0 String line;
952   
953  0 if (enableBlockMode) {
954    // no need to parse the content, ship it directly to jdbc in one sql
955    // statement
956  0 line = IOUtil.toString(reader);
957  0 execSQL(line, out);
958  0 return;
959    }
960   
961  0 StringBuffer sql = new StringBuffer();
962   
963  0 BufferedReader in = new BufferedReader(reader);
964   
965  0 while ((line = in.readLine()) != null) {
966  0 if (!keepFormat) {
967  0 line = line.trim();
968    }
969   
970  0 if (!keepFormat) {
971  0 if (line.startsWith("//")) {
972  0 continue;
973    }
974  0 if (line.startsWith("--")) {
975  0 continue;
976    }
977  0 StringTokenizer st = new StringTokenizer(line);
978  0 if (st.hasMoreTokens()) {
979  0 String token = st.nextToken();
980  0 if ("REM".equalsIgnoreCase(token)) {
981  0 continue;
982    }
983    }
984    }
985   
986  0 if (!keepFormat) {
987  0 sql.append(" ").append(line);
988    } else {
989  0 sql.append("\n").append(line);
990    }
991   
992    // SQL defines "--" as a comment to EOL
993    // and in Oracle it may contain a hint
994    // so we cannot just remove it, instead we must end it
995  0 if (!keepFormat) {
996  0 if (SqlSplitter.containsSqlEnd(line, delimiter) == SqlSplitter.NO_END) {
997  0 sql.append("\n");
998    }
999    }
1000   
1001  0 if ((delimiterType.equals(DelimiterType.NORMAL) && SqlSplitter.containsSqlEnd(line, delimiter) > 0)
1002    || (delimiterType.equals(DelimiterType.ROW) && line.trim().equals(delimiter))) {
1003  0 execSQL(sql.substring(0, sql.length() - delimiter.length()), out);
1004  0 sql.setLength(0); // clean buffer
1005    }
1006    }
1007   
1008    // Catch any statements not followed by ;
1009  0 if (!sql.toString().equals("")) {
1010  0 execSQL(sql.toString(), out);
1011    }
1012    }
1013   
1014    /**
1015    * Exec the sql statement.
1016    *
1017    * @param sql
1018    * query to execute
1019    * @param out
1020    * the outputstream
1021    */
 
1022  0 toggle private void execSQL(String sql, PrintStream out) throws SQLException {
1023    // Check and ignore empty statements
1024  0 if ("".equals(sql.trim())) {
1025  0 return;
1026    }
1027   
1028  0 ResultSet resultSet = null;
1029  0 try {
1030  0 totalStatements++;
1031  0 getLog().debug("SQL: " + sql);
1032   
1033  0 boolean ret;
1034  0 int updateCountTotal = 0;
1035   
1036  0 ret = statement.execute(sql);
1037  0 do {
1038  0 if (!ret) {
1039  0 int updateCount = statement.getUpdateCount();
1040  0 if (updateCount != -1) {
1041  0 updateCountTotal += updateCount;
1042    }
1043    } else {
1044  0 resultSet = statement.getResultSet();
1045  0 if (printResultSet) {
1046  0 printResultSet(resultSet, out);
1047    }
1048    }
1049  0 ret = statement.getMoreResults();
1050  0 } while (ret);
1051   
1052  0 getLog().debug(updateCountTotal + " rows affected");
1053   
1054  0 if (printResultSet) {
1055  0 StringBuffer line = new StringBuffer();
1056  0 line.append(updateCountTotal).append(" rows affected");
1057  0 out.println(line);
1058    }
1059   
1060  0 SQLWarning warning = conn.getWarnings();
1061  0 while (warning != null) {
1062  0 getLog().debug(warning + " sql warning");
1063  0 warning = warning.getNextWarning();
1064    }
1065  0 conn.clearWarnings();
1066  0 successfulStatements++;
1067    } catch (SQLException e) {
1068  0 getLog().error("Failed to execute: " + sql);
1069  0 if (ON_ERROR_ABORT.equalsIgnoreCase(getOnError())) {
1070  0 throw e;
1071    }
1072  0 getLog().error(e.toString());
1073    } finally {
1074  0 if (resultSet != null) {
1075  0 resultSet.close();
1076    }
1077    }
1078    }
1079   
1080    /**
1081    * print any results in the result set.
1082    *
1083    * @param rs
1084    * the resultset to print information about
1085    * @param out
1086    * the place to print results
1087    * @throws SQLException
1088    * on SQL problems.
1089    */
 
1090  0 toggle private void printResultSet(ResultSet rs, PrintStream out) throws SQLException {
1091  0 if (rs != null) {
1092  0 getLog().debug("Processing new result set.");
1093  0 ResultSetMetaData md = rs.getMetaData();
1094  0 int columnCount = md.getColumnCount();
1095  0 StringBuffer line = new StringBuffer();
1096  0 if (showheaders) {
1097  0 boolean first = true;
1098  0 for (int col = 1; col <= columnCount; col++) {
1099  0 String columnValue = md.getColumnName(col);
1100   
1101  0 if (columnValue != null) {
1102  0 columnValue = columnValue.trim();
1103   
1104  0 if (",".equals(outputDelimiter)) {
1105  0 columnValue = StringEscapeUtils.escapeCsv(columnValue);
1106    }
1107    }
1108   
1109  0 if (first) {
1110  0 first = false;
1111    } else {
1112  0 line.append(outputDelimiter);
1113    }
1114  0 line.append(columnValue);
1115    }
1116  0 out.println(line);
1117  0 line = new StringBuffer();
1118    }
1119  0 while (rs.next()) {
1120  0 boolean first = true;
1121  0 for (int col = 1; col <= columnCount; col++) {
1122  0 String columnValue = rs.getString(col);
1123  0 if (columnValue != null) {
1124  0 columnValue = columnValue.trim();
1125   
1126  0 if (",".equals(outputDelimiter)) {
1127  0 columnValue = StringEscapeUtils.escapeCsv(columnValue);
1128    }
1129    }
1130   
1131  0 if (first) {
1132  0 first = false;
1133    } else {
1134  0 line.append(outputDelimiter);
1135    }
1136  0 line.append(columnValue);
1137    }
1138  0 out.println(line);
1139  0 line = new StringBuffer();
1140    }
1141    }
1142  0 out.println();
1143    }
1144   
1145    /**
1146    * Contains the definition of a new transaction element. Transactions allow
1147    * several files or blocks of statements to be executed using the same JDBC
1148    * connection and commit operation in between.
1149    */
 
1150    private class Transaction implements Comparable<Transaction> {
1151    private File tSrcFile = null;
1152   
1153    private String tSqlCommand = "";
1154   
1155    /**
1156    *
1157    */
 
1158  0 toggle public void setSrc(File src) {
1159  0 this.tSrcFile = src;
1160    }
1161   
1162    /**
1163    *
1164    */
 
1165  0 toggle public void addText(String sql) {
1166  0 this.tSqlCommand += sql;
1167    }
1168   
1169    /**
1170    *
1171    */
 
1172  0 toggle private void runTransaction(PrintStream out) throws IOException, SQLException {
1173  0 if (tSqlCommand.length() != 0) {
1174  0 getLog().info("Executing commands");
1175   
1176  0 runStatements(new StringReader(tSqlCommand), out);
1177    }
1178   
1179  0 if (tSrcFile != null) {
1180  0 getLog().info("Executing file: " + tSrcFile.getAbsolutePath());
1181   
1182  0 Reader reader = null;
1183   
1184  0 if (StringUtils.isEmpty(encoding)) {
1185  0 reader = new FileReader(tSrcFile);
1186    } else {
1187  0 reader = new InputStreamReader(new FileInputStream(tSrcFile), encoding);
1188    }
1189   
1190  0 try {
1191  0 runStatements(reader, out);
1192    } finally {
1193  0 reader.close();
1194    }
1195    }
1196    }
1197   
 
1198  0 toggle public int compareTo(Transaction transaction) {
1199   
1200  0 if (transaction.tSrcFile == null) {
1201  0 if (this.tSrcFile == null) {
1202  0 return 0;
1203    } else {
1204  0 return Integer.MAX_VALUE;
1205    }
1206    } else {
1207  0 if (this.tSrcFile == null) {
1208  0 return Integer.MIN_VALUE;
1209    } else {
1210  0 return this.tSrcFile.compareTo(transaction.tSrcFile);
1211    }
1212    }
1213    }
1214    }
1215   
1216    //
1217    // helper accessors for unit test purposes
1218    //
1219   
 
1220  0 toggle public String getUsername() {
1221  0 return this.username;
1222    }
1223   
 
1224  0 toggle public void setUsername(String username) {
1225  0 this.username = username;
1226    }
1227   
 
1228  0 toggle public String getPassword() {
1229  0 return this.password;
1230    }
1231   
 
1232  0 toggle public void setPassword(String password) {
1233  0 this.password = password;
1234    }
1235   
 
1236  0 toggle public String getUrl() {
1237  0 return this.url;
1238    }
1239   
 
1240  0 toggle public void setUrl(String url) {
1241  0 this.url = url;
1242    }
1243   
 
1244  0 toggle public String getDriver() {
1245  0 return this.driver;
1246    }
1247   
 
1248  0 toggle public void setDriver(String driver) {
1249  0 this.driver = driver;
1250    }
1251   
 
1252  0 toggle void setAutocommit(boolean autocommit) {
1253  0 this.autocommit = autocommit;
1254    }
1255   
 
1256  0 toggle void setFileset(Fileset fileset) {
1257  0 this.fileset = fileset;
1258    }
1259   
 
1260  0 toggle public File[] getSrcFiles() {
1261  0 return this.srcFiles;
1262    }
1263   
 
1264  0 toggle public void setSrcFiles(File[] files) {
1265  0 this.srcFiles = files;
1266    }
1267   
 
1268  0 toggle public String getOrderFile() {
1269  0 return this.orderFile;
1270    }
1271   
 
1272  0 toggle public void setOrderFile(String orderFile) {
1273  0 if (FILE_SORTING_ASC.equalsIgnoreCase(orderFile)) {
1274  0 this.orderFile = FILE_SORTING_ASC;
1275  0 } else if (FILE_SORTING_DSC.equalsIgnoreCase(orderFile)) {
1276  0 this.orderFile = FILE_SORTING_DSC;
1277    } else {
1278  0 throw new IllegalArgumentException(orderFile + " is not a valid value for orderFile, only '"
1279    + FILE_SORTING_ASC + "' or '" + FILE_SORTING_DSC + "'.");
1280    }
1281    }
1282   
1283    /**
1284    * @deprecated use {@link #getSuccessfulStatements()}
1285    */
 
1286  0 toggle int getGoodSqls() {
1287  0 return this.getSuccessfulStatements();
1288    }
1289   
1290    /**
1291    * Number of SQL statements executed so far that caused errors.
1292    *
1293    * @return the number
1294    */
 
1295  0 toggle public int getSuccessfulStatements() {
1296  0 return successfulStatements;
1297    }
1298   
1299    /**
1300    * Number of SQL statements executed so far, including the ones that caused
1301    * errors.
1302    *
1303    * @return the number
1304    */
 
1305  0 toggle public int getTotalStatements() {
1306  0 return totalStatements;
1307    }
1308   
 
1309  0 toggle public String getOnError() {
1310  0 return this.onError;
1311    }
1312   
 
1313  0 toggle public void setOnError(String action) {
1314  0 if (ON_ERROR_ABORT.equalsIgnoreCase(action)) {
1315  0 this.onError = ON_ERROR_ABORT;
1316  0 } else if (ON_ERROR_CONTINUE.equalsIgnoreCase(action)) {
1317  0 this.onError = ON_ERROR_CONTINUE;
1318  0 } else if (ON_ERROR_ABORT_AFTER.equalsIgnoreCase(action)) {
1319  0 this.onError = ON_ERROR_ABORT_AFTER;
1320    } else {
1321  0 throw new IllegalArgumentException(action + " is not a valid value for onError, only '" + ON_ERROR_ABORT
1322    + "', '" + ON_ERROR_ABORT_AFTER + "', or '" + ON_ERROR_CONTINUE + "'.");
1323    }
1324    }
1325   
 
1326  0 toggle void setSettings(Settings settings) {
1327  0 this.settings = settings;
1328    }
1329   
 
1330  0 toggle void setSettingsKey(String key) {
1331  0 this.settingsKey = key;
1332    }
1333   
 
1334  0 toggle void setSkip(boolean skip) {
1335  0 this.skip = skip;
1336    }
1337   
 
1338  0 toggle public void setDriverProperties(String driverProperties) {
1339  0 this.driverProperties = driverProperties;
1340    }
1341   
 
1342  0 toggle public boolean isEnableBlockMode() {
1343  0 return enableBlockMode;
1344    }
1345   
 
1346  0 toggle public void setEnableBlockMode(boolean enableBlockMode) {
1347  0 this.enableBlockMode = enableBlockMode;
1348    }
1349   
 
1350  0 toggle public String getSqlCommand() {
1351  0 return sqlCommand;
1352    }
1353   
 
1354  0 toggle public void setSqlCommand(String sqlCommand) {
1355  0 this.sqlCommand = sqlCommand;
1356    }
1357   
 
1358  0 toggle public Vector<Transaction> getTransactions() {
1359  0 return transactions;
1360    }
1361   
 
1362  0 toggle public void setTransactions(Vector<Transaction> transactions) {
1363  0 this.transactions = transactions;
1364    }
1365   
 
1366  0 toggle public void setFileFilter(MavenFileFilter filter) {
1367  0 this.fileFilter = filter;
1368    }
1369   
 
1370  0 toggle public String[] getResourceLocations() {
1371  0 return resourceLocations;
1372    }
1373   
 
1374  0 toggle public void setResourceLocations(String[] resourceLocations) {
1375  0 this.resourceLocations = resourceLocations;
1376    }
1377    }