001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.db.sql;
031:
032: import com.caucho.db.jdbc.GeneratedKeysResultSet;
033: import com.caucho.db.store.Block;
034: import com.caucho.db.store.Transaction;
035: import com.caucho.db.table.TableIterator;
036: import com.caucho.log.Log;
037: import com.caucho.util.FreeList;
038: import com.caucho.util.L10N;
039:
040: import java.io.IOException;
041: import java.sql.SQLException;
042: import java.util.HashMap;
043: import java.util.Iterator;
044: import java.util.logging.Level;
045: import java.util.logging.Logger;
046:
047: /**
048: * Represents the state of the query at any particular time.
049: */
050: public class QueryContext {
051: private static final Logger log = Log.open(QueryContext.class);
052: private static final L10N L = new L10N(QueryContext.class);
053:
054: private static final long LOCK_TIMEOUT = 120000;
055:
056: private static final FreeList<QueryContext> _freeList = new FreeList<QueryContext>(
057: 64);
058:
059: private Transaction _xa;
060: private TableIterator[] _tableIterators;
061: private boolean _isWrite;
062:
063: private Data[] _parameters = new Data[8];
064:
065: private GroupItem _tempGroupItem;
066: private GroupItem _groupItem;
067:
068: private boolean _isReturnGeneratedKeys;
069: private SelectResult _result;
070: private GeneratedKeysResultSet _generatedKeys;
071: private int _rowUpdateCount;
072:
073: private int _limit = -1;
074:
075: private Block[] _blockLocks;
076: private boolean _isLocked;
077:
078: private HashMap<GroupItem, GroupItem> _groupMap;
079:
080: private byte[] _buffer = new byte[256];
081:
082: private QueryContext() {
083: _tempGroupItem = GroupItem.allocate(new boolean[8]);
084: }
085:
086: /**
087: * Returns a new query context.
088: */
089: public static QueryContext allocate() {
090: QueryContext queryContext = _freeList.allocate();
091:
092: if (queryContext == null)
093: queryContext = new QueryContext();
094:
095: queryContext.clearParameters();
096:
097: queryContext._limit = -1;
098:
099: return queryContext;
100: }
101:
102: public void clearParameters() {
103: for (int i = _parameters.length - 1; i >= 0; i--) {
104: if (_parameters[i] == null)
105: _parameters[i] = new Data();
106:
107: _parameters[i].clear();
108: }
109: }
110:
111: /**
112: * Initializes the query state.
113: */
114: public void init(Transaction xa, TableIterator[] tableIterators,
115: boolean isReadOnly) {
116: _xa = xa;
117: _isWrite = !isReadOnly;
118: _tableIterators = tableIterators;
119: _blockLocks = new Block[_tableIterators.length];
120: _isLocked = false;
121:
122: _rowUpdateCount = 0;
123: _groupItem = _tempGroupItem;
124: _groupItem.init(0, null);
125: }
126:
127: /**
128: * Initializes the group.
129: */
130: public void initGroup(int size, boolean[] isGroupByFields) {
131: _groupItem = _tempGroupItem;
132:
133: _groupItem.init(size, isGroupByFields);
134:
135: if (_groupMap == null)
136: _groupMap = new HashMap<GroupItem, GroupItem>();
137: }
138:
139: /**
140: * Selects the actual group item.
141: */
142: public void selectGroup() {
143: GroupItem item = _groupMap.get(_groupItem);
144:
145: if (item == null) {
146: item = _groupItem.allocateCopy();
147:
148: _groupMap.put(item, item);
149: }
150:
151: _groupItem = item;
152: }
153:
154: /**
155: * Returns the group results.
156: */
157: Iterator<GroupItem> groupResults() {
158: if (_groupMap == null)
159: return com.caucho.util.NullIterator.create();
160:
161: Iterator<GroupItem> results = _groupMap.values().iterator();
162: _groupMap = null;
163:
164: return results;
165: }
166:
167: /**
168: * Sets the current result.
169: */
170: void setGroupItem(GroupItem item) {
171: _groupItem = item;
172: }
173:
174: /**
175: * Returns the table iterator.
176: */
177: public TableIterator[] getTableIterators() {
178: return _tableIterators;
179: }
180:
181: /**
182: * Sets the transaction.
183: */
184: public void setTransaction(Transaction xa) {
185: _xa = xa;
186: }
187:
188: /**
189: * Returns the transaction.
190: */
191: public Transaction getTransaction() {
192: return _xa;
193: }
194:
195: /**
196: * Returns the temp buffer.
197: */
198: public byte[] getBuffer() {
199: return _buffer;
200: }
201:
202: /**
203: * Returns the number of rows updated.
204: */
205: public int getRowUpdateCount() {
206: return _rowUpdateCount;
207: }
208:
209: /**
210: * Sets the number of rows updated.
211: */
212: public void setRowUpdateCount(int count) {
213: _rowUpdateCount = count;
214: }
215:
216: /**
217: * Set if the query should return the generated keys.
218: */
219: public boolean isReturnGeneratedKeys() {
220: return _isReturnGeneratedKeys;
221: }
222:
223: /**
224: * Set if the query should return the generated keys.
225: */
226: public void setReturnGeneratedKeys(boolean isReturnGeneratedKeys) {
227: _isReturnGeneratedKeys = isReturnGeneratedKeys;
228:
229: if (_isReturnGeneratedKeys && _generatedKeys != null)
230: _generatedKeys.init();
231: }
232:
233: /**
234: * The max rows returned in a select
235: */
236: public void setLimit(int limit) {
237: _limit = limit;
238: }
239:
240: /**
241: * The max rows returned in a select
242: */
243: public int getLimit() {
244: return _limit;
245: }
246:
247: /**
248: * Sets the indexed group field.
249: */
250: public boolean isGroupNull(int index) {
251: return _groupItem.isNull(index);
252: }
253:
254: /**
255: * Sets the indexed group field.
256: */
257: public void setGroupString(int index, String value) {
258: _groupItem.setString(index, value);
259: }
260:
261: /**
262: * Sets the indexed group field.
263: */
264: public String getGroupString(int index) {
265: return _groupItem.getString(index);
266: }
267:
268: /**
269: * Sets the indexed group field as a long.
270: */
271: public void setGroupLong(int index, long value) {
272: _groupItem.setLong(index, value);
273: }
274:
275: /**
276: * Sets the indexed group field as a long.
277: */
278: public long getGroupLong(int index) {
279: return _groupItem.getLong(index);
280: }
281:
282: /**
283: * Sets the indexed group field as a double.
284: */
285: public void setGroupDouble(int index, double value) {
286: _groupItem.setDouble(index, value);
287: }
288:
289: /**
290: * Sets the indexed group field as a double.
291: */
292: public double getGroupDouble(int index) {
293: return _groupItem.getDouble(index);
294: }
295:
296: /**
297: * Returns the indexed group field.
298: */
299: public Data getGroupData(int index) {
300: return _groupItem.getData(index);
301: }
302:
303: /**
304: * Set a null parameter.
305: */
306: public void setNull(int index) {
307: _parameters[index].setString(null);
308: }
309:
310: /**
311: * Returns the null parameter.
312: */
313: public boolean isNull(int index) {
314: return _parameters[index].isNull();
315: }
316:
317: /**
318: * Set a long parameter.
319: */
320: public void setLong(int index, long value) {
321: _parameters[index].setLong(value);
322: }
323:
324: /**
325: * Returns the boolean parameter.
326: */
327: public int getBoolean(int index) {
328: return _parameters[index].getBoolean();
329: }
330:
331: /**
332: * Set a boolean parameter.
333: */
334: public void setBoolean(int index, boolean value) {
335: _parameters[index].setBoolean(value);
336: }
337:
338: /**
339: * Returns the long parameter.
340: */
341: public long getLong(int index) {
342: return _parameters[index].getLong();
343: }
344:
345: /**
346: * Returns the long parameter.
347: */
348: public long getDate(int index) {
349: return _parameters[index].getDate();
350: }
351:
352: /**
353: * Set a double parameter.
354: */
355: public void setDouble(int index, double value) {
356: _parameters[index].setDouble(value);
357: }
358:
359: /**
360: * Returns the double parameter.
361: */
362: public double getDouble(int index) {
363: return _parameters[index].getDouble();
364: }
365:
366: /**
367: * Set a string parameter.
368: */
369: public void setString(int index, String value) {
370: _parameters[index].setString(value);
371: }
372:
373: /**
374: * Returns the string parameter.
375: */
376: public String getString(int index) {
377: return _parameters[index].getString();
378: }
379:
380: /**
381: * Sets the result set.
382: */
383: public void setResult(SelectResult result) {
384: _result = result;
385: }
386:
387: /**
388: * Gets the result set.
389: */
390: public SelectResult getResult() {
391: return _result;
392: }
393:
394: /**
395: * Gets the generated keys result set.
396: */
397: public GeneratedKeysResultSet getGeneratedKeysResultSet() {
398: if (!_isReturnGeneratedKeys)
399: return null;
400:
401: if (_generatedKeys == null)
402: _generatedKeys = new GeneratedKeysResultSet();
403:
404: return _generatedKeys;
405: }
406:
407: /**
408: * Lock the blocks. The blocks are locked in ascending block id
409: * order to avoid deadlocks.
410: *
411: * @param isWrite if true, the block should be locked for writing
412: */
413: public void lock() throws SQLException {
414: if (_isLocked)
415: throw new IllegalStateException(L
416: .l("blocks are already locked"));
417: _isLocked = true;
418:
419: int len = _blockLocks.length;
420:
421: for (int i = 0; i < len; i++) {
422: Block bestBlock = null;
423: long bestId = Long.MAX_VALUE;
424:
425: loop: for (int j = 0; j < len; j++) {
426: TableIterator iter = _tableIterators[j];
427:
428: if (iter == null)
429: continue;
430:
431: Block block = iter.getBlock();
432:
433: if (block == null)
434: continue;
435:
436: long id = block.getBlockId();
437: if (bestId <= id)
438: continue;
439:
440: for (int k = 0; k < i; k++) {
441: if (_blockLocks[k] == block)
442: continue loop;
443: }
444:
445: bestId = id;
446: bestBlock = block;
447: }
448:
449: _blockLocks[i] = bestBlock;
450:
451: if (bestBlock == null) {
452: } else if (_isWrite) {
453: _xa.lockReadAndWrite(bestBlock.getLock());
454: } else {
455: _xa.lockRead(bestBlock.getLock());
456: }
457: }
458: }
459:
460: /**
461: * Unlock the blocks. The blocks are unlocked in descending order.
462: *
463: * @param isWrite if true, the block should be unlocked for writing
464: */
465: public void unlock() throws SQLException {
466: if (!_isLocked)
467: return;
468:
469: _isLocked = false;
470:
471: int len = _blockLocks.length;
472:
473: // need to unlock first since the writeData/commit will wait for
474: // write locks to clear before committing
475: for (int i = len - 1; i >= 0; i--) {
476: Block block = _blockLocks[i];
477:
478: if (block == null) {
479: } else if (_isWrite) {
480: _xa.unlockReadAndWrite(block.getLock());
481: } else {
482: _xa.unlockRead(block.getLock());
483: }
484: }
485:
486: try {
487: _xa.writeData();
488: } finally {
489: for (int i = len - 1; i >= 0; i--) {
490: Block block = _blockLocks[i];
491: _blockLocks[i] = null;
492:
493: if (block == null) {
494: } else if (_isWrite) {
495: try {
496: block.commit();
497: } catch (IOException e) {
498: log.log(Level.FINE, e.toString(), e);
499: }
500: }
501: }
502: }
503: }
504:
505: /**
506: * Returns a new query context.
507: */
508: public static void free(QueryContext queryContext) {
509: queryContext._groupMap = null;
510:
511: _freeList.free(queryContext);
512: }
513: }
|