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