001: /**
002: * com.mckoi.database.interpret.CreateTable 14 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.util.IntegerVector;
027: import java.util.Vector;
028: import java.util.ArrayList;
029: import java.util.List;
030:
031: /**
032: * A parsed state container for the 'create' statement.
033: *
034: * @author Tobias Downer
035: */
036:
037: public class CreateTable extends Statement {
038:
039: /**
040: * Set to true if this create statement is for a temporary table.
041: */
042: boolean temporary = false;
043:
044: /**
045: * Only create if table doesn't exist.
046: */
047: boolean only_if_not_exists = false;
048:
049: /**
050: * The name of the table to create.
051: */
052: String table_name;
053:
054: /**
055: * List of column declarations (ColumnDef)
056: */
057: ArrayList columns;
058:
059: /**
060: * List of table constraints (ConstraintDef)
061: */
062: ArrayList constraints;
063:
064: // /**
065: // * The expression that must be evaluated to true for this row to be
066: // * added to the table.
067: // */
068: // Expression check_exp;
069:
070: /**
071: * The TableName object.
072: */
073: private TableName tname;
074:
075: // /**
076: // * Adds a new ColumnDef object to this create statement. A ColumnDef
077: // * object describes a column for the new table we are creating. The column's
078: // * must be added in the order they are to be in the created table.
079: // */
080: // void addColumnDef(ColumnDef column) {
081: // columns.addElement(column);
082: // }
083:
084: /**
085: * Adds a new ConstraintDef object to this create statement. A ConstraintDef
086: * object describes any constraints for the new table we are creating.
087: */
088: void addConstraintDef(ConstraintDef constraint) {
089: constraints.add(constraint);
090: }
091:
092: // /**
093: // * Handles the create statement 'CHECK' expression for compatibility.
094: // */
095: // void addCheckConstraint(Expression check_expression) {
096: // ConstraintDef constraint = new ConstraintDef();
097: // constraint.setCheck(check_expression);
098: // constraints.addElement(constraint);
099: // }
100:
101: /**
102: * Creates a DataTableDef that describes the table that was defined by
103: * this create statement. This is used by the 'alter' statement.
104: */
105: DataTableDef createDataTableDef() throws DatabaseException {
106: // Make all this information into a DataTableDef object...
107: DataTableDef table_def = new DataTableDef();
108: table_def.setTableName(tname);
109: table_def
110: .setTableClass("com.mckoi.database.VariableSizeDataTableFile");
111:
112: // Add the columns.
113: // NOTE: Any duplicate column names will be found here...
114: for (int i = 0; i < columns.size(); ++i) {
115: DataTableColumnDef cd = (DataTableColumnDef) columns.get(i);
116: table_def.addColumn(cd);
117: }
118:
119: return table_def;
120: }
121:
122: /**
123: * Adds a schema constraint to the rules for the schema represented by the
124: * manager.
125: */
126: static void addSchemaConstraint(DatabaseConnection manager,
127: TableName table, ConstraintDef constraint)
128: throws DatabaseException {
129: if (constraint.type == ConstraintDef.PRIMARY_KEY) {
130: manager.addPrimaryKeyConstraint(table, constraint
131: .getColumnList(), constraint.deferred,
132: constraint.name);
133: } else if (constraint.type == ConstraintDef.FOREIGN_KEY) {
134: // Currently we forbid referencing a table in another schema
135: TableName ref_table = TableName
136: .resolve(constraint.reference_table_name);
137: String update_rule = constraint.getUpdateRule()
138: .toUpperCase();
139: String delete_rule = constraint.getDeleteRule()
140: .toUpperCase();
141: if (table.getSchema().equals(ref_table.getSchema())) {
142: manager.addForeignKeyConstraint(table, constraint
143: .getColumnList(), ref_table, constraint
144: .getColumnList2(), delete_rule, update_rule,
145: constraint.deferred, constraint.name);
146: } else {
147: throw new DatabaseException(
148: "Foreign key reference error: "
149: + "Not permitted to reference a table outside of the schema: "
150: + table + " -> " + ref_table);
151: }
152: } else if (constraint.type == ConstraintDef.UNIQUE) {
153: manager.addUniqueConstraint(table, constraint
154: .getColumnList(), constraint.deferred,
155: constraint.name);
156: } else if (constraint.type == ConstraintDef.CHECK) {
157: manager.addCheckConstraint(table,
158: constraint.original_check_expression,
159: constraint.deferred, constraint.name);
160: } else {
161: throw new DatabaseException("Unrecognized constraint type.");
162: }
163: }
164:
165: /**
166: * Returns a com.mckoi.database.interpret.ColumnDef object a a
167: * com.mckoi.database.DataTableColumnDef object.
168: */
169: static DataTableColumnDef convertColumnDef(ColumnDef cdef) {
170: TType type = cdef.type;
171:
172: DataTableColumnDef dtcdef = new DataTableColumnDef();
173: dtcdef.setName(cdef.name);
174: dtcdef.setNotNull(cdef.isNotNull());
175: dtcdef.setFromTType(type);
176:
177: if (cdef.index_str != null) {
178: dtcdef.setIndexScheme(cdef.index_str);
179: }
180: if (cdef.default_expression != null) {
181: dtcdef
182: .setDefaultExpression(cdef.original_default_expression);
183: }
184:
185: dtcdef.initTTypeInfo();
186: return dtcdef;
187: }
188:
189: /**
190: * Sets up all constraints specified in this create statement.
191: */
192: void setupAllConstraints() throws DatabaseException {
193: for (int i = 0; i < constraints.size(); ++i) {
194: ConstraintDef constraint = (ConstraintDef) constraints
195: .get(i);
196:
197: // Add this to the schema manager tables
198: addSchemaConstraint(database, tname, constraint);
199: }
200: }
201:
202: // ---------- Implemented from Statement ----------
203:
204: public void prepare() throws DatabaseException {
205:
206: // Get the state from the model
207: temporary = cmd.getBoolean("temporary");
208: only_if_not_exists = cmd.getBoolean("only_if_not_exists");
209: table_name = (String) cmd.getObject("table_name");
210: ArrayList column_list = (ArrayList) cmd
211: .getObject("column_list");
212: constraints = (ArrayList) cmd.getObject("constraint_list");
213:
214: // Convert column_list to list of com.mckoi.database.DataTableColumnDef
215: int size = column_list.size();
216: columns = new ArrayList(size);
217: for (int i = 0; i < size; ++i) {
218: ColumnDef cdef = (ColumnDef) column_list.get(i);
219: columns.add(convertColumnDef(cdef));
220: }
221:
222: // ----
223:
224: String schema_name = database.getCurrentSchema();
225: tname = TableName.resolve(schema_name, table_name);
226:
227: String name_strip = tname.getName();
228:
229: if (name_strip.indexOf('.') != -1) {
230: throw new DatabaseException(
231: "Table name can not contain '.' character.");
232: }
233:
234: final boolean ignores_case = database.isInCaseInsensitiveMode();
235:
236: // Implement the checker class for this statement.
237: ColumnChecker checker = new ColumnChecker() {
238:
239: String resolveColumnName(String col_name)
240: throws DatabaseException {
241: // We need to do case sensitive and case insensitive resolution,
242: String found_col = null;
243: for (int n = 0; n < columns.size(); ++n) {
244: DataTableColumnDef col = (DataTableColumnDef) columns
245: .get(n);
246: if (!ignores_case) {
247: if (col.getName().equals(col_name)) {
248: return col_name;
249: }
250: } else {
251: if (col.getName().equalsIgnoreCase(col_name)) {
252: if (found_col != null) {
253: throw new DatabaseException(
254: "Ambiguous column name '"
255: + col_name + "'");
256: }
257: found_col = col.getName();
258: }
259: }
260: }
261: return found_col;
262: }
263:
264: };
265:
266: ArrayList unique_column_list = new ArrayList();
267: ArrayList primary_key_column_list = new ArrayList();
268:
269: // Check the expressions that represent the default values for the columns.
270: // Also check each column name
271: for (int i = 0; i < columns.size(); ++i) {
272: DataTableColumnDef cdef = (DataTableColumnDef) columns
273: .get(i);
274: ColumnDef model_cdef = (ColumnDef) column_list.get(i);
275: checker.checkExpression(cdef.getDefaultExpression(database
276: .getSystem()));
277: String col_name = cdef.getName();
278: // If column name starts with [table_name]. then strip it off
279: cdef.setName(checker.stripTableName(name_strip, col_name));
280: // If unique then add to unique columns
281: if (model_cdef.isUnique()) {
282: unique_column_list.add(col_name);
283: }
284: // If primary key then add to primary key columns
285: if (model_cdef.isPrimaryKey()) {
286: primary_key_column_list.add(col_name);
287: }
288: }
289:
290: // Add the unique and primary key constraints.
291: if (unique_column_list.size() > 0) {
292: ConstraintDef constraint = new ConstraintDef();
293: constraint.setUnique(unique_column_list);
294: addConstraintDef(constraint);
295: }
296: if (primary_key_column_list.size() > 0) {
297: ConstraintDef constraint = new ConstraintDef();
298: constraint.setPrimaryKey(primary_key_column_list);
299: addConstraintDef(constraint);
300: }
301:
302: // Strip the column names and set the expression in all the constraints.
303: for (int i = 0; i < constraints.size(); ++i) {
304: ConstraintDef constraint = (ConstraintDef) constraints
305: .get(i);
306: checker.stripColumnList(name_strip, constraint.column_list);
307: // Check the referencing table for foreign keys
308: if (constraint.type == ConstraintDef.FOREIGN_KEY) {
309: checker.stripColumnList(
310: constraint.reference_table_name,
311: constraint.column_list2);
312: TableName ref_tname = resolveTableName(
313: constraint.reference_table_name, database);
314: if (database.isInCaseInsensitiveMode()) {
315: ref_tname = database.tryResolveCase(ref_tname);
316: }
317: constraint.reference_table_name = ref_tname.toString();
318:
319: DataTableDef ref_table_def;
320: if (database.tableExists(ref_tname)) {
321: // Get the DataTableDef for the table we are referencing
322: ref_table_def = database.getDataTableDef(ref_tname);
323: } else if (ref_tname.equals(tname)) {
324: // We are referencing the table we are creating
325: ref_table_def = createDataTableDef();
326: } else {
327: throw new DatabaseException("Referenced table '"
328: + ref_tname + "' in constraint '"
329: + constraint.name + "' does not exist.");
330: }
331: // Resolve columns against the given table def
332: ref_table_def.resolveColumnsInArray(database,
333: constraint.column_list2);
334:
335: }
336: checker.checkExpression(constraint.check_expression);
337: checker.checkColumnList(constraint.column_list);
338: }
339:
340: }
341:
342: public Table evaluate() throws DatabaseException {
343:
344: DatabaseQueryContext context = new DatabaseQueryContext(
345: database);
346:
347: // Does the schema exist?
348: boolean ignore_case = database.isInCaseInsensitiveMode();
349: SchemaDef schema = database.resolveSchemaCase(
350: tname.getSchema(), ignore_case);
351: if (schema == null) {
352: throw new DatabaseException("Schema '" + tname.getSchema()
353: + "' doesn't exist.");
354: } else {
355: tname = new TableName(schema.getName(), tname.getName());
356: }
357:
358: // Does the user have privs to create this tables?
359: if (!database.getDatabase().canUserCreateTableObject(context,
360: user, tname)) {
361: throw new UserAccessException(
362: "User not permitted to create table: " + table_name);
363: }
364:
365: // PENDING: Creation of temporary tables...
366:
367: // Does the table already exist?
368: if (!database.tableExists(tname)) {
369:
370: // Create the data table definition and tell the database to create
371: // it.
372: DataTableDef table_def = createDataTableDef();
373: database.createTable(table_def);
374:
375: // The initial grants for a table is to give the user who created it
376: // full access.
377: database.getGrantManager().addGrant(
378: Privileges.TABLE_ALL_PRIVS, GrantManager.TABLE,
379: tname.toString(), user.getUserName(), true,
380: Database.INTERNAL_SECURE_USERNAME);
381:
382: // Set the constraints in the schema.
383: setupAllConstraints();
384:
385: // Return '0' if we created the table. (0 rows affected)
386: return FunctionTable.resultTable(context, 0);
387: }
388:
389: // Report error unless 'if not exists' command is in the statement.
390: if (only_if_not_exists == false) {
391: throw new DatabaseException("Table '" + tname
392: + "' already exists.");
393: }
394:
395: // Return '0' (0 rows affected). This happens when we don't create a
396: // table (because it exists) and the 'IF NOT EXISTS' clause is present.
397: return FunctionTable.resultTable(context, 0);
398:
399: }
400:
401: }
|