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