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.Database;
033: import com.caucho.db.store.Transaction;
034: import com.caucho.db.table.Column;
035: import com.caucho.db.table.Table;
036: import com.caucho.db.table.TableIterator;
037: import com.caucho.log.Log;
038: import com.caucho.sql.SQLExceptionWrapper;
039: import com.caucho.util.CharBuffer;
040: import com.caucho.util.L10N;
041:
042: import java.io.IOException;
043: import java.io.InputStream;
044: import java.sql.SQLException;
045: import java.util.ArrayList;
046: import java.util.logging.Level;
047: import java.util.logging.Logger;
048:
049: abstract public class Query {
050: private static final Logger log = Log.open(Query.class);
051: private static final L10N L = new L10N(Query.class);
052:
053: private Database _db;
054:
055: private String _sql;
056:
057: private FromItem[] _fromItems;
058: private ParamExpr[] _params;
059:
060: private boolean _isGroup;
061: private int _dataFieldCount;
062:
063: private Query _parent;
064: private SubSelectExpr _parentSubSelect;
065:
066: private Expr[] _whereExprs;
067: protected Expr _whereExpr;
068:
069: private RowIterateExpr[] _indexExprs;
070:
071: private ArrayList<SubSelectParamExpr> _paramExprs = new ArrayList<SubSelectParamExpr>();
072:
073: protected Query(Database db, String sql) {
074: _db = db;
075: _sql = sql;
076: }
077:
078: protected Query(Database db, String sql, FromItem[] fromItems) {
079: _db = db;
080: _sql = sql;
081: _fromItems = fromItems;
082: }
083:
084: /**
085: * Returns the owning database.
086: */
087: public Database getDatabase() {
088: return _db;
089: }
090:
091: /**
092: * Sets the parent query
093: */
094: public void setParent(Query query) {
095: _parent = query;
096: }
097:
098: /**
099: * Gets the parent query
100: */
101: public Query getParent() {
102: return _parent;
103: }
104:
105: /**
106: * Sets the parent sub-select.
107: */
108: public void setSubSelect(SubSelectExpr subSelect) {
109: _parentSubSelect = subSelect;
110: }
111:
112: /**
113: * Gets the parent sub-select.
114: */
115: public SubSelectExpr getSubSelect() {
116: return _parentSubSelect;
117: }
118:
119: /**
120: * Returns the number of temporary data fields.
121: */
122: public int getDataFields() {
123: return _dataFieldCount;
124: }
125:
126: /**
127: * Sets the number of temporary data fields.
128: */
129: public void setDataFields(int fieldCount) {
130: _dataFieldCount = fieldCount;
131: }
132:
133: /**
134: * Sets the maximum entires
135: */
136: public void setLimit(int limit) {
137: }
138:
139: /**
140: * Returns any from items.
141: */
142: public FromItem[] getFromItems() {
143: return _fromItems;
144: }
145:
146: /**
147: * Sets from items.
148: */
149: protected void setFromItems(FromItem[] fromItems) {
150: _fromItems = fromItems;
151: }
152:
153: /**
154: * Sets from items.
155: */
156: protected void setFromItems(ArrayList<FromItem> fromItems) {
157: _fromItems = new FromItem[fromItems.size()];
158: fromItems.toArray(_fromItems);
159: }
160:
161: /**
162: * Sets the where expr.
163: */
164: public void setWhereExpr(Expr expr) {
165: _whereExpr = expr;
166: }
167:
168: /**
169: * Returns the where exprs
170: */
171: public Expr[] getWhereExprs() {
172: return _whereExprs;
173: }
174:
175: /**
176: * Sets the where exprs.
177: */
178: protected void setWhereExprs(Expr[] whereExprs) {
179: _whereExprs = whereExprs;
180: }
181:
182: /**
183: * Sets the params.
184: */
185: public void setParams(ParamExpr[] params) {
186: _params = params;
187: }
188:
189: /**
190: * Returns the param exprs.
191: */
192: public ArrayList<SubSelectParamExpr> getParamExprs() {
193: return _paramExprs;
194: }
195:
196: /**
197: * Returns the SQL.
198: */
199: String getSQL() {
200: return _sql;
201: }
202:
203: /**
204: * Returns true for select queries.
205: */
206: public boolean isSelect() {
207: return false;
208: }
209:
210: public boolean isReadOnly() {
211: return true;
212: }
213:
214: /**
215: * Sets the current number of group fields.
216: */
217: public void setGroup(boolean isGroup) {
218: _isGroup = isGroup;
219: }
220:
221: /**
222: * Sets true for group operations
223: */
224: public boolean isGroup() {
225: return _isGroup;
226: }
227:
228: /**
229: * Binds the query.
230: */
231: protected void bind() throws SQLException {
232: if (_whereExpr != null) {
233: generateWhere(_whereExpr);
234:
235: for (int i = 0; i < _whereExprs.length; i++) {
236: Expr expr = _whereExprs[i];
237:
238: if (expr != null)
239: _whereExprs[i] = expr.bind(this );
240: }
241: } else if (_fromItems != null) {
242: _whereExprs = new Expr[_fromItems.length + 1];
243: _indexExprs = new RowIterateExpr[_fromItems.length];
244: } else {
245: _whereExprs = new Expr[2];
246: _indexExprs = new RowIterateExpr[1];
247: }
248:
249: for (int i = 0; i < _indexExprs.length; i++) {
250: Expr expr = _indexExprs[i];
251:
252: if (expr == null)
253: _indexExprs[i] = new RowIterateExpr();
254: else
255: _indexExprs[i] = (RowIterateExpr) _indexExprs[i]
256: .bind(this );
257: }
258:
259: for (int i = 0; i < _paramExprs.size(); i++) {
260: SubSelectParamExpr expr = _paramExprs.get(i);
261:
262: expr = (SubSelectParamExpr) expr.bind(_parent);
263: _paramExprs.set(i, expr);
264: }
265: }
266:
267: /**
268: * Optimize the where and order the from items.
269: */
270: protected void generateWhere(Expr whereExpr) throws SQLException {
271: ArrayList<Expr> andProduct = new ArrayList<Expr>();
272:
273: whereExpr.splitAnd(andProduct);
274:
275: FromItem[] fromItems = getFromItems();
276:
277: Expr[] whereExprs = new Expr[fromItems.length + 1];
278: RowIterateExpr[] indexExprs = new RowIterateExpr[fromItems.length];
279:
280: _whereExprs = whereExprs;
281: _indexExprs = indexExprs;
282:
283: ArrayList<FromItem> costItems = new ArrayList<FromItem>();
284: orderFromItems(costItems, andProduct);
285:
286: costItems.clear();
287: for (int i = fromItems.length; i >= 0; i--) {
288: if (i < fromItems.length)
289: costItems.add(fromItems[i]);
290:
291: AndExpr subWhereExpr = null;
292: boolean hasExpr = false;
293:
294: int bestIndex = -1;
295: long bestCost;
296:
297: do {
298: bestCost = Long.MAX_VALUE;
299:
300: for (int j = andProduct.size() - 1; j >= 0; j--) {
301: Expr subExpr = andProduct.get(j);
302:
303: long cost = subExpr.cost(costItems);
304:
305: if (Integer.MAX_VALUE <= cost && i != 0) {
306: } else if (cost < bestCost) {
307: bestCost = cost;
308: bestIndex = j;
309: }
310: }
311:
312: if (bestCost < Long.MAX_VALUE) {
313: Expr expr = andProduct.remove(bestIndex);
314: RowIterateExpr indexExpr = null;
315:
316: if (i < fromItems.length)
317: indexExpr = expr.getIndexExpr(fromItems[i]);
318:
319: if (indexExpr != null && indexExprs[i] == null) {
320: indexExprs[i] = indexExpr;
321: } else {
322: // XXX: check if really need to add
323: if (subWhereExpr == null)
324: subWhereExpr = new AndExpr();
325:
326: subWhereExpr.add(expr);
327: }
328: }
329: } while (bestCost < Long.MAX_VALUE);
330:
331: if (subWhereExpr != null)
332: whereExprs[i] = subWhereExpr.getSingleExpr();
333: }
334:
335: for (int i = 0; i < whereExprs.length; i++) {
336: Expr expr = whereExprs[i];
337: /*
338: if (expr != null)
339: expr = expr.bind(this);
340: */
341:
342: whereExprs[i] = expr;
343: }
344:
345: _whereExprs = whereExprs;
346:
347: if (log.isLoggable(Level.FINE)) {
348: log.fine("where-" + (whereExprs.length - 1) + ": static "
349: + whereExprs[whereExprs.length - 1]);
350:
351: for (int i = whereExprs.length - 2; i >= 0; i--) {
352: if (_indexExprs[i] != null)
353: log.fine("index-" + i + ": " + _fromItems[i] + " "
354: + _indexExprs[i]);
355:
356: log.fine("where-" + i + ": " + _fromItems[i] + " "
357: + whereExprs[i]);
358: }
359: }
360: }
361:
362: private void orderFromItems(ArrayList<FromItem> costItems,
363: ArrayList<Expr> topAndProduct) {
364: FromItem[] fromItems = getFromItems();
365:
366: ArrayList<Expr> andProduct = new ArrayList<Expr>(topAndProduct);
367:
368: for (int i = fromItems.length - 1; i >= 0; i--) {
369: costItems.clear();
370:
371: for (int j = i + 1; j < fromItems.length; j++)
372: costItems.add(fromItems[j]);
373:
374: int bestIndex = i;
375: long bestCost = Expr.COST_INVALID;
376:
377: loop: for (int j = 0; j <= i; j++) {
378: FromItem item = fromItems[j];
379:
380: costItems.add(item);
381:
382: for (int k = 0; k < fromItems.length; k++) {
383: if (!fromItems[k].isValid(costItems)) {
384: costItems.remove(costItems.size() - 1);
385: continue loop;
386: }
387: }
388:
389: long cost = Long.MAX_VALUE;
390: for (int k = 0; k < andProduct.size(); k++) {
391: Expr expr = andProduct.get(k);
392:
393: long subCost = expr.cost(costItems);
394:
395: if (Expr.COST_INVALID <= subCost) {
396: costItems.remove(costItems.size() - 1);
397: continue loop;
398: }
399:
400: if (subCost < cost)
401: cost = subCost;
402: }
403:
404: costItems.remove(costItems.size() - 1);
405:
406: if (cost < bestCost) {
407: bestCost = cost;
408: bestIndex = j;
409: }
410: }
411:
412: FromItem tempItem = fromItems[i];
413: fromItems[i] = fromItems[bestIndex];
414: fromItems[bestIndex] = tempItem;
415:
416: costItems.add(fromItems[i]);
417: for (int k = andProduct.size() - 1; k >= 0; k--) {
418: Expr expr = andProduct.get(k);
419:
420: long subCost = expr.cost(costItems);
421:
422: if (subCost < Expr.COST_NO_TABLE) {
423: andProduct.remove(k);
424: }
425: }
426: }
427: }
428:
429: private String logWhere() {
430: CharBuffer cb = CharBuffer.allocate();
431:
432: cb.append("[");
433: for (int i = 0; i < _whereExprs.length; i++) {
434: if (i != 0)
435: cb.append(", ");
436:
437: if (_whereExprs[i] != null)
438: cb.append(_whereExprs[i]);
439: }
440:
441: cb.append("]");
442:
443: return cb.close();
444: }
445:
446: /**
447: * Returns a bound expression for the specified table.column.
448: */
449: protected Expr bind(String tableName, String columnName)
450: throws SQLException {
451: FromItem[] fromItems = getFromItems();
452:
453: if (tableName == null) {
454: if ("resin_oid".equals(columnName))
455: return new OidExpr(fromItems[0].getTable(), 0);
456:
457: for (int i = 0; i < fromItems.length; i++) {
458: Table table = fromItems[i].getTable();
459:
460: int columnIndex = table.getColumnIndex(columnName);
461:
462: if (columnIndex >= 0) {
463: Column column = table.getColumn(columnName);
464:
465: return new IdExpr(fromItems[i], column);
466: }
467: }
468:
469: Expr expr = bindParent(tableName, columnName);
470: if (expr != null) {
471: return expr;
472: }
473:
474: throw new SQLException(L.l("`{0}' is an unknown column.",
475: columnName));
476: } else {
477: for (int i = 0; i < fromItems.length; i++) {
478: if (tableName.equals(fromItems[i].getName())) {
479: Table table = fromItems[i].getTable();
480:
481: if ("resin_oid".equals(columnName))
482: return new OidExpr(table, i);
483:
484: int columnIndex = table.getColumnIndex(columnName);
485:
486: if (columnIndex < 0) {
487: Expr expr = bindParent(tableName, columnName);
488: if (expr != null)
489: return expr;
490:
491: throw new SQLException(
492: L
493: .l(
494: "`{0}' is an unknown column in \n {1}.",
495: columnName, _sql));
496: }
497:
498: Column column = table.getColumn(columnName);
499:
500: return new IdExpr(fromItems[i], column);
501: }
502: }
503:
504: Expr expr = bindParent(tableName, columnName);
505: if (expr != null)
506: return expr;
507:
508: throw new SQLException(L.l(
509: "`{0}' is an unknown table.\n{1}", tableName,
510: getSQL()));
511: }
512: }
513:
514: /**
515: * Binds as a subselect.
516: */
517: private Expr bindParent(String tableName, String columnName)
518: throws SQLException {
519: if (_parent != null) {
520: Expr expr = _parent.bind(tableName, columnName);
521:
522: if (expr != null) {
523: SubSelectParamExpr paramExpr;
524:
525: paramExpr = new SubSelectParamExpr(this , expr,
526: _paramExprs.size());
527: _paramExprs.add(paramExpr);
528:
529: return paramExpr;
530: }
531: }
532:
533: return null;
534: }
535:
536: /**
537: * Clears the paramters.
538: */
539: public void clearParameters() {
540: for (int i = 0; i < _params.length; i++)
541: _params[i].clear();
542: }
543:
544: /**
545: * Sets the indexed parameter as a boolean.
546: */
547: public void setBoolean(int index, boolean value) {
548: _params[index - 1].setBoolean(value);
549: }
550:
551: /**
552: * Sets the indexed parameter as a string.
553: */
554: public void setString(int index, String value) {
555: _params[index - 1].setString(value);
556: }
557:
558: /**
559: * Sets the indexed parameter as a long.
560: */
561: public void setLong(int index, long value) {
562: _params[index - 1].setLong(value);
563: }
564:
565: /**
566: * Sets the indexed parameter as a double.
567: */
568: public void setDouble(int index, double value) {
569: _params[index - 1].setDouble(value);
570: }
571:
572: /**
573: * Sets the indexed parameter as a date value.
574: */
575: public void setDate(int index, long value) {
576: _params[index - 1].setDate(value);
577: }
578:
579: /**
580: * Sets the indexed parameter as a binary stream
581: */
582: public void setBinaryStream(int index, InputStream is, int length) {
583: _params[index - 1].setBinaryStream(is, length);
584: }
585:
586: /**
587: * Executes the query.
588: */
589: abstract public void execute(QueryContext queryCtx, Transaction xa)
590: throws SQLException;
591:
592: /**
593: * Starts the query.
594: */
595: protected boolean start(TableIterator[] rows, int rowLength,
596: QueryContext queryContext, Transaction xa)
597: throws SQLException {
598: try {
599: Expr[] whereExprs = _whereExprs;
600:
601: // Test the constant expression
602: if (whereExprs == null || whereExprs[rowLength] == null) {
603: } else if (!whereExprs[rowLength].isSelect(queryContext)) {
604: return false;
605: }
606:
607: if (rowLength == 0)
608: return true;
609:
610: for (int i = rowLength - 1; i >= 0; i--) {
611: TableIterator row = rows[i];
612: RowIterateExpr iterExpr = _indexExprs[i];
613:
614: if (!iterExpr.init(queryContext, row)) {
615: return false;
616: }
617:
618: // XXX: check to make sure others actually lock this properly
619: //if (! xa.isAutoCommit())
620: // xa.lockRead(row.getTable().getLock());
621: }
622:
623: return (initBlockRow(rowLength - 1, rows, queryContext) || nextBlock(
624: rowLength - 1, rows, rowLength, queryContext));
625: } catch (IOException e) {
626: throw new SQLExceptionWrapper(e);
627: }
628: }
629:
630: /**
631: * Returns the next tuple from the query.
632: */
633: protected boolean nextTuple(TableIterator[] rows, int rowLength,
634: QueryContext queryContext, Transaction xa)
635: throws SQLException {
636: try {
637: if (rowLength == 0)
638: return false;
639:
640: RowIterateExpr[] indexExprs = _indexExprs;
641: Expr[] whereExprs = _whereExprs;
642:
643: for (int i = 0; i < rowLength; i++) {
644: TableIterator table = rows[i];
645: RowIterateExpr indexExpr = indexExprs[i];
646:
647: Expr whereExpr = whereExprs == null ? null
648: : whereExprs[i];
649:
650: while (indexExpr.nextRow(queryContext, table)) {
651: if (whereExpr == null
652: || whereExpr.isSelect(queryContext)) {
653: if (i == 0
654: || initBlockRow(i - 1, rows,
655: queryContext)) {
656: return true;
657: }
658: }
659: }
660: }
661:
662: return nextBlock(rowLength - 1, rows, rowLength,
663: queryContext);
664: } catch (IOException e) {
665: throw new SQLExceptionWrapper(e);
666: }
667: }
668:
669: /**
670: * Initialize this row and all previous rows within this block group.
671: */
672: private boolean nextBlock(int i, TableIterator[] rows,
673: int rowLength, QueryContext queryContext)
674: throws IOException, SQLException {
675: TableIterator rowIter = rows[i];
676: RowIterateExpr iterExpr = _indexExprs[i];
677:
678: while (true) {
679: if (i > 0
680: && nextBlock(i - 1, rows, rowLength, queryContext)) {
681: return true;
682: }
683:
684: if (!iterExpr.nextBlock(queryContext, rowIter)) {
685: return false;
686: }
687:
688: if (!iterExpr.allowChildRowShift(queryContext, rows[i]))
689: return false;
690:
691: for (int j = i - 1; j >= 0; j--) {
692: if (!iterExpr.init(queryContext, rows[j]))
693: return false;
694: }
695:
696: if (initBlockRow(rowLength - 1, rows, queryContext))
697: return true;
698: }
699: }
700:
701: /**
702: * Initialize this row and all previous rows within this block group.
703: */
704: private boolean initBlockRow(int i, TableIterator[] rows,
705: QueryContext queryContext) throws IOException, SQLException {
706: RowIterateExpr iterExpr = _indexExprs[i];
707:
708: Expr[] whereExprs = _whereExprs;
709: Expr expr = whereExprs == null ? null : whereExprs[i];
710:
711: TableIterator rowIter = rows[i];
712:
713: if (!iterExpr.initRow(queryContext, rowIter)) {
714: return false;
715: }
716:
717: while (expr != null && !expr.isSelect(queryContext) || i > 0
718: && !initBlockRow(i - 1, rows, queryContext)) {
719: if (!iterExpr.nextRow(queryContext, rowIter)) {
720: return false;
721: }
722: }
723:
724: return true;
725: }
726:
727: /**
728: * Frees any blocks for the rows.
729: */
730: protected void freeRows(TableIterator[] rows, int rowLength) {
731: for (rowLength--; rowLength >= 0; rowLength--) {
732: if (rows[rowLength] != null)
733: rows[rowLength].free();
734: }
735: }
736: }
|