001: /*
002: * $Id: SelectCommand.java,v 1.111 2005/12/23 01:32:25 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2002-2006 Axion Development Team. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above
011: * copyright notice, this list of conditions and the following
012: * disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The names "Tigris", "Axion", nor the names of its contributors may
020: * not be used to endorse or promote products derived from this
021: * software without specific prior written permission.
022: *
023: * 4. Products derived from this software may not be called "Axion", nor
024: * may "Tigris" or "Axion" appear in their names without specific prior
025: * written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
030: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
032: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
033: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
034: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
035: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
037: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038: * =======================================================================
039: */
040:
041: package org.axiondb.engine.commands;
042:
043: import java.util.ArrayList;
044: import java.util.Collections;
045: import java.util.HashMap;
046: import java.util.List;
047: import java.util.Map;
048:
049: import org.axiondb.AxionException;
050: import org.axiondb.ColumnIdentifier;
051: import org.axiondb.Database;
052: import org.axiondb.OrderNode;
053: import org.axiondb.RowDecorator;
054: import org.axiondb.RowIterator;
055: import org.axiondb.Selectable;
056: import org.axiondb.TableIdentifier;
057: import org.axiondb.engine.rowiterators.RowIteratorRowDecoratorIterator;
058: import org.axiondb.engine.visitors.AmbiguousColumnReferenceVisitor;
059: import org.axiondb.engine.visitors.AssertGroupByRulesVisitor;
060: import org.axiondb.engine.visitors.FindAggregateFunctionVisitor;
061: import org.axiondb.engine.visitors.FindBindVariableVisitor;
062: import org.axiondb.engine.visitors.ResolveFromNodeVisitor;
063: import org.axiondb.jdbc.AxionResultSet;
064: import org.axiondb.types.StringType;
065:
066: /**
067: * A <tt>SELECT</tt> query.
068: *
069: * @version $Revision: 1.111 $ $Date: 2005/12/23 01:32:25 $
070: * @author Morgan Delagrange
071: * @author Rodney Waldhoff
072: * @author Chuck Burdick
073: * @author Amrish Lal
074: * @author Dave Pekarek Krohn
075: * @author Rahul Dwivedi
076: * @author Ahimanikya Satapathy
077: */
078: public class SelectCommand extends BaseAxionCommand {
079: //------------------------------------------------------------ Constructors
080:
081: public SelectCommand(AxionQueryContext context) {
082: _context = context;
083: _planner = new AxionQueryPlanner(context);
084: }
085:
086: public boolean execute(Database database) throws AxionException {
087: setResultSet(executeQuery(database));
088: return (getResultSet() != null);
089: }
090:
091: //-------------------------------------------------- Command Implementation
092:
093: /**
094: * Execute this command, returning a {@link java.sql.ResultSet}.
095: *
096: * @return the {@link java.sql.ResultSet}generated by this command.
097: * @throws AxionException
098: */
099: public AxionResultSet executeQuery(Database db)
100: throws AxionException {
101: return executeQuery(db, true);
102: }
103:
104: public AxionResultSet executeQuery(Database db, boolean isReadOnly)
105: throws AxionException {
106: RowIterator rows = makeRowIterator(db, isReadOnly);
107: if (_context.isExplain()) {
108: return makeExplainResultSet(rows);
109: }
110: return new AxionResultSet(new RowIteratorRowDecoratorIterator(
111: rows,
112: new RowDecorator(_planner.getColumnIdToFieldMap())),
113: _context.getSelected(), null);
114: }
115:
116: /** Unsupported */
117: public int executeUpdate(Database database) throws AxionException {
118: throw new UnsupportedOperationException(
119: "Use executeQuery, not executeUpdate.");
120: }
121:
122: public Map getColumnIdToFieldMap() {
123: return _planner.getColumnIdToFieldMap();
124: }
125:
126: public AxionQueryContext getQueryContext() {
127: return _context;
128: }
129:
130: public String toString() {
131: return _context.toString();
132: }
133:
134: protected void buildTableList(Database db) throws AxionException {
135: _context.setTables(_context.getFromArray());
136: }
137:
138: protected void buildBindVariables() {
139: setBindVariableVisitor(new FindBindVariableVisitor());
140: getBindVariableVisitor().visit(this );
141: }
142:
143: public RowIterator makeRowIterator(Database db, boolean isReadOnly)
144: throws AxionException {
145: return makeRowIterator(db, isReadOnly, false);
146: }
147:
148: public RowIterator makeRowIterator(Database db, boolean isReadOnly,
149: boolean refresh) throws AxionException {
150: resolve(db);
151: if (refresh || _currentDatabase != db
152: || _context.foundAggregateFunction()
153: || (_context.getGroupByCount() > 0)) {
154: _context.setSelected(generateSelectArrayForResultSet());
155: _context.setRows(_planner.makeRowIterator(db, isReadOnly));
156: } else {
157: _context.getRows().reset();
158: }
159: _currentDatabase = db;
160:
161: return _context.getRows();
162: }
163:
164: protected void resolve(Database db) throws AxionException {
165:
166: if (!_context.isResolved()) {
167:
168: // resolve from node for any sub-select
169: ResolveFromNodeVisitor fnVisitor = new ResolveFromNodeVisitor();
170: fnVisitor.resolveFromNode(_context.getFrom(), db);
171: buildTableList(db); // Sub-Select will inlude tables from outer select
172:
173: // resolve SELECT part
174: List tempList = new ArrayList();
175: for (int i = 0, I = _context.getSelectCount(); i < I; i++) {
176: Selectable selectable = resolveSelectable(_context
177: .getSelect(i), db, tempList, _context
178: .getTables());
179: _context.setSelect(i, selectable);
180: tempList.add(selectable);
181: }
182: tempList = null;
183:
184: // resolve for "*"
185: Selectable sel = _context.getSelect(0);
186: if (sel instanceof ColumnIdentifier
187: && "*".equals(((ColumnIdentifier) sel).getName())) {
188: addColumnIdentifiersForStar(db, _context
189: .getResolvedSelect(), _context.getTables(),
190: (ColumnIdentifier) sel);
191: } else {
192: _context.addAllSelectToResolvedSelect();
193: }
194:
195: // resolve FROM part
196: fnVisitor.resolveFromNode(_context.getFrom(), db, _context
197: .getResolvedSelect());
198:
199: // resolve WHERE part
200: _context.setWhere(resolveSelectable(_context.getWhere(),
201: db, _context.getResolvedSelect(), _context
202: .getTables()));
203:
204: FindAggregateFunctionVisitor findAggr = new FindAggregateFunctionVisitor();
205: findAggr.visit(_context.getWhere()); // check for aggregate functions in where
206: // clause
207: if (findAggr.foundAggregateFunction()) {
208: throw new AxionException(
209: "group function is not allowed here");
210: }
211:
212: // resolve Group BY part
213: if (!_context.getGroupBy().isEmpty()) {
214: ArrayList temp = new ArrayList();
215: for (int i = 0, I = _context.getGroupByCount(); i < I; i++) {
216: Selectable gp = _context.getGroupBy(i);
217: Selectable gp1 = resolveSelectable(gp, db, _context
218: .getResolvedSelect(), _context.getTables());
219: temp.add(gp1);
220: }
221: _context.setGroupBy(temp);
222: }
223: AssertGroupByRulesVisitor gpVisitor = new AssertGroupByRulesVisitor();
224: boolean foundScalar = gpVisitor.visit(_context.getSelect(),
225: _context.getGroupBy());
226: _context.setFoundAggregateFunction(!foundScalar);
227:
228: // resolve HAVING part
229: gpVisitor = new AssertGroupByRulesVisitor();
230: _context.setHaving(resolveSelectable(_context.getHaving(),
231: db, _context.getResolvedSelect(), _context
232: .getTables()));
233: gpVisitor.visit(Collections.singletonList(_context
234: .getHaving()), _context.getGroupBy());
235:
236: // resolve ORDER BY part
237: if (!_context.getOrderBy().isEmpty()) {
238: for (int i = 0, I = _context.getOrderByCount(); i < I; i++) {
239: OrderNode ob = _context.getOrderBy(i);
240: ob
241: .setSelectable(resolveSelectable(ob
242: .getSelectable(), db, _context
243: .getResolvedSelect(), _context
244: .getTables()));
245: }
246: }
247:
248: if (!_context.getOrderBy().isEmpty()) {
249: AmbiguousColumnReferenceVisitor ambiguityCheck = new AmbiguousColumnReferenceVisitor();
250: ambiguityCheck.visit(_context.getSelect(), _context
251: .getOrderBy());
252: }
253:
254: _context.setResolved(true);
255: }
256: }
257:
258: private void addColumnIdentifiersForStar(Database db, List list,
259: TableIdentifier[] tables, ColumnIdentifier colid)
260: throws AxionException {
261:
262: if (null == colid.getTableName()) {
263: for (int j = 0, J = _context.getFromCount(); j < J; j++) {
264: list.addAll(getColIdentifierList(
265: db.getTable(tables[j]), tables[j]));
266: }
267: } else {
268: TableIdentifier tid = colid.getTableIdentifier();
269: list.addAll(getColIdentifierList(db.getTable(tid), tid));
270: }
271: }
272:
273: private Selectable[] generateSelectArrayForResultSet()
274: throws AxionException {
275: List list = _context.getResolvedSelect();
276: return (Selectable[]) (list
277: .toArray(new Selectable[list.size()]));
278: }
279:
280: private AxionResultSet makeExplainResultSet(RowIterator iter) {
281: Map map = new HashMap();
282: Selectable resultcolumn = new ColumnIdentifier(null,
283: "EXPLANATION", null, StringType.instance());
284: map.put(resultcolumn, new Integer(0));
285: RowIterator rowIter = _planner.getPlanNodeRowIterator();
286: return new AxionResultSet(new RowIteratorRowDecoratorIterator(
287: rowIter, new RowDecorator(map)),
288: new Selectable[] { resultcolumn }, null);
289: }
290:
291: //----------------------------------------------------------------- Members
292: protected AxionQueryContext _context;
293: protected Database _currentDatabase;
294: protected AxionQueryPlanner _planner;
295:
296: }
|