View Javadoc
1   /*
2    * Copyright 2008 FatWire Corporation. All Rights Reserved.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.fatwire.gst.foundation.facade.sql;
18  
19  import java.math.BigDecimal;
20  import java.sql.Array;
21  import java.sql.Blob;
22  import java.sql.Clob;
23  import java.sql.Date;
24  import java.sql.Ref;
25  import java.sql.Struct;
26  import java.sql.Time;
27  import java.sql.Timestamp;
28  import java.sql.Types;
29  import java.util.Collections;
30  import java.util.Iterator;
31  
32  import COM.FutureTense.Interfaces.ICS;
33  import COM.FutureTense.Interfaces.IList;
34  
35  import com.fatwire.cs.core.db.PreparedStmt;
36  import com.fatwire.cs.core.db.StatementParam;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  /**
42   * A helper class over <tt>ICS.SQL</tt>
43   * 
44   * @author Dolf Dijkstra
45   * @see ICS#SQL(String, String, String, int, boolean, boolean, StringBuffer)
46   */
47  public class SqlHelper {
48  
49      private static final Log LOG = LogFactory.getLog(SqlHelper.class.getPackage().getName());
50  
51      private SqlHelper() {
52      }
53  
54      /**
55       * facade over ICS.SQL
56       * <p/>
57       * limit =-1;
58       * <p/>
59       * bCache=true;
60       * <p/>
61       * clears errno before ics.SQL
62       * <p/>
63       * no IList registered in ics variable space
64       * 
65       * @param ics
66       * @param table tablename
67       * @param sql the sql statement, needs to start with 'select'
68       * @return never null, always an IListIterable
69       * @throws RuntimeException if errno is not zero or not -101
70       * @see SqlHelper#select(ICS, String, String, int)
71       */
72  
73      public static final IListIterable select(final ICS ics, final String table, final String sql) {
74          return select(ics, table, sql, -1);
75      }
76  
77      /**
78       * Executes an ICS.SQL operation with a limit.
79       * 
80       * @param ics
81       * @param table tablename
82       * @param sql the sql statement, needs to start with 'select'
83       * @param limit maximum number of rows to return
84       * @return never null, always an IListIterable
85       * @see ICS#SQL(String, String, String, int, boolean, StringBuffer)
86       */
87      public static final IListIterable select(final ICS ics, final String table, final String sql, final int limit) {
88          final StringBuffer errstr = new StringBuffer();
89          ics.ClearErrno();
90          if (sql == null) {
91              throw new NullPointerException("sql can not be null");
92          }
93          if (!sql.toLowerCase().trim().startsWith("select")) {
94              throw new IllegalArgumentException("Can only do select statements:" + sql);
95          }
96  
97          final IList i = ics.SQL(table, sql, null, limit, true, errstr);
98          if (ics.GetErrno() == -101) {
99              ics.ClearErrno();
100         } else if (ics.GetErrno() != 0) {
101             throw new RuntimeException("ics.SQL returned " + ics.GetErrno() + " and errstr: '" + errstr.toString()
102                     + "' for " + sql);
103         }
104 
105         return new IListIterable(i);
106     }
107 
108     /**
109      * Executes sql statements, other then SELECT statements.
110      * <p/>
111      * flushes the table (ics.FlushCatalog()) after the statement execution
112      * 
113      * @param ics
114      * @param table tablename
115      * @param sql the sql statement, can not start with "select"
116      */
117     public static final void execute(final ICS ics, final String table, final String sql) {
118         final StringBuffer errstr = new StringBuffer();
119         ics.ClearErrno();
120         if (sql == null) {
121             throw new NullPointerException("sql can not be null");
122         }
123         if (sql.toLowerCase().trim().startsWith("select")) {
124             throw new IllegalArgumentException("Can not do select statements:" + sql);
125         }
126 
127         ics.SQL(table, sql, null, -1, false, true, errstr);
128         if (ics.GetErrno() == 0) {
129             if (ics.FlushCatalog(table)) {
130                 ics.ClearErrno();
131             } else {
132                 LOG.warn("Flushing failed for table " + table + ". (" + ics.GetErrno() + ")");
133                 ics.ClearErrno();
134             }
135         } else if (ics.GetErrno() == -502) { // update statements do not
136             // return an IList, cs signals
137             // this via errno -502
138             if (ics.FlushCatalog(table)) {
139                 ics.ClearErrno();
140             } else {
141                 // throw exception??
142                 LOG.warn("Flushing failed for table " + table + ". (" + ics.GetErrno() + ")");
143                 ics.ClearErrno();
144             }
145         } else {
146             LOG.warn("ics.SQL returned " + ics.GetErrno() + " and errstr: " + errstr.toString() + " for " + sql);
147         }
148     }
149 
150     /**
151      * Executes a PreparedStatement
152      * 
153      * @param ics
154      * @param stmt the PreparedStatement
155      * @param param the statement parameters
156      * @return never null, always an IListIterable
157      */
158 
159     public static final IListIterable select(final ICS ics, final PreparedStmt stmt, final StatementParam param) {
160 
161         final IList i = ics.SQL(stmt, param, true);
162         if (ics.GetErrno() != -101) { // no rows if fine
163             ics.ClearErrno();
164         } else if (ics.GetErrno() != 0) {
165             throw new RuntimeException("ics.SQL returned " + ics.GetErrno() + " and errstr: " + " for "
166                     + stmt.toString());
167         }
168 
169         return new IListIterable(i);
170 
171     }
172 
173     /**
174      * Executes a PreparedStatement in a simple form. The values are simply
175      * mapped based on order and type to prepared statement parameters.
176      * 
177      * @param ics
178      * @param table tablename
179      * @param sql the sql statement
180      * @param value the values for the prepared statement parameters.
181      * @return never null, always an IListIterable
182      */
183 
184     public static final IListIterable selectSimplePrepared(final ICS ics, String table, String sql, Object... value) {
185 
186         final PreparedStmt stmt = new PreparedStmt(sql, Collections.singletonList(table));
187         for (int i = 0; value != null && i < value.length; i++) {
188             stmt.setElement(i, toJdbcType(value[i]));
189         }
190         final StatementParam param = stmt.newParam();
191         for (int i = 0; value != null && i < value.length; i++) {
192             Object o = value[i];
193 
194             if (o instanceof String) {
195                 param.setString(i, (String) o);
196             } else if (o instanceof BigDecimal) {
197                 param.setBigDecimal(i, (BigDecimal) o);
198             } else if (o instanceof Boolean) {
199                 param.setBoolean(i, (Boolean) o);
200             } else if (o instanceof Integer) {
201                 param.setInt(i, (Integer) o);
202             } else if (o instanceof Long) {
203                 param.setLong(i, (Long) o);
204             } else if (o instanceof Float) {
205                 param.setFloat(i, (Float) o);
206             } else if (o instanceof Double) {
207                 param.setDouble(i, (Double) o);
208             } else if (o instanceof Byte) {
209                 param.setByte(i, (Byte)o);
210             } else if (o instanceof java.sql.Date) {
211                 param.setDate(i, (Date) o);
212             } else if (o instanceof java.sql.Time) {
213                 param.setTime(i, (Time) o);
214             } else if (o instanceof java.sql.Timestamp) {
215                 param.setTimeStamp(i, (Timestamp) o);
216             } else if (o instanceof Clob) {
217                 throw new IllegalArgumentException("Can't search for " + o.getClass().getName());
218             } else if (o instanceof Blob) {
219                 throw new IllegalArgumentException("Can't search for " + o.getClass().getName());
220             }else if (o.getClass().isArray()){
221                 throw new IllegalArgumentException("Can't search for " + o.getClass().getName());
222             } else if (o instanceof Array) {
223                 throw new IllegalArgumentException("Can't search for " + o.getClass().getName());
224             } else if (o instanceof Struct) {
225                 throw new IllegalArgumentException("Can't search for " + o.getClass().getName());
226             } else if (o instanceof Ref) {
227                 throw new IllegalArgumentException("Can't search for " + o.getClass().getName());
228             } else if (o instanceof java.net.URL) {
229                 throw new IllegalArgumentException("Can't search for " + o.getClass().getName());
230             }
231         }
232         return select(ics, stmt, param);
233 
234     }
235 
236     private static int toJdbcType(Object o) {
237         if (o instanceof String) {
238             return Types.VARCHAR;
239         } else if (o instanceof java.math.BigDecimal) {
240             return Types.NUMERIC;
241         } else if (o instanceof Boolean) {
242             return Types.BOOLEAN;
243         } else if (o instanceof Integer) {
244             return Types.INTEGER;
245         } else if (o instanceof Long) {
246             return Types.BIGINT;
247         } else if (o instanceof Float) {
248             return Types.REAL;
249         } else if (o instanceof Double) {
250             return Types.DOUBLE;
251         } else if (o instanceof byte[]) {
252             return Types.LONGVARBINARY;
253         } else if (o instanceof java.sql.Date) {
254             return Types.DATE;
255         } else if (o instanceof java.sql.Time) {
256             return Types.TIME;
257         } else if (o instanceof java.sql.Timestamp) {
258             return Types.TIMESTAMP;
259         } else if (o instanceof Clob) {
260             return Types.CLOB;
261         } else if (o instanceof Blob) {
262             return Types.BLOB;
263         } else if (o instanceof Array) {
264             return Types.ARRAY;
265         } else if (o instanceof Struct) {
266             return Types.STRUCT;
267         } else if (o instanceof Ref) {
268             return Types.REF;
269         } else if (o instanceof java.net.URL) {
270             return Types.DATALINK;
271         } else {
272             throw new IllegalArgumentException(o == null ? "o must not be nulll" : "Can't handle type "
273                     + o.getClass().getName());
274         }
275 
276     }
277 
278     /**
279      * Executes a PreparedStatement, returning a single row
280      * 
281      * @param ics the Content Server context
282      * @param stmt the PreparedStatement
283      * @param param the statement parameters
284      * @return Row if resultset is returned, otherwise null
285      */
286 
287     public static final Row selectSingle(final ICS ics, final PreparedStmt stmt, final StatementParam param) {
288         ics.ClearErrno();
289         final IList i = ics.SQL(stmt, param, true);
290         if (ics.GetErrno() == 0) {
291             return new IListIterable(i).iterator().next();
292         } else if (ics.GetErrno() == -101) { // no rows is fine
293             ics.ClearErrno();
294             return null;
295         } else {
296             throw new RuntimeException("ics.SQL returned " + ics.GetErrno() + " for " + stmt.toString());
297         }
298 
299     }
300 
301     /**
302      * Executes an ICS.SQL operation, returning a single Row, or null if no
303      * result was returned by ICS.SQL.
304      * 
305      * @param ics
306      * @param table tablename
307      * @param sql the sql statement, needs to start with 'select'
308      * @return Row if resultset is returned, otherwise null
309      */
310     public static final Row selectSingle(final ICS ics, final String table, String sql) {
311 
312         Iterator<Row> i = select(ics, table, sql, 1).iterator();
313         return i.hasNext() ? i.next() : null;
314 
315     }
316 
317     /**
318      * Quote a string for use in a SQL statement.
319      * 
320      * @param s string to quote
321      * @return quoted string. Null strings are returned simply as ''.
322      */
323     public static final String quote(final String s) {
324         if (s == null || s.length() == 0) {
325             return "''";
326         }
327         return "'" + s.replace("'", "''") + "'";
328     }
329 
330     public static boolean tableExists(final ICS ics, final String table) {
331             ics.CatalogDef(table, null, new StringBuffer());
332             return ics.GetErrno() == 0;
333     }
334 
335 }