KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > CompiledStatementManager


1 /* Copyright (c) 2001-2005, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31
32 package org.hsqldb;
33
34 import org.hsqldb.HsqlNameManager.HsqlName;
35 import org.hsqldb.lib.IntKeyHashMap;
36 import org.hsqldb.lib.IntKeyIntValueHashMap;
37 import org.hsqldb.lib.IntValueHashMap;
38 import org.hsqldb.lib.Iterator;
39
40 /**
41  * This class manages the reuse of CompiledStatement objects for prepared
42  * statements for a Database instance.<p>
43  *
44  * A compiled statement is registered by a session to be managed. Once
45  * registered, it is linked with one or more sessions.<p>
46  *
47  * The sql statement text distinguishes different compiled statements and acts
48  * as lookup key when a session initially looks for an existing instance of
49  * the compiled sql statement.<p>
50  *
51  * Once a session is linked with a statement, it uses the uniqe compiled
52  * statement id for the sql statement to access the statement.<p>
53  *
54  * Changes to database structure via DDL statements, will result in all
55  * registered CompiledStatement objects to become invalidated. This is done by
56  * setting to null all the managed CompiledStatement instances, while keeping
57  * their id and sql string. When a session subsequently attempts to use an
58  * invalidated (null) CompiledStatement via its id, it will reinstantiate the
59  * CompiledStatement using its sql statement still held by this class.<p>
60  *
61  * This class keeps count of the number of different sessions that are linked
62  * to each registered compiled statement, and the number of times each session
63  * is linked. It unregisters a compiled statement when no session remains
64  * linked to it.<p>
65  *
66  * Modified by fredt@users from the original by boucherb@users to simplify,
67  * support multiple identical prepared statements per session, and avoid
68  * keeping references to CompiledStatement objects after DDL changes which
69  * could result in memory leaks. Modified further to support schemas.<p>
70  *
71  * @author boucherb@users
72  * @author fredt@users
73  *
74  * @since 1.7.2
75  * @version 1.8.0
76  */

77 final class CompiledStatementManager {
78
79     /**
80      * The Database for which this object is managing
81      * CompiledStatement objects.
82      */

83     private Database database;
84
85     /** Map: Schema id (int) => {Map: SQL String => Compiled Statement id (int)} */
86     private IntKeyHashMap schemaMap;
87
88     /** Map: Compiled Statement id (int) => SQL String */
89     private IntKeyHashMap sqlLookup;
90
91     /** Map: Compiled statment id (int) => CompiledStatement object. */
92     private IntKeyHashMap csidMap;
93
94     /** Map: Session id (int) => {Map: compiled statement id (int) => use count in session} */
95     private IntKeyHashMap sessionUseMap;
96
97     /** Map: Compiled statment id (int) => number of sessions that use the statement */
98     private IntKeyIntValueHashMap useMap;
99
100     /**
101      * Monotonically increasing counter used to assign unique ids to compiled
102      * statements.
103      */

104     private int next_cs_id;
105
106     /**
107      * Constructs a new instance of <code>CompiledStatementManager</code>.
108      *
109      * @param database the Database instance for which this object is to
110      * manage compiled statement objects.
111      */

112     CompiledStatementManager(Database database) {
113
114         this.database = database;
115         schemaMap = new IntKeyHashMap();
116         sqlLookup = new IntKeyHashMap();
117         csidMap = new IntKeyHashMap();
118         sessionUseMap = new IntKeyHashMap();
119         useMap = new IntKeyIntValueHashMap();
120         next_cs_id = 0;
121     }
122
123     /**
124      * Clears all internal data structures, removing any references to compiled statements.
125      */

126     synchronized void reset() {
127
128         schemaMap.clear();
129         sqlLookup.clear();
130         csidMap.clear();
131         sessionUseMap.clear();
132         useMap.clear();
133
134         next_cs_id = 0;
135     }
136
137     /**
138      * Used after a DDL change that could impact the compiled statements.
139      * Clears references to CompiledStatement objects while keeping the counts
140      * and references to the sql strings.
141      */

142     synchronized void resetStatements() {
143
144         Iterator it = csidMap.values().iterator();
145
146         while (it.hasNext()) {
147             CompiledStatement cs = (CompiledStatement) it.next();
148
149             cs.clearVariables();
150         }
151     }
152
153     /**
154      * Retrieves the next compiled statement identifier in the sequence.
155      *
156      * @return the next compiled statement identifier in the sequence.
157      */

158     private int nextID() {
159
160         next_cs_id++;
161
162         return next_cs_id;
163     }
164
165     /**
166      * Retrieves the registered compiled statement identifier associated with
167      * the specified SQL String, or a value less than zero, if no such
168      * statement has been registered.
169      *
170      * @param schema the schema id
171      * @param sql the SQL String
172      * @return the compiled statement identifier associated with the
173      * specified SQL String
174      */

175     private int getStatementID(HsqlName schema, String JavaDoc sql) {
176
177         IntValueHashMap sqlMap =
178             (IntValueHashMap) schemaMap.get(schema.hashCode());
179
180         if (sqlMap == null) {
181             return -1;
182         }
183
184         return sqlMap.get(sql, -1);
185     }
186
187     /**
188      * Returns an existing CompiledStatement object with the given
189      * statement identifier. Returns null if the CompiledStatement object
190      * has been invalidated and cannot be recompiled
191      *
192      * @param session the session
193      * @param csid the identifier of the requested CompiledStatement object
194      * @return the requested CompiledStatement object
195      */

196     synchronized CompiledStatement getStatement(Session session, int csid) {
197
198         CompiledStatement cs = (CompiledStatement) csidMap.get(csid);
199
200         if (cs == null) {
201             return null;
202         }
203
204         if (!cs.isValid) {
205             String JavaDoc sql = (String JavaDoc) sqlLookup.get(csid);
206
207             // revalidate with the original schema
208
try {
209                 cs = compileSql(session, sql, cs.schemaHsqlName.name);
210                 cs.id = csid;
211
212                 csidMap.put(csid, cs);
213             } catch (Throwable JavaDoc t) {
214                 freeStatement(csid, session.getId(), true);
215
216                 return null;
217             }
218         }
219
220         return cs;
221     }
222
223     /**
224      * Links a session with a registered compiled statement.
225      *
226      * If this session has not already been linked with the given
227      * statement, then the statement use count is incremented.
228      *
229      * @param csid the compiled statement identifier
230      * @param sid the session identifier
231      */

232     private void linkSession(int csid, int sid) {
233
234         IntKeyIntValueHashMap scsMap;
235
236         scsMap = (IntKeyIntValueHashMap) sessionUseMap.get(sid);
237
238         if (scsMap == null) {
239             scsMap = new IntKeyIntValueHashMap();
240
241             sessionUseMap.put(sid, scsMap);
242         }
243
244         int count = scsMap.get(csid, 0);
245
246         scsMap.put(csid, count + 1);
247
248         if (count == 0) {
249             useMap.put(csid, useMap.get(csid, 0) + 1);
250         }
251     }
252
253     /**
254      * Registers a compiled statement to be managed.
255      *
256      * The only caller should be a Session that is attempting to prepare
257      * a statement for the first time or process a statement that has been
258      * invalidated due to DDL changes.
259      *
260      * @param csid existing id or negative if the statement is not yet managed
261      * @param cs The CompiledStatement to add
262      * @return The compiled statement id assigned to the CompiledStatement
263      * object
264      */

265     private int registerStatement(int csid, CompiledStatement cs) {
266
267         if (csid < 0) {
268             csid = nextID();
269
270             int schemaid = cs.schemaHsqlName.hashCode();
271             IntValueHashMap sqlMap =
272                 (IntValueHashMap) schemaMap.get(schemaid);
273
274             if (sqlMap == null) {
275                 sqlMap = new IntValueHashMap();
276
277                 schemaMap.put(schemaid, sqlMap);
278             }
279
280             sqlMap.put(cs.sql, csid);
281             sqlLookup.put(csid, cs.sql);
282         }
283
284         cs.id = csid;
285
286         csidMap.put(csid, cs);
287
288         return csid;
289     }
290
291     /**
292      * Removes one (or all) of the links between a session and a compiled statement.
293      *
294      * If the statement is not linked with any other session, it is removed
295      * from management.
296      *
297      * @param csid the compiled statment identifier
298      * @param sid the session identifier
299      * @param freeAll if true, remove all links to the session
300      */

301     void freeStatement(int csid, int sid, boolean freeAll) {
302
303         if (csid == -1) {
304
305             // statement was never added
306
return;
307         }
308
309         IntKeyIntValueHashMap scsMap =
310             (IntKeyIntValueHashMap) sessionUseMap.get(sid);
311
312         if (scsMap == null) {
313
314             // statement already removed due to invalidation
315
return;
316         }
317
318         int sessionUseCount = scsMap.get(csid, 0);
319
320         if (sessionUseCount == 0) {
321
322             // statement already removed due to invalidation
323
} else if (sessionUseCount == 1 || freeAll) {
324             scsMap.remove(csid);
325
326             int usecount = useMap.get(csid, 0);
327
328             if (usecount == 0) {
329
330                 // statement already removed due to invalidation
331
} else if (usecount == 1) {
332                 CompiledStatement cs =
333                     (CompiledStatement) csidMap.remove(csid);
334
335                 if (cs != null) {
336                     int schemaid = cs.schemaHsqlName.hashCode();
337                     IntValueHashMap sqlMap =
338                         (IntValueHashMap) schemaMap.get(schemaid);
339                     String JavaDoc sql = (String JavaDoc) sqlLookup.remove(csid);
340
341                     sqlMap.remove(sql);
342                 }
343
344                 useMap.remove(csid);
345             } else {
346                 useMap.put(csid, usecount - 1);
347             }
348         } else {
349             scsMap.put(csid, sessionUseCount - 1);
350         }
351     }
352
353     /**
354      * Releases the link betwen the session and all compiled statement objects
355      * it is linked to.
356      *
357      * If any such statement is not linked with any other session, it is
358      * removed from management.
359      *
360      * @param sid the session identifier
361      */

362     synchronized void removeSession(int sid) {
363
364         IntKeyIntValueHashMap scsMap;
365         int csid;
366         Iterator i;
367
368         scsMap = (IntKeyIntValueHashMap) sessionUseMap.remove(sid);
369
370         if (scsMap == null) {
371             return;
372         }
373
374         i = scsMap.keySet().iterator();
375
376         while (i.hasNext()) {
377             csid = i.nextInt();
378
379             int usecount = useMap.get(csid, 1) - 1;
380
381             if (usecount == 0) {
382                 CompiledStatement cs =
383                     (CompiledStatement) csidMap.remove(csid);
384
385                 if (cs != null) {
386                     int schemaid = cs.schemaHsqlName.hashCode();
387                     IntValueHashMap sqlMap =
388                         (IntValueHashMap) schemaMap.get(schemaid);
389                     String JavaDoc sql = (String JavaDoc) sqlLookup.remove(csid);
390
391                     sqlMap.remove(sql);
392                 }
393
394                 useMap.remove(csid);
395             } else {
396                 useMap.put(csid, usecount);
397             }
398         }
399     }
400
401     /**
402      * Retrieves a MULTI Result describing three aspects of the
403      * CompiledStatement prepared from the SQL argument for execution
404      * in this session context. <p>
405      *
406      * <ol>
407      * <li>A PREPARE_ACK mode Result describing id of the statement
408      * prepared by this request. This is used by the JDBC implementation
409      * to later identify to the engine which prepared statement to execute.
410      *
411      * <li>A DATA mode result describing the statement's result set metadata.
412      * This is used to generate the JDBC ResultSetMetaData object returned
413      * by PreparedStatement.getMetaData and CallableStatement.getMetaData.
414      *
415      * <li>A DATA mode result describing the statement's parameter metdata.
416      * This is used to by the JDBC implementation to determine
417      * how to send parameters back to the engine when executing the
418      * statement. It is also used to construct the JDBC ParameterMetaData
419      * object for PreparedStatements and CallableStatements.
420      *
421      * @param session the session
422      * @param sql a string describing the desired statement object
423      * @return a MULTI Result describing the compiled statement.
424      */

425     synchronized CompiledStatement compile(Session session,
426                                            String JavaDoc sql) throws Throwable JavaDoc {
427
428         int csid = getStatementID(session.currentSchema, sql);
429         CompiledStatement cs = (CompiledStatement) csidMap.get(csid);
430
431         if (cs == null ||!cs.isValid ||!session.isAdmin()) {
432             cs = compileSql(session, sql, session.currentSchema.name);
433             csid = registerStatement(csid, cs);
434         }
435
436         linkSession(csid, session.getId());
437
438         return cs;
439     }
440
441     private CompiledStatement compileSql(Session session, String JavaDoc sql,
442                                          String JavaDoc schemaName) throws Throwable JavaDoc {
443
444         Session sys = database.sessionManager.getSysSession(schemaName,
445             session.getUser());
446
447         return sys.sqlCompileStatement(sql);
448     }
449 }
450
Popular Tags