KickJava   Java API By Example, From Geeks To Geeks.

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


1 /**
2  * com.mckoi.database.jdbc.MStatement 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.sql.*;
28 import java.io.*;
29 import java.util.Vector JavaDoc;
30 import com.mckoi.database.global.StreamableObject;
31
32 /**
33  * An implementation of JDBC Statement.
34  * <p>
35  * Multi-threaded issue: This class is not designed to be multi-thread
36  * safe. A Statement should not be accessed by concurrent threads.
37  *
38  * @author Tobias Downer
39  */

40
41 class MStatement implements Statement {
42
43   /**
44    * The MConnection object for this statement.
45    */

46   private MConnection connection;
47
48   /**
49    * The list of all MResultSet objects that represents the results of a query.
50    */

51   private MResultSet[] result_set_list;
52
53
54   private int max_field_size;
55   private int max_row_count;
56   private int query_timeout;
57   private int fetch_size;
58
59   private SQLWarning head_warning;
60
61   private boolean escape_processing;
62
63   /**
64    * The list of queries to execute in a batch.
65    */

66   private Vector JavaDoc batch_list;
67
68   /**
69    * The list of streamable objects created via the 'createStreamableObject'
70    * method.
71    */

72   private Vector JavaDoc streamable_object_list;
73
74   /**
75    * For multiple result sets, the index of the result set we are currently on.
76    */

77   private int multi_result_set_index;
78
79   /**
80    * Constructs the statement.
81    */

82   MStatement(MConnection connection) {
83     this.connection = connection;
84     this.escape_processing = true;
85   }
86
87   /**
88    * Adds a new SQLWarning to the chain.
89    */

90   final void addSQLWarning(SQLWarning warning) {
91     if (head_warning == null) {
92       head_warning = warning;
93     }
94     else {
95       head_warning.setNextWarning(warning);
96     }
97   }
98
99   /**
100    * Returns an array of ResultSet objects of the give length for this
101    * statement. This is intended for multiple result queries (such as batch
102    * statements).
103    */

104   final MResultSet[] internalResultSetList(int count) {
105     if (count <= 0) {
106       throw new Error JavaDoc("'count' must be > 0");
107     }
108
109     if (result_set_list != null && result_set_list.length != count) {
110       // Dispose all the ResultSet objects currently open.
111
for (int i = 0; i < result_set_list.length; ++i) {
112         result_set_list[i].dispose();
113       }
114       result_set_list = null;
115     }
116
117     if (result_set_list == null) {
118       result_set_list = new MResultSet[count];
119       for (int i = 0; i < count; ++i) {
120         result_set_list[i] = new MResultSet(connection, this);
121       }
122     }
123
124     return result_set_list;
125   }
126   
127   /**
128    * Returns the single ResultSet object for this statement. This should only
129    * be used for single result queries.
130    */

131   final MResultSet internalResultSet() {
132     return internalResultSetList(1)[0];
133   }
134
135   /**
136    * Generates a new StreamableObject and stores it in the hold for future
137    * access by the server.
138    */

139   protected StreamableObject createStreamableObject(InputStream x,
140                                                     int length, byte type) {
141     StreamableObject s_ob = connection.createStreamableObject(x, length, type);
142     if (streamable_object_list == null) {
143       streamable_object_list = new Vector JavaDoc();
144     }
145     streamable_object_list.add(s_ob);
146     return s_ob;
147   }
148
149   /**
150    * Adds a query to the batch of queries executed by this statement.
151    */

152   protected void addBatch(SQLQuery query) {
153     if (batch_list == null) {
154       batch_list = new Vector JavaDoc();
155     }
156     batch_list.add(query);
157   }
158   
159   /**
160    * Executes the given SQLQuery object and fill's in at most the top 10
161    * entries of the result set.
162    */

163   protected MResultSet executeQuery(SQLQuery query) throws SQLException {
164     // Get the local result set
165
MResultSet result_set = internalResultSet();
166     // Execute the query
167
executeQueries(new SQLQuery[] { query });
168     // Return the result set
169
return result_set;
170   }
171
172   /**
173    * Executes a batch of SQL queries as listed as an array.
174    */

175   protected MResultSet[] executeQueries(SQLQuery[] queries)
176                                                           throws SQLException {
177
178     // Allocate the result set for this batch
179
MResultSet[] results = internalResultSetList(queries.length);
180
181     // Reset the result set index
182
multi_result_set_index = 0;
183
184     // For each query,
185
for (int i = 0; i < queries.length; ++i) {
186       // Prepare the query
187
queries[i].prepare(escape_processing);
188       // Make sure the result set is closed
189
results[i].closeCurrentResult();
190     }
191
192     // Execute each query
193
connection.executeQueries(queries, results);
194
195     // Post processing on the ResultSet objects
196
for (int i = 0; i < queries.length; ++i) {
197       MResultSet result_set = results[i];
198       // Set the fetch size
199
result_set.setFetchSize(fetch_size);
200       // Set the max row count
201
result_set.setMaxRowCount(max_row_count);
202       // Does the result set contain large objects? We can't cache a
203
// result that contains binary data.
204
boolean contains_large_objects = result_set.containsLargeObjects();
205       // If the result row count < 40 then download and store locally in the
206
// result set and dispose the resources on the server.
207
if (!contains_large_objects && result_set.rowCount() < 40) {
208         result_set.storeResultLocally();
209       }
210       else {
211         result_set.updateResultPart(0, Math.min(10, result_set.rowCount()));
212       }
213     }
214
215     return results;
216
217   }
218   
219   // ---------- Implemented from Statement ----------
220

221   public ResultSet executeQuery(String JavaDoc sql) throws SQLException {
222     return executeQuery(new SQLQuery(sql));
223   }
224
225   public int executeUpdate(String JavaDoc sql) throws SQLException {
226     MResultSet result_set = executeQuery(new SQLQuery(sql));
227     return result_set.intValue(); // Throws SQL error if not 1 col 1 row
228
}
229
230   public void close() throws SQLException {
231     // Behaviour of calls to Statement undefined after this method finishes.
232
if (result_set_list != null) {
233       for (int i = 0; i < result_set_list.length; ++i) {
234         result_set_list[i].dispose();
235       }
236       result_set_list = null;
237     }
238     // Remove any streamable objects that have been created on the client
239
// side.
240
if (streamable_object_list != null) {
241       int sz = streamable_object_list.size();
242       for (int i = 0; i < sz; ++i) {
243         StreamableObject s_object =
244                        (StreamableObject) streamable_object_list.elementAt(i);
245         connection.removeStreamableObject(s_object);
246       }
247       streamable_object_list = null;
248     }
249   }
250
251   //----------------------------------------------------------------------
252

253   public int getMaxFieldSize() throws SQLException {
254     // Are there limitations here? Strings can be any size...
255
return max_field_size;
256   }
257
258   public void setMaxFieldSize(int max) throws SQLException {
259     if (max >= 0) {
260       max_field_size = max;
261     }
262     else {
263       throw new SQLException("MaxFieldSize negative.");
264     }
265   }
266
267   public int getMaxRows() throws SQLException {
268     return max_row_count;
269   }
270
271   public void setMaxRows(int max) throws SQLException {
272     if (max >= 0) {
273       max_row_count = max;
274     }
275     else {
276       throw new SQLException("MaxRows negative.");
277     }
278   }
279
280   public void setEscapeProcessing(boolean enable) throws SQLException {
281     escape_processing = enable;
282   }
283
284   public int getQueryTimeout() throws SQLException {
285     return query_timeout;
286   }
287
288   public void setQueryTimeout(int seconds) throws SQLException {
289     if (seconds >= 0) {
290       query_timeout = seconds;
291       // Hack: We set the global query timeout for the driver in this VM.
292
// This global value is used in RemoteDatabaseInterface.
293
//
294
// This is a nasty 'global change' hack. A developer may wish to
295
// set a long timeout for one statement and a short for a different
296
// one however the timeout for all queries will be the very last time
297
// out set by any statement. Unfortunately to fix this problem, we'll
298
// need to make a revision to the DatabaseInterface interface. I
299
// don't think this is worth doing because I don't see this as being a
300
// major limitation of the driver.
301
MDriver.QUERY_TIMEOUT = seconds;
302     }
303     else {
304       throw new SQLException("Negative query timout.");
305     }
306   }
307
308   public void cancel() throws SQLException {
309     if (result_set_list != null) {
310       for (int i = 0; i < result_set_list.length; ++i) {
311         connection.disposeResult(result_set_list[i].getResultID());
312       }
313     }
314   }
315
316   public SQLWarning getWarnings() throws SQLException {
317     return head_warning;
318   }
319
320   public void clearWarnings() throws SQLException {
321     head_warning = null;
322   }
323
324   public void setCursorName(String JavaDoc name) throws SQLException {
325     // Cursors not supported...
326
}
327         
328   //----------------------- Multiple Results --------------------------
329

330   // NOTE: Mckoi database doesn't support multiple result sets. I think multi-
331
// result sets are pretty nasty anyway - are they really necessary?
332
// We do support the 'Multiple Results' interface for 1 result set.
333

334   public boolean execute(String JavaDoc sql) throws SQLException {
335     MResultSet result_set = executeQuery(new SQLQuery(sql));
336     return !result_set.isUpdate();
337   }
338         
339   public ResultSet getResultSet() throws SQLException {
340     if (result_set_list != null) {
341       if (multi_result_set_index < result_set_list.length) {
342         return result_set_list[multi_result_set_index];
343       }
344     }
345     return null;
346   }
347
348   public int getUpdateCount() throws SQLException {
349     if (result_set_list != null) {
350       if (multi_result_set_index < result_set_list.length) {
351         MResultSet rs = result_set_list[multi_result_set_index];
352         if (rs.isUpdate()) {
353           return rs.intValue();
354         }
355       }
356     }
357     return -1;
358   }
359
360   public boolean getMoreResults() throws SQLException {
361     // If we are at the end then return false
362
if (result_set_list == null ||
363         multi_result_set_index >= result_set_list.length) {
364       return false;
365     }
366
367     // Move to the next result set.
368
++multi_result_set_index;
369
370     // We successfully moved to the next result
371
return true;
372   }
373
374   //--------------------------JDBC 2.0-----------------------------
375

376   // NOTE: These methods are provided as extensions for the JDBC 1.0 driver.
377

378   public void setFetchSize(int rows) throws SQLException {
379     if (rows >= 0) {
380       fetch_size = rows;
381     }
382     else {
383       throw new SQLException("Negative fetch size.");
384     }
385   }
386
387   public int getFetchSize() throws SQLException {
388     return fetch_size;
389   }
390
391 //#IFDEF(JDBC2.0)
392

393   public void setFetchDirection(int direction) throws SQLException {
394     // We could use this hint to improve cache hits.....
395
}
396
397   public int getFetchDirection() throws SQLException {
398     return ResultSet.FETCH_UNKNOWN;
399   }
400
401   public int getResultSetConcurrency() throws SQLException {
402     // Read only I'm afraid...
403
return ResultSet.CONCUR_READ_ONLY;
404   }
405
406   public int getResultSetType() throws SQLException {
407     // Scroll insensitive operation only...
408
return ResultSet.TYPE_SCROLL_INSENSITIVE;
409   }
410
411   public void addBatch(String JavaDoc sql) throws SQLException {
412     addBatch(new SQLQuery(sql));
413   }
414
415   public void clearBatch() throws SQLException {
416     batch_list = null;
417   }
418
419   public int[] executeBatch() throws SQLException {
420     // Execute the batch,
421
if (batch_list == null) {
422       // Batch list is empty - nothing to do
423
throw new SQLException("Batch list is empty - nothing to do.");
424     }
425
426     int sz = batch_list.size();
427     SQLQuery[] batch_query_list = new SQLQuery[sz];
428     for (int i = 0; i < sz; ++i) {
429       batch_query_list[i] = (SQLQuery) batch_list.elementAt(i);
430     }
431     try {
432       // Execute the batch and find the results in the resultant array
433
MResultSet[] batch_results = executeQueries(batch_query_list);
434
435       // Put the result into an update array
436
int[] update_result = new int[sz];
437       for (int i = 0; i < sz; ++i) {
438         update_result[i] = batch_results[i].intValue();
439         batch_results[i].closeCurrentResult();
440       }
441
442       return update_result;
443     }
444     finally {
445       // Make sure we clear the batch list.
446
clearBatch();
447     }
448   }
449
450   public Connection getConnection() throws SQLException {
451     return connection;
452   }
453
454 //#ENDIF
455

456 //#IFDEF(JDBC3.0)
457

458   //--------------------------JDBC 3.0-----------------------------
459

460   public boolean getMoreResults(int current) throws SQLException {
461     return getMoreResults();
462   }
463
464   public ResultSet getGeneratedKeys() throws SQLException {
465     throw MSQLException.unsupported();
466   }
467
468   public int executeUpdate(String JavaDoc sql, int autoGeneratedKeys)
469                                                         throws SQLException {
470     throw MSQLException.unsupported();
471   }
472
473   public int executeUpdate(String JavaDoc sql, int columnIndexes[])
474                                                         throws SQLException {
475     throw MSQLException.unsupported();
476   }
477
478   public int executeUpdate(String JavaDoc sql, String JavaDoc columnNames[])
479                                                         throws SQLException {
480     throw MSQLException.unsupported();
481   }
482
483   public boolean execute(String JavaDoc sql, int autoGeneratedKeys)
484                                                         throws SQLException {
485     throw MSQLException.unsupported();
486   }
487
488   public boolean execute(String JavaDoc sql, int columnIndexes[])
489                                                         throws SQLException {
490     throw MSQLException.unsupported();
491   }
492
493   public boolean execute(String JavaDoc sql, String JavaDoc columnNames[])
494                                                         throws SQLException {
495     throw MSQLException.unsupported();
496   }
497
498   public int getResultSetHoldability() throws SQLException {
499     // In Mckoi, all cursors may be held over commit.
500
return ResultSet.HOLD_CURSORS_OVER_COMMIT;
501   }
502
503 //#ENDIF
504

505
506   // ---------- Finalize ----------
507

508   /**
509    * The statement will close when it is garbage collected.
510    */

511   public void finalize() {
512     try {
513       close();
514     }
515     catch (SQLException e) { /* ignore */ }
516   }
517
518 }
519
Popular Tags