001: /**
002: * com.mckoi.database.interpret.Select 09 Sep 2001
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.database.interpret;
024:
025: import com.mckoi.database.*;
026: import com.mckoi.debug.*;
027: import com.mckoi.util.IntegerVector;
028: import java.util.Set;
029: import java.util.List;
030: import java.util.Vector;
031: import java.util.Iterator;
032: import java.util.ArrayList;
033: import java.util.Collections;
034:
035: /**
036: * Logic for interpreting an SQL SELECT statement.
037: *
038: * @author Tobias Downer
039: */
040:
041: public class Select extends Statement {
042:
043: /**
044: * The TableSelectExpression representing the select query itself.
045: */
046: private TableSelectExpression select_expression;
047:
048: /**
049: * The list of all columns to order by. (ByColumn)
050: */
051: private ArrayList order_by;
052:
053: /**
054: * The list of columns in the 'order_by' clause fully resolved.
055: */
056: private Variable[] order_cols;
057:
058: /**
059: * The plan for evaluating this select expression.
060: */
061: private QueryPlanNode plan;
062:
063: /**
064: * Checks the permissions for this user to determine if they are allowed to
065: * select (read) from tables in this plan. If the user is not allowed to
066: * select from a table in the plan, a UserAccessException is thrown. This is
067: * a static method.
068: */
069: static final void checkUserSelectPermissions(
070: DatabaseQueryContext context, User user, QueryPlanNode plan)
071: throws UserAccessException, DatabaseException {
072:
073: // Discover the list of TableName objects this query touches,
074: ArrayList touched_tables = plan
075: .discoverTableNames(new ArrayList());
076: Database dbase = context.getDatabase();
077: // Check that the user is allowed to select from these tables.
078: for (int i = 0; i < touched_tables.size(); ++i) {
079: TableName t = (TableName) touched_tables.get(i);
080: if (!dbase.canUserSelectFromTableObject(context, user, t,
081: null)) {
082: throw new UserAccessException(
083: "User not permitted to select from table: " + t);
084: }
085: }
086: }
087:
088: /**
089: * Prepares the select statement with a Database object. This sets up
090: * internal state so that it correctly maps to a database. Also, this
091: * checks format to ensure there are no run-time syntax problems. This must
092: * be called because we 'evaluate' the statement.
093: * <p>
094: * NOTE: Care must be taken to ensure that all methods called here are safe
095: * in as far as modifications to the data occuring. The rules for
096: * safety should be as follows. If the database is in EXCLUSIVE mode,
097: * then we need to wait until it's switched back to SHARED mode
098: * before this method is called.
099: * All collection of information done here should not involve any table
100: * state info. except for column count, column names, column types, etc.
101: * Queries such as obtaining the row count, selectable scheme information,
102: * and certainly 'getCellContents' must never be called during prepare.
103: * When prepare finishes, the affected tables are locked and the query ia
104: * safe to 'evaluate' at which time table state is safe to inspect.
105: */
106: public void prepare() throws DatabaseException {
107: DatabaseConnection db = database;
108:
109: // Prepare this object from the StatementTree,
110: // The select expression itself
111: select_expression = (TableSelectExpression) cmd
112: .getObject("table_expression");
113: // The order by information
114: order_by = (ArrayList) cmd.getObject("order_by");
115:
116: // Generate the TableExpressionFromSet hierarchy for the expression,
117: TableExpressionFromSet from_set = Planner.generateFromSet(
118: select_expression, db);
119:
120: // Form the plan
121: plan = Planner.formQueryPlan(db, select_expression, from_set,
122: order_by);
123:
124: }
125:
126: /**
127: * Evaluates the select statement with the given Database context.
128: */
129: public Table evaluate() throws DatabaseException {
130:
131: DatabaseQueryContext context = new DatabaseQueryContext(
132: database);
133:
134: // Check the permissions for this user to select from the tables in the
135: // given plan.
136: checkUserSelectPermissions(context, user, plan);
137:
138: boolean error = true;
139: try {
140: Table t = plan.evaluate(context);
141: error = false;
142: return t;
143: } finally {
144: // If an error occured, dump the query plan to the debug log.
145: // Or just dump the query plan if debug level = INFORMATION
146: if (Debug().isInterestedIn(Lvl.INFORMATION)
147: || (error && Debug().isInterestedIn(Lvl.WARNING))) {
148: StringBuffer buf = new StringBuffer();
149: plan.debugString(0, buf);
150:
151: Debug().write(Lvl.WARNING, this ,
152: "Query Plan debug:\n" + buf.toString());
153: }
154: }
155:
156: }
157:
158: /**
159: * Outputs information for debugging.
160: */
161: public String toString() {
162: StringBuffer buf = new StringBuffer();
163: buf.append("[ SELECT: expression=");
164: buf.append(select_expression.toString());
165: buf.append(" ORDER_BY=");
166: buf.append(order_by);
167: buf.append(" ]");
168: return new String(buf);
169: }
170:
171: }
|