001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.ModifyColumnNode
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.sql.compile.C_NodeTypes;
025:
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028: import org.apache.derby.iapi.error.StandardException;
029:
030: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
031: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
032: import org.apache.derby.iapi.sql.dictionary.DefaultDescriptor;
033: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
034: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;
035: import org.apache.derby.iapi.sql.dictionary.KeyConstraintDescriptor;
036: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
037:
038: import org.apache.derby.iapi.types.TypeId;
039: import org.apache.derby.iapi.types.DataTypeDescriptor;
040:
041: import org.apache.derby.iapi.reference.SQLState;
042:
043: import org.apache.derby.impl.sql.execute.ColumnInfo;
044: import org.apache.derby.catalog.TypeDescriptor;
045: import org.apache.derby.catalog.UUID;
046: import org.apache.derby.catalog.types.DefaultInfoImpl;
047:
048: /**
049: * A ModifyColumnNode represents a modify column in an ALTER TABLE statement.
050: *
051: * @author Jerry Brenner
052: */
053:
054: public class ModifyColumnNode extends ColumnDefinitionNode {
055: int columnPosition = -1;
056: UUID oldDefaultUUID;
057:
058: /**
059: * Get the UUID of the old column default.
060: *
061: * @return The UUID of the old column default.
062: */
063: UUID getOldDefaultUUID() {
064: return oldDefaultUUID;
065: }
066:
067: /**
068: * Get the column position for the column.
069: *
070: * @return The column position for the column.
071: */
072: public int getColumnPosition() {
073: if (SanityManager.DEBUG) {
074: SanityManager.ASSERT(columnPosition > 0,
075: "columnPosition expected to be > 0");
076: }
077: return columnPosition;
078: }
079:
080: /**
081: * Check the validity of a user type. Checks that
082: * 1. the column type is either varchar, ....
083: * 2. is the same type after the alter.
084: * 3. length is greater than the old length.
085: *
086: * @exception StandardException Thrown on error
087: */
088:
089: public void checkUserType(TableDescriptor td)
090: throws StandardException {
091: ColumnDescriptor cd;
092: TypeDescriptor oldType;
093: DataTypeDescriptor newType = dataTypeServices;
094: TypeId oldTypeId;
095: TypeId newTypeId;
096:
097: if (getNodeType() != C_NodeTypes.MODIFY_COLUMN_TYPE_NODE)
098: return; // nothing to do if user not changing length
099:
100: cd = td.getColumnDescriptor(name);
101: if (cd == null) {
102: throw StandardException.newException(
103: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, name, td
104: .getName());
105: }
106:
107: oldType = cd.getType();
108: oldTypeId = cd.getType().getTypeId();
109: newTypeId = dataTypeServices.getTypeId();
110: newType.setNullability(oldType.isNullable());
111:
112: // can't change types yet.
113: if (!(oldTypeId.equals(newTypeId))) {
114: throw StandardException.newException(
115: SQLState.LANG_MODIFY_COLUMN_CHANGE_TYPE, name);
116: }
117:
118: // can only alter the length of varchar, nvarchar, bitvarying columns
119: String typeName = dataTypeServices.getTypeName();
120: if (!(typeName.equals(TypeId.NATIONAL_VARCHAR_NAME))
121: && !(typeName.equals(TypeId.VARCHAR_NAME))
122: && !(typeName.equals(TypeId.VARBIT_NAME))) {
123: throw StandardException
124: .newException(SQLState.LANG_MODIFY_COLUMN_INVALID_TYPE);
125: }
126:
127: // cannot decrease the length of a column
128: if (newType.getMaximumWidth() < oldType.getMaximumWidth()) {
129: throw StandardException.newException(
130: SQLState.LANG_MODIFY_COLUMN_INVALID_LENGTH, name);
131: }
132: }
133:
134: /**
135: * If the type of a column is being changed (for mulan, the length of the
136: * column is being increased then make sure that this does not violate
137: * any key constraints;
138: * the column being altered is
139: * 1. part of foreign key constraint
140: * ==> ERROR. This references a Primary Key constraint and the
141: * type & lengths of the pkey/fkey must match exactly.
142: * 2. part of a unique/primary key constraint
143: * ==> OK if no fkey references this constraint.
144: * ==> ERROR if any fkey in the system references this constraint.
145: *
146: * @param td The Table Descriptor on which the ALTER is being done.
147: *
148: * @exception StandardException Thrown on Error.
149: *
150: */
151: public void checkExistingConstraints(TableDescriptor td)
152: throws StandardException {
153: if ((getNodeType() != C_NodeTypes.MODIFY_COLUMN_TYPE_NODE)
154: && (getNodeType() != C_NodeTypes.MODIFY_COLUMN_CONSTRAINT_NODE)
155: && (getNodeType() != C_NodeTypes.MODIFY_COLUMN_CONSTRAINT_NOT_NULL_NODE))
156: return;
157:
158: DataDictionary dd = getDataDictionary();
159: ConstraintDescriptorList cdl = dd.getConstraintDescriptors(td);
160: int intArray[] = new int[1];
161: intArray[0] = columnPosition;
162:
163: for (int index = 0; index < cdl.size(); index++) {
164: ConstraintDescriptor existingConstraint = cdl
165: .elementAt(index);
166: if (!(existingConstraint instanceof KeyConstraintDescriptor))
167: continue;
168:
169: if (!existingConstraint.columnIntersects(intArray))
170: continue;
171:
172: int constraintType = existingConstraint.getConstraintType();
173:
174: // cannot change the length of a column that is part of a
175: // foreign key constraint. Must be an exact match between pkey
176: // and fkey columns.
177: if ((constraintType == DataDictionary.FOREIGNKEY_CONSTRAINT)
178: && (getNodeType() == C_NodeTypes.MODIFY_COLUMN_TYPE_NODE)) {
179: throw StandardException.newException(
180: SQLState.LANG_MODIFY_COLUMN_FKEY_CONSTRAINT,
181: name, existingConstraint.getConstraintName());
182: }
183:
184: else {
185: // a column that is part of a primary key or unique constraint
186: // is being made nullable; can't be done.
187: if ((getNodeType() == C_NodeTypes.MODIFY_COLUMN_CONSTRAINT_NODE)
188: && ((existingConstraint.getConstraintType() == DataDictionary.PRIMARYKEY_CONSTRAINT) || (existingConstraint
189: .getConstraintType() == DataDictionary.UNIQUE_CONSTRAINT))) {
190: throw StandardException
191: .newException(
192: SQLState.LANG_MODIFY_COLUMN_EXISTING_CONSTRAINT,
193: name);
194: }
195: // unique key or primary key.
196: ConstraintDescriptorList refcdl = dd
197: .getForeignKeys(existingConstraint.getUUID());
198:
199: if (refcdl.size() > 0) {
200: throw StandardException.newException(
201: SQLState.LANG_MODIFY_COLUMN_REFERENCED,
202: name, refcdl.elementAt(0)
203: .getConstraintName());
204: }
205:
206: // Make the statement dependent on the primary key constraint.
207: getCompilerContext().createDependency(
208: existingConstraint);
209: }
210: }
211:
212: }
213:
214: /**
215: * Get the action associated with this node.
216: *
217: * @return The action associated with this node.
218: */
219: int getAction() {
220: switch (getNodeType()) {
221: case C_NodeTypes.MODIFY_COLUMN_DEFAULT_NODE:
222: if (autoinc_create_or_modify_Start_Increment == ColumnDefinitionNode.MODIFY_AUTOINCREMENT_RESTART_VALUE)
223: return ColumnInfo.MODIFY_COLUMN_DEFAULT_RESTART;
224: else
225: return ColumnInfo.MODIFY_COLUMN_DEFAULT_INCREMENT;
226: case C_NodeTypes.MODIFY_COLUMN_TYPE_NODE:
227: return ColumnInfo.MODIFY_COLUMN_TYPE;
228: case C_NodeTypes.MODIFY_COLUMN_CONSTRAINT_NODE:
229: return ColumnInfo.MODIFY_COLUMN_CONSTRAINT;
230: case C_NodeTypes.MODIFY_COLUMN_CONSTRAINT_NOT_NULL_NODE:
231: return ColumnInfo.MODIFY_COLUMN_CONSTRAINT_NOT_NULL;
232: default:
233: if (SanityManager.DEBUG) {
234: SanityManager.THROWASSERT("Unexpected nodeType = "
235: + getNodeType());
236: }
237: return 0;
238: }
239: }
240:
241: /**
242: * Check the validity of the default, if any, for this node.
243: *
244: * @param dd The DataDictionary.
245: * @param td The TableDescriptor.
246: *
247: * @exception StandardException Thrown on error
248: */
249: void bindAndValidateDefault(DataDictionary dd, TableDescriptor td)
250: throws StandardException {
251: ColumnDescriptor cd;
252:
253: // First verify that the column exists
254: cd = td.getColumnDescriptor(name);
255: if (cd == null) {
256: throw StandardException.newException(
257: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, name, td
258: .getName());
259: }
260:
261: // Get the UUID for the old default
262: DefaultDescriptor defaultDescriptor = cd
263: .getDefaultDescriptor(dd);
264:
265: oldDefaultUUID = (defaultDescriptor == null) ? null
266: : defaultDescriptor.getUUID();
267:
268: // Remember the column position
269: columnPosition = cd.getPosition();
270:
271: // No other work to do if no user specified default
272: if (getNodeType() != C_NodeTypes.MODIFY_COLUMN_DEFAULT_NODE) {
273: return;
274: }
275:
276: // If the statement is not setting the column's default, then
277: // recover the old default and re-use it. If the statement is
278: // changing the start value for the auto-increment, then recover
279: // the old increment-by value and re-use it. If the statement is
280: // changing the increment-by value, then recover the old start value
281: // and re-use it. This way, the column alteration only changes the
282: // aspects of the autoincrement settings that it intends to change,
283: // and does not lose the other aspecs.
284: if (defaultNode == null)
285: defaultInfo = (DefaultInfoImpl) cd.getDefaultInfo();
286: if (autoinc_create_or_modify_Start_Increment == ColumnDefinitionNode.MODIFY_AUTOINCREMENT_RESTART_VALUE)
287: autoincrementIncrement = cd.getAutoincInc();
288: if (autoinc_create_or_modify_Start_Increment == ColumnDefinitionNode.MODIFY_AUTOINCREMENT_INC_VALUE)
289: autoincrementStart = cd.getAutoincStart();
290:
291: /* Fill in the DataTypeServices from the DataDictionary */
292: dataTypeServices = cd.getType();
293:
294: // Now validate the default
295: validateDefault(dd, td);
296: }
297:
298: private ColumnDescriptor getLocalColumnDescriptor(String name,
299: TableDescriptor td) throws StandardException {
300: ColumnDescriptor cd;
301:
302: // First verify that the column exists
303: cd = td.getColumnDescriptor(name);
304: if (cd == null) {
305: throw StandardException.newException(
306: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, name, td
307: .getName());
308: }
309:
310: return cd;
311: }
312:
313: /**
314: * check the validity of autoincrement values in the case that we are
315: * modifying an existing column (includes checking if autoincrement is set
316: * when making a column nullable)
317: */
318: public void validateAutoincrement(DataDictionary dd,
319: TableDescriptor td, int tableType) throws StandardException {
320: ColumnDescriptor cd;
321:
322: // a column that has an autoincrement default can't be made nullable
323: if (getNodeType() == C_NodeTypes.MODIFY_COLUMN_CONSTRAINT_NODE) {
324: cd = getLocalColumnDescriptor(name, td);
325: if (cd.isAutoincrement()) {
326: throw StandardException.newException(
327: SQLState.LANG_AI_CANNOT_NULL_AI,
328: getColumnName());
329: }
330: }
331:
332: if (autoincrementVerify) {
333: cd = getLocalColumnDescriptor(name, td);
334: if (!cd.isAutoincrement())
335: throw StandardException.newException(
336: SQLState.LANG_INVALID_ALTER_TABLE_ATTRIBUTES,
337: td.getQualifiedName(), name);
338: }
339: if (isAutoincrement == false)
340: return;
341:
342: super.validateAutoincrement(dd, td, tableType);
343: if (dataTypeServices.isNullable())
344: throw StandardException.newException(
345: SQLState.LANG_AI_CANNOT_ADD_AI_TO_NULLABLE,
346: getColumnName());
347: }
348: }
|