001: /*
002: * $Id: CreateTableCommand.java,v 1.42 2005/12/20 18:32:28 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2002-2005 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.HashSet;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Properties;
049: import java.util.Set;
050:
051: import org.axiondb.AxionCommand;
052: import org.axiondb.AxionException;
053: import org.axiondb.Column;
054: import org.axiondb.ColumnIdentifier;
055: import org.axiondb.DataType;
056: import org.axiondb.Database;
057: import org.axiondb.ExternalTable;
058: import org.axiondb.Selectable;
059: import org.axiondb.Sequence;
060: import org.axiondb.Table;
061: import org.axiondb.TableFactory;
062: import org.axiondb.TableIdentifier;
063: import org.axiondb.engine.tables.ExternalTableFactory;
064:
065: /**
066: * A <code>CREATE [<i>TYPE</i>] TABLE</code> command.
067: *
068: * @version $Revision: 1.42 $ $Date: 2005/12/20 18:32:28 $
069: * @author Chuck Burdick
070: * @author James Strachan
071: * @author Rodney Waldhoff
072: * @author Ahimanikya Satapathy
073: */
074: public class CreateTableCommand extends CreateCommand {
075:
076: public CreateTableCommand() {
077: }
078:
079: public CreateTableCommand(String tableName) {
080: setObjectName(tableName);
081: }
082:
083: public void addChildCommand(AxionCommand cmd) {
084: _childCommands.add(cmd);
085: }
086:
087: public void addColumn(String name, String datatypename) {
088: addColumn(name, datatypename, null, null, null, null);
089: }
090:
091: public void addColumn(String name, String datatypename,
092: String precision) {
093: addColumn(name, datatypename, precision, null, null, null);
094: }
095:
096: public void addColumn(String name, String datatypename,
097: String precision, String scale, Object defaultValue) {
098: addColumn(name, datatypename, precision, scale, defaultValue,
099: null);
100: }
101:
102: public void addColumn(String name, String datatypename,
103: String precision, String scale, Object defaultValue,
104: String generated) {
105: _columnNames.add(name);
106: _dataTypes.add(datatypename);
107: _defaults.add(defaultValue);
108: _columnSize.add(precision);
109: _columnScale.add(scale);
110: _generated.add(generated);
111: }
112:
113: public void alterColumn(String name, String newName,
114: Selectable newDefault, Boolean dropDefault) {
115: _alterColumnNames.add(name);
116: _newColumnNames.add(newName);
117: _newDefaults.add(newDefault);
118: _dropdefaults.add(dropDefault);
119: }
120:
121: public void excludeColumn(String colName) {
122: _excludeSourceColumns.add(_columnsAreCaseSensitive ? colName
123: : colName.toUpperCase());
124: }
125:
126: public boolean isColumnEexcluded(String colName) {
127: return _excludeSourceColumns
128: .contains(_columnsAreCaseSensitive ? colName : colName
129: .toUpperCase());
130: }
131:
132: public boolean execute(Database db) throws AxionException {
133: synchronized (CreateTableCommand.class) {
134: assertNotReadOnly(db);
135: if (!db.hasTable(getObjectName())) {
136:
137: _columnsAreCaseSensitive = Boolean
138: .valueOf(
139: (String) _tableOrganization
140: .get(ExternalTable.COLUMNS_ARE_CASE_SENSITIVE))
141: .booleanValue();
142:
143: // Handle CREATE TABLE AS
144: buildColumnsFormSourceTable(db);
145: if (_sourceTable != null && _subQuery == null) {
146: buildSubQuery();
147: }
148:
149: buildColumns(db);
150: applyAlterColumn();
151:
152: if (_sourceTable != null
153: && _excludeSourceColumns.size() == _sourceTable
154: .getColumnCount()) {
155: throw new AxionException("Can't drop last column "
156: + _excludeSourceColumns);
157: }
158:
159: TableFactory factory = db.getTableFactory(_type);
160: if (factory == null) {
161: throw new AxionException("Unknown table type ("
162: + _type + ")");
163: }
164:
165: Table table = null;
166: if (factory instanceof ExternalTableFactory) {
167: table = ((ExternalTableFactory) factory)
168: .createTable(db, getObjectName(),
169: _tableOrganization, _columns);
170: } else {
171: table = factory.createTable(db, getObjectName());
172: Iterator iter = _columns.iterator();
173: while (iter.hasNext()) {
174: table.addColumn((Column) iter.next());
175: }
176: }
177:
178: db.addTable(table);
179: try {
180: for (Iterator iter = _childCommands.iterator(); iter
181: .hasNext();) {
182: AxionCommand cmd = (AxionCommand) (iter.next());
183: cmd.execute(db);
184: }
185:
186: // persist sequences if any for the identity column.
187: if (_sequence != null) {
188: table.setSequence(_sequence);
189: }
190:
191: if (_createTableWithData) {
192: populateData(db, table);
193: }
194: } catch (AxionException e) {
195: db.dropTable(getObjectName());
196: throw e;
197: }
198:
199: } else if (!isIfNotExists()) {
200: throw new AxionException("A table/view named \""
201: + getObjectName().toUpperCase()
202: + "\" already exists.");
203: }
204: }
205: return false;
206: }
207:
208: public AxionCommand getChildCommand(int i) {
209: return (AxionCommand) (_childCommands.get(i));
210: }
211:
212: public int getChildCommandCount() {
213: return _childCommands.size();
214: }
215:
216: public List getColumnNames() {
217: return Collections.unmodifiableList(_columnNames);
218: }
219:
220: public String getType() {
221: return _type;
222: }
223:
224: public void setProperties(Properties prop) {
225: _tableOrganization = prop;
226: }
227:
228: public void setSourceTable(Table table) {
229: _sourceTable = table;
230: }
231:
232: public void setSubQuery(SubSelectCommand subQuery) {
233: _subQuery = subQuery;
234: }
235:
236: public void setType(String type) {
237: _type = type;
238: }
239:
240: public void setCreateTableWithData(boolean createTableWithData) {
241: _createTableWithData = createTableWithData;
242: }
243:
244: private void buildColumns(Database db) throws AxionException {
245: Set existingNames = new HashSet(_columnNames.size());
246: for (int i = 0, I = _columnNames.size(); i < I; i++) {
247: String typeStr = (String) _dataTypes.get(i);
248: String generated = (String) _generated.get(i);
249:
250: // If the column is derived then set the column to StringType...
251: if (typeStr == null && generated != null
252: && generated.equals(Column.GENERATED_ALWAYS)) {
253: typeStr = "string";
254: }
255:
256: DataType type = db.getDataType(typeStr);
257: if (null == type) {
258: try {
259: type = (DataType) (Class
260: .forName((String) (_dataTypes.get(i)))
261: .newInstance());
262: } catch (Exception e) {
263: type = null;
264: }
265: } else {
266: // Clone this instance of datatype so that its precision and scale can be
267: // customized
268: type = type.makeNewInstance();
269: }
270:
271: if (null == type) {
272: throw new AxionException("Type " + _dataTypes.get(i)
273: + " not recognized.");
274: }
275:
276: String normalizedColumnName = _columnsAreCaseSensitive ? (String) _columnNames
277: .get(i)
278: : ((String) _columnNames.get(i)).toUpperCase();
279: if (existingNames.contains(normalizedColumnName)) {
280: throw new AxionException("Duplicate column name "
281: + _columnNames.get(i) + ".");
282: }
283: existingNames.add(normalizedColumnName);
284:
285: Selectable defaultValue = null;
286: CreateSequenceCommand createSeqCmd = null;
287: Object def = _defaults.get(i);
288: if (def instanceof Selectable) {
289: defaultValue = (Selectable) def;
290: } else if (def instanceof CreateSequenceCommand) {
291: if (_sequence == null) {
292: createSeqCmd = (CreateSequenceCommand) def;
293: _sequence = createSeqCmd.createSequence(db);
294: } else {
295: throw new AxionException(
296: "Can't have more than one Identity columns");
297: }
298: }
299:
300: String sizeSpec = (String) _columnSize.get(i);
301: if (null != sizeSpec
302: && type instanceof DataType.NonFixedPrecision) {
303: try {
304: Integer size = new Integer(sizeSpec);
305: ((DataType.NonFixedPrecision) type)
306: .setPrecision(size.intValue());
307: } catch (NumberFormatException e) {
308: // ignore, use default
309: }
310:
311: String scaleSpec = (String) _columnScale.get(i);
312: if (null != scaleSpec
313: && type instanceof DataType.ExactNumeric) {
314: try {
315: Integer scale = new Integer(scaleSpec);
316: ((DataType.ExactNumeric) type).setScale(scale
317: .intValue());
318: } catch (NumberFormatException e) {
319: // ignore, use default
320: }
321: }
322: }
323:
324: Column col = new Column((String) _columnNames.get(i), type,
325: defaultValue);
326: col.setSqlType((String) _dataTypes.get(i));
327:
328: if (createSeqCmd != null) {
329: col.setIdentityType(createSeqCmd.getIdentityType());
330: }
331:
332: if (generated != null
333: && generated.equals(Column.GENERATED_ALWAYS)) {
334: col.setGeneratedColType(generated);
335: }
336:
337: _columns.add(col);
338: }
339: }
340:
341: private void applyAlterColumn() throws AxionException {
342: TableIdentifier trgtTid = new TableIdentifier(getObjectName());
343: for (int i = 0, I = _columns.size(), K = _alterColumnNames
344: .size(); i < I && K > 0; i++) {
345: Column col = (Column) _columns.get(i);
346: if (!col.isDerivedColumn() && !col.isIdentityColumn()) {
347: for (int j = 0, J = _alterColumnNames.size(); j < J; j++) {
348: if (col.getName().equals(_alterColumnNames.get(j))) {
349: String newColName = _newColumnNames.get(j) == null ? col
350: .getName()
351: : (String) _newColumnNames.get(j);
352: Selectable newDefault = _newDefaults.get(j) == null ? col
353: .getDefault()
354: : (Selectable) _newDefaults.get(j);
355: newDefault = _dropdefaults.get(j) == null ? newDefault
356: : null;
357: Column newCol = new Column(newColName, col
358: .getDataType(), newDefault);
359: newCol.setGeneratedColType(col
360: .getGeneratedColType());
361: newCol.setIdentityType(col.getIdentityType());
362: newCol.setSqlType(col.getSqlType());
363: _alterColumnNames.remove(j);
364:
365: _columns.set(i, newCol);
366: ColumnIdentifier colid = new ColumnIdentifier(
367: trgtTid, col.getName(), null, col
368: .getDataType());
369: ColumnIdentifier newcolid = new ColumnIdentifier(
370: trgtTid, newCol.getName(), null, newCol
371: .getDataType());
372: _colIdsForInsert.set(_colIdsForInsert
373: .indexOf(colid), newcolid);
374: }
375: }
376: } else {
377: throw new AxionException(
378: "Can't rename Generated columns");
379: }
380: }
381: }
382:
383: private void buildColumnsFormSourceTable(Database db)
384: throws AxionException {
385: // Create table from view, used in CREATE TABLE AS VIEW command
386: if (_subQuery != null) {
387: _sourceTable = _subQuery.getTableView(db, null);
388: }
389:
390: // resolve columns to be droped, used in ALTER TABLE DROP COLUMN
391: if (_sourceTable != null) {
392: Iterator iter = _excludeSourceColumns.iterator();
393: while (iter.hasNext()) {
394: String columnName = (String) iter.next();
395: Column col = _sourceTable.getColumn(columnName);
396: if (col == null) {
397: throw new AxionException("Column not found "
398: + columnName);
399: }
400: }
401:
402: // check for duplicate column name
403: iter = _columnNames.iterator();
404: while (iter.hasNext()) {
405: String columnName = (String) iter.next();
406: Column col = _sourceTable.getColumn(columnName);
407: if (col != null) {
408: throw new AxionException("Column Already Exists "
409: + columnName);
410: }
411: }
412:
413: // Add column definitions from the source Table/View to new table
414: TableIdentifier trgtTid = new TableIdentifier(
415: getObjectName());
416: for (int i = 0, count = _sourceTable.getColumnCount(); i < count; i++) {
417: Column col = _sourceTable.getColumn(i);
418: if (!_excludeSourceColumns
419: .contains(_columnsAreCaseSensitive ? col
420: .getName() : col.getName()
421: .toUpperCase())) {
422: _columns.add(col);
423:
424: if (!col.isDerivedColumn()) {
425: ColumnIdentifier colid = new ColumnIdentifier(
426: trgtTid, col.getName(), null, col
427: .getDataType());
428: _colIdsForInsert.add(colid);
429: }
430: }
431: }
432: }
433: }
434:
435: private void buildSubQuery() {
436: AxionQueryContext ctx = new AxionQueryContext();
437: TableIdentifier srctid = new TableIdentifier(_sourceTable
438: .getName());
439: ctx.addFrom(srctid);
440: Iterator iter = _columns.iterator();
441: while (iter.hasNext()) {
442: Column col = (Column) iter.next();
443: if (!col.isDerivedColumn()) {
444: ColumnIdentifier colid = new ColumnIdentifier(srctid,
445: col.getName(), null, col.getDataType());
446: ctx.addSelect(colid);
447: }
448: }
449:
450: _subQuery = new SubSelectCommand(ctx);
451: }
452:
453: private void populateData(Database db, Table table)
454: throws AxionException {
455: if (_sourceTable != null) {
456: // prepare insert select command and add as a child
457: TableIdentifier tid = new TableIdentifier(table.getName());
458: InsertCommand insertSelectCmd = new InsertCommand(tid,
459: _colIdsForInsert, _subQuery);
460: insertSelectCmd.execute(db);
461: }
462: }
463:
464: private List _childCommands = new ArrayList();
465: private List _colIdsForInsert = new ArrayList();
466: private List _columnNames = new ArrayList();
467:
468: private List _columns = new ArrayList();
469: private List _columnScale = new ArrayList();
470: private Sequence _sequence;
471:
472: private List _alterColumnNames = new ArrayList();
473: private List _newColumnNames = new ArrayList();
474: private List _newDefaults = new ArrayList();
475: private List _dropdefaults = new ArrayList();
476:
477: private List _columnSize = new ArrayList(); // precision
478: private List _dataTypes = new ArrayList();
479: private List _defaults = new ArrayList();
480: private List _generated = new ArrayList();
481: private Set _excludeSourceColumns = new HashSet();
482: private Table _sourceTable;
483: private SubSelectCommand _subQuery;
484: private Properties _tableOrganization = new Properties();
485: private String _type;
486: private boolean _createTableWithData = true;
487: private boolean _columnsAreCaseSensitive = false;
488: }
|