001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.CreateTableNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.reference.Property;
025: import org.apache.derby.iapi.reference.SQLState;
026: import org.apache.derby.iapi.reference.Limits;
027:
028: import org.apache.derby.iapi.services.property.PropertyUtil;
029: import org.apache.derby.iapi.services.sanity.SanityManager;
030:
031: import org.apache.derby.iapi.sql.execute.ConstantAction;
032:
033: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
034: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
035: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
036:
037: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
038:
039: import org.apache.derby.iapi.error.StandardException;
040:
041: import org.apache.derby.impl.sql.execute.ColumnInfo;
042: import org.apache.derby.impl.sql.execute.CreateConstraintConstantAction;
043: import org.apache.derby.iapi.types.DataTypeDescriptor;
044: import java.util.Properties;
045:
046: /**
047: * A CreateTableNode is the root of a QueryTree that represents a CREATE TABLE or DECLARE GLOBAL TEMPORARY TABLE
048: * statement.
049: *
050: * @author Jeff Lichtman
051: */
052:
053: public class CreateTableNode extends DDLStatementNode {
054: private char lockGranularity;
055: private boolean onCommitDeleteRows; //If true, on commit delete rows else on commit preserve rows of temporary table.
056: private boolean onRollbackDeleteRows; //If true, on rollback delete rows from temp table if it was logically modified in that UOW. true is the only supported value
057: private Properties properties;
058: private TableElementList tableElementList;
059: protected int tableType; //persistent table or global temporary table
060:
061: /**
062: * Initializer for a CreateTableNode for a base table
063: *
064: * @param newObjectName The name of the new object being created (ie base table)
065: * @param tableElementList The elements of the table: columns,
066: * constraints, etc.
067: * @param properties The optional list of properties associated with
068: * the table.
069: * @param lockGranularity The lock granularity.
070: *
071: * @exception StandardException Thrown on error
072: */
073:
074: public void init(Object newObjectName, Object tableElementList,
075: Object properties, Object lockGranularity)
076: throws StandardException {
077: tableType = TableDescriptor.BASE_TABLE_TYPE;
078: this .lockGranularity = ((Character) lockGranularity)
079: .charValue();
080: implicitCreateSchema = true;
081:
082: if (SanityManager.DEBUG) {
083: if (this .lockGranularity != TableDescriptor.TABLE_LOCK_GRANULARITY
084: && this .lockGranularity != TableDescriptor.ROW_LOCK_GRANULARITY) {
085: SanityManager
086: .THROWASSERT("Unexpected value for lockGranularity = "
087: + this .lockGranularity);
088: }
089: }
090:
091: initAndCheck(newObjectName);
092: this .tableElementList = (TableElementList) tableElementList;
093: this .properties = (Properties) properties;
094: }
095:
096: /**
097: * Initializer for a CreateTableNode for a global temporary table
098: *
099: * @param newObjectName The name of the new object being declared (ie temporary table)
100: * @param tableElementList The elements of the table: columns,
101: * constraints, etc.
102: * @param properties The optional list of properties associated with
103: * the table.
104: * @param onCommitDeleteRows If true, on commit delete rows else on commit preserve rows of temporary table.
105: * @param onRollbackDeleteRows If true, on rollback, delete rows from temp tables which were logically modified. true is the only supported value
106: *
107: * @exception StandardException Thrown on error
108: */
109:
110: public void init(Object newObjectName, Object tableElementList,
111: Object properties, Object onCommitDeleteRows,
112: Object onRollbackDeleteRows) throws StandardException {
113: tableType = TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE;
114: newObjectName = tempTableSchemaNameCheck(newObjectName);
115: this .onCommitDeleteRows = ((Boolean) onCommitDeleteRows)
116: .booleanValue();
117: this .onRollbackDeleteRows = ((Boolean) onRollbackDeleteRows)
118: .booleanValue();
119: initAndCheck(newObjectName);
120: this .tableElementList = (TableElementList) tableElementList;
121: this .properties = (Properties) properties;
122:
123: if (SanityManager.DEBUG) {
124: if (this .onRollbackDeleteRows == false) {
125: SanityManager
126: .THROWASSERT("Unexpected value for onRollbackDeleteRows = "
127: + this .onRollbackDeleteRows);
128: }
129: }
130: }
131:
132: /**
133: * If no schema name specified for global temporary table, SESSION is the implicit schema.
134: * Otherwise, make sure the specified schema name for global temporary table is SESSION.
135: * @param objectName The name of the new object being declared (ie temporary table)
136: */
137: private Object tempTableSchemaNameCheck(Object objectName)
138: throws StandardException {
139: TableName tempTableName = (TableName) objectName;
140: if (tempTableName != null) {
141: if (tempTableName.getSchemaName() == null)
142: tempTableName
143: .setSchemaName(SchemaDescriptor.STD_DECLARED_GLOBAL_TEMPORARY_TABLES_SCHEMA_NAME); //If no schema specified, SESSION is the implicit schema.
144: else if (!(isSessionSchema(tempTableName.getSchemaName())))
145: throw StandardException
146: .newException(SQLState.LANG_DECLARED_GLOBAL_TEMP_TABLE_ONLY_IN_SESSION_SCHEMA);
147: }
148: return (tempTableName);
149: }
150:
151: /**
152: * Convert this object to a String. See comments in QueryTreeNode.java
153: * for how this should be done for tree printing.
154: *
155: * @return This object as a String
156: */
157:
158: public String toString() {
159: if (SanityManager.DEBUG) {
160: String tempString = "tableElementList: " + "\n"
161: + tableElementList + "\n";
162: if (tableType == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) {
163: tempString = tempString + "onCommitDeleteRows: " + "\n"
164: + onCommitDeleteRows + "\n";
165: tempString = tempString + "onRollbackDeleteRows: "
166: + "\n" + onRollbackDeleteRows + "\n";
167: } else
168: tempString = tempString + "properties: " + "\n"
169: + properties + "\n" + "lockGranularity: "
170: + "\n" + lockGranularity + "\n";
171: return super .toString() + tempString;
172: } else {
173: return "";
174: }
175: }
176:
177: public String statementToString() {
178: if (tableType == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE)
179: return "DECLARE GLOBAL TEMPORARY TABLE";
180: else
181: return "CREATE TABLE";
182: }
183:
184: // We inherit the generate() method from DDLStatementNode.
185:
186: /**
187: * Bind this CreateTableNode. This means doing any static error checking that can be
188: * done before actually creating the base table or declaring the global temporary table.
189: * For eg, verifying that the TableElementList does not contain any duplicate column names.
190: *
191: * @return The bound query tree
192: *
193: * @exception StandardException Thrown on error
194: */
195:
196: public QueryTreeNode bind() throws StandardException {
197: DataDictionary dataDictionary = getDataDictionary();
198: int numPrimaryKeys = 0;
199: int numCheckConstraints = 0;
200: int numReferenceConstraints = 0;
201: int numUniqueConstraints = 0;
202:
203: tableElementList.validate(this , dataDictionary,
204: (TableDescriptor) null);
205:
206: /* Only 1012 columns allowed per table */
207: if (tableElementList.countNumberOfColumns() > Limits.DB2_MAX_COLUMNS_IN_TABLE) {
208: throw StandardException.newException(
209: SQLState.LANG_TOO_MANY_COLUMNS_IN_TABLE_OR_VIEW,
210: String.valueOf(tableElementList
211: .countNumberOfColumns()),
212: getRelativeName(), String
213: .valueOf(Limits.DB2_MAX_COLUMNS_IN_TABLE));
214: }
215:
216: numPrimaryKeys = tableElementList
217: .countConstraints(DataDictionary.PRIMARYKEY_CONSTRAINT);
218:
219: /* Only 1 primary key allowed per table */
220: if (numPrimaryKeys > 1) {
221: throw StandardException.newException(
222: SQLState.LANG_TOO_MANY_PRIMARY_KEY_CONSTRAINTS,
223: getRelativeName());
224: }
225:
226: /* Check the validity of all check constraints */
227: numCheckConstraints = tableElementList
228: .countConstraints(DataDictionary.CHECK_CONSTRAINT);
229:
230: numReferenceConstraints = tableElementList
231: .countConstraints(DataDictionary.FOREIGNKEY_CONSTRAINT);
232:
233: numUniqueConstraints = tableElementList
234: .countConstraints(DataDictionary.UNIQUE_CONSTRAINT);
235:
236: //temp tables can't have primary key or check or foreign key or unique constraints defined on them
237: if ((tableType == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE)
238: && (numPrimaryKeys > 0 || numCheckConstraints > 0
239: || numReferenceConstraints > 0 || numUniqueConstraints > 0))
240: throw StandardException
241: .newException(SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE);
242:
243: //each of these constraints have a backing index in the back. We need to make sure that a table never has more
244: //more than 32767 indexes on it and that is why this check.
245: if ((numPrimaryKeys + numReferenceConstraints + numUniqueConstraints) > Limits.DB2_MAX_INDEXES_ON_TABLE) {
246: throw StandardException.newException(
247: SQLState.LANG_TOO_MANY_INDEXES_ON_TABLE, String
248: .valueOf(numPrimaryKeys
249: + numReferenceConstraints
250: + numUniqueConstraints),
251: getRelativeName(), String
252: .valueOf(Limits.DB2_MAX_INDEXES_ON_TABLE));
253: }
254:
255: if (numCheckConstraints > 0) {
256: /* In order to check the validity of the check constraints
257: * we must goober up a FromList containing a single table,
258: * the table being created, with an RCL containing the
259: * new columns and their types. This will allow us to
260: * bind the constraint definition trees against that
261: * FromList. When doing this, we verify that there are
262: * no nodes which can return non-deterministic results.
263: */
264: FromList fromList = (FromList) getNodeFactory().getNode(
265: C_NodeTypes.FROM_LIST,
266: getNodeFactory().doJoinOrderOptimization(),
267: getContextManager());
268: FromBaseTable table = (FromBaseTable) getNodeFactory()
269: .getNode(C_NodeTypes.FROM_BASE_TABLE,
270: getObjectName(), null, null, null,
271: getContextManager());
272: table.setTableNumber(0);
273: fromList.addFromTable(table);
274: table.setResultColumns((ResultColumnList) getNodeFactory()
275: .getNode(C_NodeTypes.RESULT_COLUMN_LIST,
276: getContextManager()));
277: tableElementList.appendNewColumnsToRCL(table);
278:
279: /* Now that we've finally goobered stuff up, bind and validate
280: * the check constraints.
281: */
282: tableElementList.bindAndValidateCheckConstraints(fromList);
283: }
284:
285: return this ;
286: }
287:
288: /**
289: * Return true if the node references SESSION schema tables (temporary or permanent)
290: *
291: * @return true if references SESSION schema tables, else false
292: *
293: * @exception StandardException Thrown on error
294: */
295: public boolean referencesSessionSchema() throws StandardException {
296: //If table being created/declared is in SESSION schema, then return true.
297: return isSessionSchema(getSchemaDescriptor());
298: }
299:
300: /**
301: * Create the Constant information that will drive the guts of Execution.
302: *
303: * @exception StandardException Thrown on failure
304: */
305: public ConstantAction makeConstantAction() throws StandardException {
306: TableElementList coldefs = tableElementList;
307:
308: // for each column, stuff system.column
309: ColumnInfo[] colInfos = new ColumnInfo[coldefs
310: .countNumberOfColumns()];
311:
312: int numConstraints = coldefs.genColumnInfos(colInfos);
313:
314: /* If we've seen a constraint, then build a constraint list */
315: CreateConstraintConstantAction[] conActions = null;
316:
317: SchemaDescriptor sd = getSchemaDescriptor();
318:
319: if (numConstraints > 0) {
320: conActions = new CreateConstraintConstantAction[numConstraints];
321:
322: coldefs.genConstraintActions(conActions, getRelativeName(),
323: sd, getDataDictionary());
324: }
325:
326: // if the any of columns are "long" and user has not specified a
327: // page size, set the pagesize to 32k.
328: // Also in case where the approximate sum of the column sizes is
329: // greater than the bump threshold , bump the pagesize to 32k
330:
331: boolean table_has_long_column = false;
332: int approxLength = 0;
333:
334: for (int i = 0; i < colInfos.length; i++) {
335: DataTypeDescriptor dts = colInfos[i].dataType;
336: if (dts.getTypeId().isLongConcatableTypeId()) {
337: table_has_long_column = true;
338: break;
339: }
340:
341: approxLength += dts.getTypeId()
342: .getApproximateLengthInBytes(dts);
343: }
344:
345: if (table_has_long_column
346: || (approxLength > Property.TBL_PAGE_SIZE_BUMP_THRESHOLD)) {
347: if (((properties == null) || (properties
348: .get(Property.PAGE_SIZE_PARAMETER) == null))
349: && (PropertyUtil.getServiceProperty(
350: getLanguageConnectionContext()
351: .getTransactionCompile(),
352: Property.PAGE_SIZE_PARAMETER) == null)) {
353: // do not override the user's choice of page size, whether it
354: // is set for the whole database or just set on this statement.
355:
356: if (properties == null)
357: properties = new Properties();
358:
359: properties.put(Property.PAGE_SIZE_PARAMETER,
360: Property.PAGE_SIZE_DEFAULT_LONG);
361: }
362: }
363:
364: return (getGenericConstantActionFactory()
365: .getCreateTableConstantAction(sd.getSchemaName(),
366: getRelativeName(), tableType, colInfos,
367: conActions, properties, lockGranularity,
368: onCommitDeleteRows, onRollbackDeleteRows));
369: }
370: }
|