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