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