KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > jdbc > SQLQuery


1 /**
2  * com.mckoi.database.jdbc.SQLQuery 20 Jul 2000
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database.jdbc;
26
27 import java.io.*;
28 import java.sql.SQLException JavaDoc;
29 import com.mckoi.database.global.ObjectTransfer;
30 import com.mckoi.database.global.ObjectTranslator;
31 import com.mckoi.database.global.ByteLongObject;
32
33 /**
34  * Represents an SQL Query to the database. This includes the query string
35  * itself plus any data types that are part of the query.
36  * <p>
37  * FUTURE ENHANCEMENTS: This could do some preliminary parsing of the query
38  * string for faster translation by the database.
39  *
40  * @author Tobias Downer
41  */

42
43 public final class SQLQuery {
44
45   /**
46    * The SQL String. For example, "select * from Part".
47    */

48   private String JavaDoc query;
49
50   /**
51    * Set to true when this query is prepared via the prepare method.
52    */

53   private boolean prepared;
54
55   /**
56    * The list of all variable substitutions that are in the query. A
57    * variable substitution is set up in a prepared statement.
58    */

59   private Object JavaDoc[] parameters;
60   private int parameters_index;
61   private int parameter_count;
62
63
64   /**
65    * Empty constructor.
66    */

67   private SQLQuery() {
68   }
69
70   /**
71    * Constructs the query.
72    */

73   public SQLQuery(String JavaDoc query) {
74     this.query = query;
75     parameters = new Object JavaDoc[8];
76     parameters_index = 0;
77     parameter_count = 0;
78     prepared = false;
79   }
80
81   /**
82    * Grows the parameters list to the given size.
83    */

84   private void growParametersList(int new_size) {
85     // Make new list
86
Object JavaDoc[] new_list = new Object JavaDoc[new_size];
87     // Copy everything to new list
88
System.arraycopy(parameters, 0, new_list, 0, parameters.length);
89     // Set the new list.
90
parameters = new_list;
91   }
92
93   /**
94    * Translates the given object to a type the object can process.
95    */

96   private Object JavaDoc translateObjectType(Object JavaDoc ob) {
97     return ObjectTranslator.translate(ob);
98   }
99
100   /**
101    * Adds a variable to the query. If the object is not a type that is
102    * a database 'primitive' type (BigDecimal, ByteLongObject, Boolean,
103    * Date, String) then it is serialized and the serialized form is wrapped
104    * in a ByteLongObject.
105    */

106   public void addVar(Object JavaDoc ob) {
107     ob = translateObjectType(ob);
108     parameters[parameters_index] = ob;
109     ++parameters_index;
110     ++parameter_count;
111     if (parameters_index >= parameters.length) {
112       growParametersList(parameters_index + 8);
113     }
114   }
115
116   /**
117    * Sets a variable at the given index. Grows if necessary. If the object is
118    * not a type that is a database 'primitive' type (BigDecimal,
119    * ByteLongObject, Boolean, Date, String) then it is serialized and the
120    * serialized form is wrapped in a ByteLongObject.
121    */

122   public void setVar(int i, Object JavaDoc ob) {
123     ob = translateObjectType(ob);
124     if (i >= parameters.length) {
125       growParametersList(i + 8);
126     }
127     parameters[i] = ob;
128     parameters_index = i + 1;
129     parameter_count = Math.max(parameters_index, parameter_count);
130   }
131
132   /**
133    * Clears all the parameters.
134    */

135   public void clear() {
136     parameters_index = 0;
137     parameter_count = 0;
138     for (int i = 0; i < parameters.length; ++i) {
139       parameters[i] = null;
140     }
141   }
142
143
144   /**
145    * Returns the query string.
146    */

147   public String JavaDoc getQuery() {
148     return query;
149   }
150
151   /**
152    * Returns the array of all objects that are to be used as substitutions
153    * for '?' in the query.
154    * <p>
155    * NOTE: Array returned references internal Object[] here so don't change!
156    */

157   public Object JavaDoc[] getVars() {
158     return parameters;
159   }
160
161   /**
162    * Given a JDBC escape code of the form {keyword ... parameters ...} this
163    * will return the most optimal Mckoi SQL query for the code.
164    */

165   private String JavaDoc escapeJDBCSubstitution(String JavaDoc jdbc_code)
166                                                        throws SQLException JavaDoc {
167     String JavaDoc code = jdbc_code.substring(1, jdbc_code.length() - 1);
168     int kp_delim = code.indexOf(' ');
169     if (kp_delim != -1) {
170       String JavaDoc keyword = code.substring(0, kp_delim);
171       String JavaDoc body = code.substring(kp_delim).trim();
172
173       if (keyword.equals("d")) { // Process a date
174
return "DATE " + body;
175       }
176       if (keyword.equals("t")) { // Process a time
177
return "TIME " + body;
178       }
179       if (keyword.equals("ts")) { // Process a timestamp
180
return "TIMESTAMP " + body;
181       }
182       if (keyword.equals("fn")) { // A function
183
return body;
184       }
185       if (keyword.equals("call") || keyword.equals("?=")) {
186         throw new MSQLException("Stored procedures not supported.");
187       }
188       if (keyword.equals("oj")) { // Outer join
189
return body;
190       }
191
192       throw new MSQLException("Do not understand JDBC substitution keyword '" +
193                               keyword + "' of " + jdbc_code);
194     }
195     else {
196       throw new MSQLException("Malformed JDBC escape code: " + jdbc_code);
197     }
198
199   }
200
201   /**
202    * Performs any JDBC escape processing on the query. For example, the
203    * code {d 'yyyy-mm-dd'} is converted to 'DATE 'yyyy-mm-dd'.
204    */

205   private void doEscapeSubstitutions() throws SQLException JavaDoc {
206     // This is a fast but primitive parser that scans the SQL string and
207
// substitutes any {[code] ... } type escape sequences to the Mckoi
208
// equivalent. This will not make substitutions of anything inside a
209
// quoted area of the query.
210

211     // Exit early if no sign of an escape code
212
if (query.indexOf('{') == -1) {
213       return;
214     }
215
216     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
217     StringBuffer JavaDoc jdbc_escape = null;
218
219     int i = 0;
220     int sz = query.length();
221     int state = 0;
222     boolean ignore_next = false;
223
224     while (i < sz) {
225       char c = query.charAt(i);
226
227       if (state == 0) { // If currently processing SQL code
228
if (c == '\'' || c == '\"') {
229           state = c; // Set state to quote
230
}
231         else if (c == '{') {
232           jdbc_escape = new StringBuffer JavaDoc();
233           state = '}';
234         }
235       }
236       else if (state != 0) { // If currently inside a quote or escape
237
if (!ignore_next) {
238           if (c == '\\') {
239             ignore_next = true;
240           }
241           else {
242             // If at the end of a quoted area
243
if (c == (char) state) {
244               state = 0;
245               if (c == '}') {
246                 jdbc_escape.append('}');
247                 buf.append(escapeJDBCSubstitution(new String JavaDoc(jdbc_escape)));
248                 jdbc_escape = null;
249                 c = ' ';
250               }
251             }
252           }
253         }
254         else {
255           ignore_next = false;
256         }
257       }
258
259       if (state != '}') {
260         // Copy the character
261
buf.append(c);
262       }
263       else {
264         jdbc_escape.append(c);
265       }
266
267       ++i;
268     }
269
270     if (state == '}') {
271       throw new SQLException JavaDoc("Unterminated JDBC escape code in query: " +
272                              new String JavaDoc(jdbc_escape));
273     }
274
275     query = new String JavaDoc(buf);
276   }
277
278   /**
279    * Prepares the query by parsing the query string and performing any updates
280    * that are required before being passed down to the lower layers of the
281    * database engine for processing. For example, JDBC escape code processing.
282    */

283   public void prepare(boolean do_escape_processing) throws SQLException JavaDoc {
284     if (do_escape_processing) {
285       doEscapeSubstitutions();
286     }
287     prepared = true;
288   }
289
290   /**
291    * Returns true if this query is equal to another.
292    */

293   public boolean equals(Object JavaDoc ob) {
294     SQLQuery q2 = (SQLQuery) ob;
295     // NOTE: This could do syntax analysis on the query string to determine
296
// if it's the same or not.
297
if (query.equals(q2.query)) {
298       if (parameter_count == q2.parameter_count) {
299         for (int i = 0; i < parameter_count; ++i) {
300           if (parameters[i] != q2.parameters[i]) {
301             return false;
302           }
303         }
304         return true;
305       }
306     }
307     return false;
308   }
309
310   /**
311    * Creates an exact copy of this object.
312    */

313   public SQLQuery copy() {
314     SQLQuery q = new SQLQuery();
315     q.query = query;
316     q.parameters = (Object JavaDoc[]) parameters.clone();
317     q.parameters_index = parameters_index;
318     q.parameter_count = parameter_count;
319     q.prepared = prepared;
320     return q;
321   }
322
323   /**
324    * Outputs the query as text (for debugging)
325    */

326   public String JavaDoc toString() {
327     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
328     buf.append("[ Query:\n[ ");
329     buf.append(getQuery());
330     buf.append(" ]\n");
331     if (parameter_count > 0) {
332       buf.append("\nParams:\n[ ");
333       for (int i = 0; i < parameter_count; ++i) {
334         Object JavaDoc ob = parameters[i];
335         if (ob == null) {
336           buf.append("NULL");
337         }
338         else {
339           buf.append(parameters[i].toString());
340         }
341         buf.append(", ");
342       }
343       buf.append(" ]");
344     }
345     buf.append("\n]");
346     return new String JavaDoc(buf);
347   }
348
349   // ---------- Stream transfer methods ----------
350

351   /**
352    * Writes the SQL query to the data output stream.
353    */

354   public void writeTo(DataOutputStream out) throws IOException {
355     out.writeUTF(query);
356     out.writeInt(parameter_count);
357     for (int i = 0; i < parameter_count; ++i) {
358       ObjectTransfer.writeTo(out, parameters[i]);
359     }
360   }
361
362   /**
363    * Reads an SQLQuery object from the data input stream.
364    */

365   public static SQLQuery readFrom(DataInputStream in) throws IOException {
366     String JavaDoc query_string = in.readUTF();
367     SQLQuery query = new SQLQuery(query_string);
368     int arg_length = in.readInt();
369     for (int i = 0; i < arg_length; ++i) {
370       query.addVar(ObjectTransfer.readFrom(in));
371     }
372     return query;
373   }
374
375   /**
376    * Serializes an SQLQuery object to a ByteLongObject.
377    */

378   public ByteLongObject serializeToBlob() {
379     
380     ByteArrayOutputStream bout = new ByteArrayOutputStream();
381     DataOutputStream out = new DataOutputStream(bout);
382     try {
383       writeTo(out);
384       out.flush();
385       return new ByteLongObject(bout.toByteArray());
386     }
387     catch (IOException e) {
388       throw new Error JavaDoc("IO Error: " + e.getMessage());
389     }
390   }
391   
392   /**
393    * Deserializes an SQLQuery object from a ByteLongObject.
394    */

395   public static SQLQuery deserializeFromBlob(ByteLongObject ob) {
396     DataInputStream in = new DataInputStream(
397                                 new ByteArrayInputStream(ob.getByteArray()));
398     try {
399       return readFrom(in);
400     }
401     catch (IOException e) {
402       throw new Error JavaDoc("IO Error: " + e.getMessage());
403     }
404   }
405   
406   
407 }
408
Popular Tags