001: /**
002: * com.mckoi.database.interpret.ViewManager 24 Aug 2002
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 java.util.ArrayList;
027: import java.util.List;
028:
029: /**
030: * Handler for creating and dropping views in the database.
031: *
032: * @author Tobias Downer
033: */
034:
035: public class ViewManager extends Statement {
036:
037: /**
038: * The type of command we are running through this ViewManager.
039: */
040: private String type;
041:
042: /**
043: * The view name to create/drop.
044: */
045: private String view_name;
046:
047: /**
048: * The view name as a TableName object.
049: */
050: private TableName vname;
051:
052: /**
053: * If this is a create command, the TableSelectExpression that forms the view.
054: */
055: private TableSelectExpression select_expression;
056:
057: /**
058: * If this is a create command, the QueryPlanNode that represents the view
059: * plan.
060: */
061: private QueryPlanNode plan;
062:
063: // ---------- Implemented from Statement ----------
064:
065: public void prepare() throws DatabaseException {
066: type = (String) cmd.getObject("type");
067: view_name = (String) cmd.getObject("view_name");
068:
069: String schema_name = database.getCurrentSchema();
070: vname = TableName.resolve(schema_name, view_name);
071: vname = database.tryResolveCase(vname);
072:
073: if (type.equals("create")) {
074: // Get the select expression
075: select_expression = (TableSelectExpression) cmd
076: .getObject("select_expression");
077: // Get the column name list
078: ArrayList col_list = (ArrayList) cmd
079: .getObject("column_list");
080:
081: // Generate the TableExpressionFromSet hierarchy for the expression,
082: TableExpressionFromSet from_set = Planner.generateFromSet(
083: select_expression, database);
084: // Form the plan
085: plan = Planner.formQueryPlan(database, select_expression,
086: from_set, new ArrayList());
087:
088: // Wrap the result around a SubsetNode to alias the columns in the
089: // table correctly for this view.
090: int sz = (col_list == null) ? 0 : col_list.size();
091: Variable[] original_vars = from_set
092: .generateResolvedVariableList();
093: Variable[] new_column_vars = new Variable[original_vars.length];
094:
095: if (sz > 0) {
096: if (sz != original_vars.length) {
097: throw new StatementException(
098: "Column list is not the same size as the columns selected.");
099: }
100: for (int i = 0; i < sz; ++i) {
101: String col_name = (String) col_list.get(i);
102: new_column_vars[i] = new Variable(vname, col_name);
103: }
104: } else {
105: sz = original_vars.length;
106: for (int i = 0; i < sz; ++i) {
107: new_column_vars[i] = new Variable(vname,
108: original_vars[i].getName());
109: }
110: }
111:
112: // Check there are no repeat column names in the table.
113: for (int i = 0; i < sz; ++i) {
114: Variable cur_v = new_column_vars[i];
115: for (int n = i + 1; n < sz; ++n) {
116: if (new_column_vars[n].equals(cur_v)) {
117: throw new DatabaseException(
118: "Duplicate column name '"
119: + cur_v
120: + "' in view. "
121: + "A view may not contain duplicate column names.");
122: }
123: }
124: }
125:
126: // Wrap the plan around a SubsetNode plan
127: plan = new QueryPlan.SubsetNode(plan, original_vars,
128: new_column_vars);
129:
130: }
131:
132: }
133:
134: public Table evaluate() throws DatabaseException {
135:
136: DatabaseQueryContext context = new DatabaseQueryContext(
137: database);
138:
139: if (type.equals("create")) {
140: // Does the user have privs to create this tables?
141: if (!database.getDatabase().canUserCreateTableObject(
142: context, user, vname)) {
143: throw new UserAccessException(
144: "User not permitted to create view: "
145: + view_name);
146: }
147:
148: // Does the schema exist?
149: boolean ignore_case = database.isInCaseInsensitiveMode();
150: SchemaDef schema = database.resolveSchemaCase(vname
151: .getSchema(), ignore_case);
152: if (schema == null) {
153: throw new DatabaseException("Schema '"
154: + vname.getSchema() + "' doesn't exist.");
155: } else {
156: vname = new TableName(schema.getName(), vname.getName());
157: }
158:
159: // Check the permissions for this user to select from the tables in the
160: // given plan.
161: Select.checkUserSelectPermissions(context, user, plan);
162:
163: // Does the table already exist?
164: if (database.tableExists(vname)) {
165: throw new DatabaseException("View or table with name '"
166: + vname + "' already exists.");
167: }
168:
169: // Before evaluation, make a clone of the plan,
170: QueryPlanNode plan_copy;
171: try {
172: plan_copy = (QueryPlanNode) plan.clone();
173: } catch (CloneNotSupportedException e) {
174: Debug().writeException(e);
175: throw new DatabaseException("Clone error: "
176: + e.getMessage());
177: }
178:
179: // We have to execute the plan to get the DataTableDef that represents the
180: // result of the view execution.
181: Table t = plan.evaluate(context);
182: DataTableDef data_table_def = new DataTableDef(t
183: .getDataTableDef());
184: data_table_def.setTableName(vname);
185:
186: // Create a ViewDef object,
187: ViewDef view_def = new ViewDef(data_table_def, plan_copy);
188:
189: // And create the view object,
190: database.createView(query, view_def);
191:
192: // The initial grants for a view is to give the user who created it
193: // full access.
194: database.getGrantManager().addGrant(
195: Privileges.TABLE_ALL_PRIVS, GrantManager.TABLE,
196: vname.toString(), user.getUserName(), true,
197: Database.INTERNAL_SECURE_USERNAME);
198:
199: } else if (type.equals("drop")) {
200:
201: // Does the user have privs to drop this tables?
202: if (!database.getDatabase().canUserDropTableObject(context,
203: user, vname)) {
204: throw new UserAccessException(
205: "User not permitted to drop view: " + view_name);
206: }
207:
208: // Drop the view object
209: database.dropView(vname);
210:
211: // Drop the grants for this object
212: database.getGrantManager().revokeAllGrantsOnObject(
213: GrantManager.TABLE, vname.toString());
214:
215: } else {
216: throw new Error("Unknown view command type: " + type);
217: }
218:
219: return FunctionTable.resultTable(context, 0);
220: }
221:
222: }
|