001package com.hfg.sql;
002
003import java.io.BufferedReader;
004import java.io.IOException;
005import java.io.Reader;
006import java.sql.*;
007import java.util.ArrayList;
008import java.util.Collection;
009import java.util.List;
010import java.util.logging.Level;
011import java.util.logging.Logger;
012
013import com.hfg.datetime.DateUtil;
014import com.hfg.sql.jdbc.JDBCException;
015import com.hfg.sql.jdbc.JDBCResultSet;
016import com.hfg.sql.jdbc.SQLStatementOptions;
017import com.hfg.sql.table.DatabaseCol;
018import com.hfg.sql.table.DatabaseTable;
019import com.hfg.util.StackTraceElementFilter;
020import com.hfg.util.StackTraceUtil;
021import com.hfg.util.StringBuilderPlus;
022import com.hfg.util.StringUtil;
023import com.hfg.util.collection.CollectionUtil;
024
025//------------------------------------------------------------------------------
026/**
027 SQL utility functions.
028 <div>
029 @author J. Alex Taylor, hairyfatguy.com
030 </div>
031 */
032//------------------------------------------------------------------------------
033// com.hfg XML/HTML Coding Library
034//
035// This library is free software; you can redistribute it and/or
036// modify it under the terms of the GNU Lesser General Public
037// License as published by the Free Software Foundation; either
038// version 2.1 of the License, or (at your option) any later version.
039//
040// This library is distributed in the hope that it will be useful,
041// but WITHOUT ANY WARRANTY; without even the implied warranty of
042// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
043// Lesser General Public License for more details.
044//
045// You should have received a copy of the GNU Lesser General Public
046// License along with this library; if not, write to the Free Software
047// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
048//
049// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
050// jataylor@hairyfatguy.com
051//------------------------------------------------------------------------------
052
053public class SQLUtil
054{
055   public static Level sSQLLoggingLevel = Level.FINE;
056
057   private final static Logger LOGGER = Logger.getLogger(SQLUtil.class.getName());
058
059   private static List<StackTraceElementFilter> sExclusions = new ArrayList<>(4);
060
061   static
062   {
063      LOGGER.setUseParentHandlers(false);
064
065      sExclusions.add(new StackTraceElementFilter().setPackage(SQLUtil.class.getPackage()));
066      sExclusions.add(new StackTraceElementFilter().setPackage(DatabaseTable.class.getPackage()));
067   }
068
069   //---------------------------------------------------------------------------
070   public static Logger getLogger()
071   {
072      return LOGGER;
073   }
074
075   //---------------------------------------------------------------------------
076   public static String sqlString(Object inValue)
077   {
078      return inValue != null ? sqlString(inValue.toString()) : "null";
079   }
080
081   //---------------------------------------------------------------------------
082   public static String sqlString(String inValue)
083   {
084      return inValue != null ? "'" + StringUtil.replaceAll(inValue, "\'", "\'\'") + "'" : "null";
085   }
086
087   //---------------------------------------------------------------------------
088   /**
089    All single and double quotes internal to the string are doubled for escaping.
090    @param inValue the value string to be escaped for single and double quotes
091    @return the escaped value
092    */
093   public static String sqlStringWithDoubleQuotes(String inValue)
094   {
095      return inValue != null ? "\"" + StringUtil.replaceAll(StringUtil.replaceAll(inValue, "\"", "\"\""), "\'", "\'\'") + "\"" : "null";
096   }
097
098   //---------------------------------------------------------------------------
099   public static String sqlStringList(Collection<? extends Object> inValues)
100   {
101      String listString = "";
102
103      if (CollectionUtil.hasValues(inValues))
104      {
105         List<String> sqlStrings = new ArrayList<>(inValues.size());
106         for (Object object : inValues)
107         {
108            sqlStrings.add(sqlString(object));
109         }
110
111         listString = StringUtil.join(sqlStrings, ", ");
112      }
113
114      return listString;
115   }
116
117   //---------------------------------------------------------------------------
118   public static boolean execute(Connection inConn, String inSQL)
119         throws SQLException
120   {
121      boolean result;
122
123      Statement stmt = null;
124      try
125      {
126         stmt = inConn.createStatement();
127         result = execute(stmt, inSQL);
128      }
129      finally
130      {
131         SQLUtil.close(stmt);
132      }
133
134      return result;
135   }
136
137   //---------------------------------------------------------------------------
138   public static boolean execute(PreparedStatement inPreparedStatement)
139         throws JDBCException
140   {
141      return execute(inPreparedStatement, null);
142   }
143
144   //---------------------------------------------------------------------------
145   public static boolean execute(Statement inStatement, String inSQL)
146         throws JDBCException
147   {
148      boolean result;
149
150      long startTime = System.currentTimeMillis();
151
152      try
153      {
154         if (inStatement instanceof PreparedStatement)
155         {
156            PreparedStatement ps = (PreparedStatement) inStatement;
157            inSQL = ps.toString();
158            result = ((PreparedStatement) inStatement).execute();
159         }
160         else
161         {
162            result = inStatement.execute(inSQL);
163         }
164      }
165      catch (Exception e)
166      {
167         throw new JDBCException("Error executing SQL:\n" + inSQL, e);
168      }
169
170      if (LOGGER.isLoggable(sSQLLoggingLevel))
171      {
172         // Generate debugging info w/ the SQL run and its execution time
173         StringBuilderPlus msg = new StringBuilderPlus()
174               .appendln("[" + StackTraceUtil.getCallingStackTraceElement(sExclusions) + "] SQL:\n")
175               .appendln(inSQL)
176               .appendln("Execution Time: " + DateUtil.generateElapsedTimeString(startTime));
177         
178         LOGGER.log(sSQLLoggingLevel, msg.toString());
179      }
180
181      return result;
182   }
183
184   //---------------------------------------------------------------------------
185   public static int executeUpdate(Connection inConn, String inSQL)
186         throws SQLException
187   {
188      int rowsUpdated;
189
190      Statement stmt = null;
191      try
192      {
193         stmt = inConn.createStatement();
194         rowsUpdated = executeUpdate(stmt, inSQL);
195      }
196      finally
197      {
198         SQLUtil.close(stmt);
199      }
200
201      return rowsUpdated;
202   }
203
204   //---------------------------------------------------------------------------
205   public static int executeUpdate(PreparedStatement inPreparedStatement)
206         throws SQLException
207   {
208      return executeUpdate(inPreparedStatement, null);
209   }
210
211   //---------------------------------------------------------------------------
212   public static int executeUpdate(Statement inStatement, CharSequence inSQL)
213         throws SQLException
214   {
215      int result;
216
217      long startTime = System.currentTimeMillis();
218
219      try
220      {
221         if (inStatement instanceof PreparedStatement)
222         {
223            PreparedStatement ps = (PreparedStatement) inStatement;
224            inSQL = ps.toString();
225            result = ((PreparedStatement) inStatement).executeUpdate();
226         }
227         else
228         {
229            result = inStatement.executeUpdate(inSQL.toString());
230         }
231      }
232      catch (Exception e)
233      {
234         throw new JDBCException("Error executing SQL:\n" + inSQL, e);
235      }
236
237      if (LOGGER.isLoggable(sSQLLoggingLevel))
238      {
239         // Generate debugging info w/ the SQL run and its execution time
240         StringBuilderPlus msg = new StringBuilderPlus()
241               .appendln("[" + StackTraceUtil.getCallingStackTraceElement(sExclusions) + "] SQL:")
242               .appendln(inSQL)
243               .appendln("Rows " + (isDelete(inSQL) ? "Deleted" : "Updated") + ": " + result)
244               .appendln("Execution Time: " + DateUtil.generateElapsedTimeString(startTime));
245
246         LOGGER.log(sSQLLoggingLevel, msg.toString());
247      }
248
249      return result;
250   }
251
252   //---------------------------------------------------------------------------
253   public static ResultSet executeQuery(Connection inConn, String inSQL)
254         throws SQLException
255   {
256      return executeQuery(inConn, inSQL, null);
257   }
258
259   //---------------------------------------------------------------------------
260   public static ResultSet executeQuery(Connection inConn, String inSQL, SQLStatementOptions inOptions)
261         throws SQLException
262   {
263      Statement stmt = null;
264      ResultSet rs;
265
266      try
267      {
268         stmt = inConn.createStatement();
269
270         if (inOptions != null)
271         {
272            inOptions.configureStmt(stmt);
273         }
274
275         rs = executeQuery(stmt, inSQL);
276      }
277      catch (SQLException e)
278      {
279         close(stmt);
280         throw e;
281      }
282
283      return rs;
284   }
285
286   //---------------------------------------------------------------------------
287   public static int executeQuery(PreparedStatement inPreparedStatement)
288         throws SQLException
289   {
290      return executeUpdate(inPreparedStatement, null);
291   }
292
293   //---------------------------------------------------------------------------
294   public static ResultSet executeQuery(Statement inStatement, String inSQL)
295         throws JDBCException
296   {
297      ResultSet rs;
298
299      long startTime = System.currentTimeMillis();
300
301      try
302      {
303         if (inStatement instanceof PreparedStatement)
304         {
305            PreparedStatement ps = (PreparedStatement) inStatement;
306            inSQL = ps.toString();
307            rs = ((PreparedStatement) inStatement).executeQuery();
308         }
309         else
310         {
311            rs = inStatement.executeQuery(inSQL);
312         }
313      }
314      catch (Exception e)
315      {
316         throw new JDBCException("Error executing SQL:\n" + inSQL, e);
317      }
318
319      if (LOGGER.isLoggable(sSQLLoggingLevel))
320      {
321         // Generate debugging info w/ the SQL run and its execution time
322         StringBuilderPlus msg = new StringBuilderPlus()
323               .appendln("[" + StackTraceUtil.getCallingStackTraceElement(sExclusions) + "] SQL:\n")
324               .appendln(inSQL)
325               .appendln("Execution Time: " + DateUtil.generateElapsedTimeString(startTime));
326
327         LOGGER.log(sSQLLoggingLevel, msg.toString());
328      }
329
330      return new JDBCResultSet(rs);
331   }
332
333   //---------------------------------------------------------------------------
334   public static void closeResultSetAndStatement(ResultSet inResultSet)
335      throws JDBCException
336   {
337      try
338      {
339         if (inResultSet != null)
340         {
341            Statement stmt = inResultSet.getStatement();
342            stmt.close();
343            inResultSet.close();
344         }
345      } catch (SQLException e)
346      {
347         throw new JDBCException(e);
348      }
349   }
350
351   //---------------------------------------------------------------------------
352   public static void close(ResultSet inResultSet)
353      throws JDBCException
354   {
355      try
356      {
357         if (inResultSet != null)
358         {
359            inResultSet.close();
360         }
361      } catch (SQLException e)
362      {
363         throw new JDBCException(e);
364      }
365   }
366
367   //---------------------------------------------------------------------------
368   public static void close(Statement inStmt)
369      throws JDBCException
370   {
371      try
372      {
373         if (inStmt != null)
374         {
375            inStmt.close();
376         }
377      }
378      catch (SQLException e)
379      {
380         throw new JDBCException(e);
381      }
382   }
383
384   //---------------------------------------------------------------------------
385   public static void close(Connection inConn)
386      throws JDBCException
387   {
388      try
389      {
390         if (inConn != null)
391         {
392            inConn.close();
393         }
394      }
395      catch (SQLException e)
396      {
397         throw new JDBCException(e);
398      }
399   }
400
401   //---------------------------------------------------------------------------
402   public static List<String> splitBatchSQL(Reader inSQL)
403      throws IOException
404   {
405      List<String> sqlCommands = new ArrayList<String>();
406
407      BufferedReader bufferedReader;
408      if (inSQL instanceof BufferedReader)
409      {
410         bufferedReader = (BufferedReader) inSQL;
411      }
412      else
413      {
414         bufferedReader = new BufferedReader(inSQL);
415      }
416
417      StringBuilderPlus currentSQLCommand = new StringBuilderPlus();
418
419      // In-line SQL comments start with two dashes and continue to the end of the line. Ex: '-- my comment'
420      // Block SQL comments look like block comments in C or Java. Ex: '/* my comment */'
421      boolean inInlineComment = false;
422      boolean inBlockComment  = false;
423      boolean inQuotedContent = false;
424
425      String line;
426      while ((line = bufferedReader.readLine()) != null)
427      {
428         inInlineComment = false;
429         char[] lineChars = line.toCharArray();
430
431         for (int i = 0; i < line.length(); i++)
432         {
433            char theChar = lineChars[i];
434            if (theChar == ';'
435                && ! inInlineComment
436                && ! inBlockComment
437                && ! inQuotedContent)
438            {
439               String sql = currentSQLCommand.toString().trim();
440               if (StringUtil.isSet(sql))
441               {
442                  sqlCommands.add(sql);
443               }
444
445               currentSQLCommand.setLength(0);
446            }
447            else
448            {
449               currentSQLCommand.append(theChar);
450
451               // Is this the beginning of an inline comment?
452               if (theChar == ' '
453                   && i > 1
454                   && lineChars[i - 1] == '-'
455                   && lineChars[i - 2] == '-')
456               {
457                  inInlineComment = true;
458               }
459               else if (inBlockComment   // Is it the end of a block comment?
460                        && theChar == '/'
461                        && i > 0
462                        && lineChars[i - 1] == '*')
463               {
464                  inBlockComment = false;
465               }
466               else if (! inBlockComment   // Is it the beginning of a block comment?
467                        && theChar == '*'
468                        && i > 0
469                        && lineChars[i - 1] == '/')
470               {
471                  inBlockComment = true;
472               }
473               else if (inQuotedContent   // Is it end of some quoted content?
474                        && theChar == '\''
475                        && (0 == i || lineChars[i - 1] != '\\'))
476               {
477                  inQuotedContent = false;
478               }
479               else if (! inQuotedContent   // Is it start of some quoted content?
480                        && theChar == '\''
481                        && (0 == i || lineChars[i - 1] != '\\'))
482               {
483                  inQuotedContent = true;
484               }
485            }
486         }
487
488         currentSQLCommand.appendln();
489      }
490
491      // Process remaining content
492      String sql = currentSQLCommand.toString().trim();
493      if (StringUtil.isSet(sql))
494      {
495         sqlCommands.add(sql);
496      }
497
498      bufferedReader.close();
499
500      return sqlCommands;
501   }
502
503
504   //---------------------------------------------------------------------------
505   public static Integer getInteger(ResultSet inResultSet, String inColumnLabel)
506      throws SQLException
507   {
508      Integer value = inResultSet.getInt(inColumnLabel);
509      return (inResultSet.wasNull() ? null : value);
510   }
511
512   //---------------------------------------------------------------------------
513   public static Integer getInteger(ResultSet inResultSet, DatabaseCol inColumn)
514      throws SQLException
515   {
516      Integer value = inResultSet.getInt(inColumn.name());
517      return (inResultSet.wasNull() ? null : value);
518   }
519
520   //---------------------------------------------------------------------------
521   public static Integer getInteger(ResultSet inResultSet, int inColumnIndex)
522      throws SQLException
523   {
524      Integer value = inResultSet.getInt(inColumnIndex);
525      return (inResultSet.wasNull() ? null : value);
526   }
527
528
529   //---------------------------------------------------------------------------
530   public static Long getLong(ResultSet inResultSet, String inColumnLabel)
531      throws SQLException
532   {
533      Long value = inResultSet.getLong(inColumnLabel);
534      return (inResultSet.wasNull() ? null : value);
535   }
536
537   //---------------------------------------------------------------------------
538   public static Long getLong(ResultSet inResultSet, DatabaseCol inColumn)
539      throws SQLException
540   {
541      Long value = inResultSet.getLong(inColumn.name());
542      return (inResultSet.wasNull() ? null : value);
543   }
544
545   //---------------------------------------------------------------------------
546   public static Long getLong(ResultSet inResultSet, int inColumnIndex)
547      throws SQLException
548   {
549      Long value = inResultSet.getLong(inColumnIndex);
550      return (inResultSet.wasNull() ? null : value);
551   }
552
553
554   //---------------------------------------------------------------------------
555   public static Float getFloat(ResultSet inResultSet, String inColumnLabel)
556      throws SQLException
557   {
558      Float value = inResultSet.getFloat(inColumnLabel);
559      return (inResultSet.wasNull() ? null : value);
560   }
561
562   //---------------------------------------------------------------------------
563   public static Float getFloat(ResultSet inResultSet, DatabaseCol inColumn)
564      throws SQLException
565   {
566      Float value = inResultSet.getFloat(inColumn.name());
567      return (inResultSet.wasNull() ? null : value);
568   }
569
570   //---------------------------------------------------------------------------
571   public static Float getFloat(ResultSet inResultSet, int inColumnIndex)
572      throws SQLException
573   {
574      Float value = inResultSet.getFloat(inColumnIndex);
575      return (inResultSet.wasNull() ? null : value);
576   }
577
578
579   //---------------------------------------------------------------------------
580   public static Double getDouble(ResultSet inResultSet, String inColumnLabel)
581      throws SQLException
582   {
583      Double value = inResultSet.getDouble(inColumnLabel);
584      return (inResultSet.wasNull() ? null : value);
585   }
586
587   //---------------------------------------------------------------------------
588   public static Double getDouble(ResultSet inResultSet, DatabaseCol inColumn)
589      throws SQLException
590   {
591      Double value = inResultSet.getDouble(inColumn.name());
592      return (inResultSet.wasNull() ? null : value);
593   }
594
595   //---------------------------------------------------------------------------
596   public static Double getDouble(ResultSet inResultSet, int inColumnIndex)
597      throws SQLException
598   {
599      Double value = inResultSet.getDouble(inColumnIndex);
600      return (inResultSet.wasNull() ? null : value);
601   }
602
603   //---------------------------------------------------------------------------
604   /**
605    A null-tolerant wrapper for PreparedStatement's setString().
606    @param inPreparedStatement the PreparedStatement in which to set the value
607    @param inIndex the parameter index where the value should be placed
608    @param inValue the value to insert into the PreparedStatement
609    @throws SQLException
610    */
611   public static void setString(PreparedStatement inPreparedStatement, int inIndex, String inValue)
612         throws SQLException
613   {
614      if (inValue != null)
615      {
616         inPreparedStatement.setString(inIndex, inValue);
617      }
618      else
619      {
620         inPreparedStatement.setNull(inIndex, Types.VARCHAR);
621      }
622   }
623
624   //---------------------------------------------------------------------------
625   /**
626    A null-tolerant wrapper for PreparedStatement's setFloat().
627    @param inPreparedStatement the PreparedStatement in which to set the value
628    @param inIndex the parameter index where the value should be placed
629    @param inValue the value to insert into the PreparedStatement
630    @throws SQLException
631    */
632   public static void setFloat(PreparedStatement inPreparedStatement, int inIndex, Float inValue)
633         throws SQLException
634   {
635      if (inValue != null)
636      {
637         inPreparedStatement.setFloat(inIndex, inValue);
638      }
639      else
640      {
641         inPreparedStatement.setNull(inIndex, Types.FLOAT);
642      }
643   }
644
645   //---------------------------------------------------------------------------
646   /**
647    A null-tolerant wrapper for PreparedStatement's setDouble().
648    @param inPreparedStatement the PreparedStatement in which to set the value
649    @param inIndex the parameter index where the value should be placed
650    @param inValue the value to insert into the PreparedStatement
651    @throws SQLException
652    */
653   public static void setDouble(PreparedStatement inPreparedStatement, int inIndex, Double inValue)
654         throws SQLException
655   {
656      if (inValue != null)
657      {
658         inPreparedStatement.setDouble(inIndex, inValue);
659      }
660      else
661      {
662         inPreparedStatement.setNull(inIndex, Types.FLOAT);
663      }
664   }
665
666   //---------------------------------------------------------------------------
667   /**
668    A null-tolerant wrapper for PreparedStatement's setInt().
669    @param inPreparedStatement the PreparedStatement in which to set the value
670    @param inIndex the parameter index where the value should be placed
671    @param inValue the value to insert into the PreparedStatement
672    @throws SQLException
673    */
674   public static void setInt(PreparedStatement inPreparedStatement, int inIndex, Integer inValue)
675         throws SQLException
676   {
677      if (inValue != null)
678      {
679         inPreparedStatement.setInt(inIndex, inValue);
680      }
681      else
682      {
683         inPreparedStatement.setNull(inIndex, Types.BIGINT);
684      }
685   }
686
687   //---------------------------------------------------------------------------
688   /**
689    A null-tolerant wrapper for PreparedStatement's setShort().
690    @param inPreparedStatement the PreparedStatement in which to set the value
691    @param inIndex the parameter index where the value should be placed
692    @param inValue the value to insert into the PreparedStatement
693    @throws SQLException
694    */
695   public static void setShort(PreparedStatement inPreparedStatement, int inIndex, Short inValue)
696         throws SQLException
697   {
698      if (inValue != null)
699      {
700         inPreparedStatement.setShort(inIndex, inValue);
701      }
702      else
703      {
704         inPreparedStatement.setNull(inIndex, Types.SMALLINT);
705      }
706   }
707
708   //---------------------------------------------------------------------------
709   /**
710    A null-tolerant wrapper for PreparedStatement's setLong().
711    @param inPreparedStatement the PreparedStatement in which to set the value
712    @param inIndex the parameter index where the value should be placed
713    @param inValue the value to insert into the PreparedStatement
714    @throws SQLException
715    */
716   public static void setLong(PreparedStatement inPreparedStatement, int inIndex, Long inValue)
717         throws SQLException
718   {
719      if (inValue != null)
720      {
721         inPreparedStatement.setLong(inIndex, inValue);
722      }
723      else
724      {
725         inPreparedStatement.setNull(inIndex, Types.BIGINT);
726      }
727   }
728
729   //---------------------------------------------------------------------------
730   /**
731    A null-tolerant wrapper for PreparedStatement's setDate().
732    @param inPreparedStatement the PreparedStatement in which to set the value
733    @param inIndex the parameter index where the value should be placed
734    @param inValue the value to insert into the PreparedStatement
735    @throws SQLException
736    */
737   public static void setDate(PreparedStatement inPreparedStatement, int inIndex, java.util.Date inValue)
738         throws SQLException
739   {
740      if (inValue != null)
741      {
742         inPreparedStatement.setDate(inIndex, new Date(inValue.getTime()));
743      }
744      else
745      {
746         inPreparedStatement.setNull(inIndex, Types.BIGINT);
747      }
748   }
749
750   //---------------------------------------------------------------------------
751   /**
752    A null-tolerant wrapper for PreparedStatement's setDate().
753    @param inPreparedStatement the PreparedStatement in which to set the value
754    @param inIndex the parameter index where the value should be placed
755    @param inValue the value to insert into the PreparedStatement
756    @throws SQLException
757    */
758   public static void setDate(PreparedStatement inPreparedStatement, int inIndex, Date inValue)
759         throws SQLException
760   {
761      if (inValue != null)
762      {
763         inPreparedStatement.setDate(inIndex, inValue);
764      }
765      else
766      {
767         inPreparedStatement.setNull(inIndex, Types.BIGINT);
768      }
769   }
770
771   //---------------------------------------------------------------------------
772   /**
773    A null-tolerant wrapper for PreparedStatement's setTimestamp().
774    @param inPreparedStatement the PreparedStatement in which to set the value
775    @param inIndex the parameter index where the value should be placed
776    @param inValue the value to insert into the PreparedStatement
777    @throws SQLException
778    */
779   public static void setTimestamp(PreparedStatement inPreparedStatement, int inIndex, Timestamp inValue)
780         throws SQLException
781   {
782      if (inValue != null)
783      {
784         inPreparedStatement.setTimestamp(inIndex, inValue);
785      }
786      else
787      {
788         inPreparedStatement.setNull(inIndex, Types.BIGINT);
789      }
790   }
791
792
793   //---------------------------------------------------------------------------
794   /**
795    A convenience method for getting a count of rows that meet a specific criteria.
796    @param inConn the database connection
797    @param inTable the database table
798    @param inWhereClause the where clause to use
799    */
800   public static int getRowCount(Connection inConn, DatabaseTable inTable, WhereClause inWhereClause)
801         throws SQLException
802   {
803      List<SQLClause> clauses = new ArrayList<>(1);
804      clauses.add(inWhereClause);
805
806      return getRowCount(inConn, inTable, clauses);
807   }
808
809
810   //---------------------------------------------------------------------------
811   /**
812    A convenience method for getting a count of rows that meet a specific criteria.
813    @param inConn the database connection
814    @param inTable the database table
815    @param inClauses additional clauses to use
816    */
817   public static int getRowCount(Connection inConn, DatabaseTable inTable, Collection<SQLClause> inClauses)
818         throws SQLException
819   {
820      int count = 0;
821
822      SQLQuery query = new SQLQuery()
823            .addSelect("count('x')")
824            .addFrom(inTable);
825
826      if (CollectionUtil.hasValues(inClauses))
827      {
828         query.addClauses(inClauses);
829      }
830
831      ResultSet rs = null;
832      try
833      {
834         rs = query.execute(inConn);
835         if (rs.next())
836         {
837            count = rs.getInt(1);
838         }
839      }
840      finally
841      {
842         SQLUtil.close(rs);
843      }
844
845      return count;
846   }
847
848   //---------------------------------------------------------------------------
849   private static boolean isDelete(CharSequence inSQL)
850   {
851      boolean isDelete = false;
852      
853      String[] lines = StringUtil.lines(inSQL); 
854      for (String line : lines)
855      {
856         line = line.trim();
857         if (! line.startsWith("-- ")) // Skip any comment lines
858         {
859            isDelete = line.toUpperCase().startsWith("DELETE");
860            break;
861         }
862      }
863      
864      return isDelete;
865   }
866}