001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.TableElementList
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.services.sanity.SanityManager;
025:
026: import org.apache.derby.iapi.error.StandardException;
027:
028: import org.apache.derby.iapi.sql.compile.CompilerContext;
029: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
030:
031: import org.apache.derby.iapi.types.DataTypeDescriptor;
032:
033: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
034: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
035: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
036: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
037:
038: import org.apache.derby.iapi.sql.depend.DependencyManager;
039: import org.apache.derby.iapi.sql.depend.ProviderInfo;
040: import org.apache.derby.iapi.sql.depend.ProviderList;
041:
042: import org.apache.derby.iapi.reference.SQLState;
043:
044: import org.apache.derby.impl.sql.execute.ColumnInfo;
045: import org.apache.derby.impl.sql.execute.ConstraintInfo;
046: import org.apache.derby.impl.sql.execute.ConstraintConstantAction;
047: import org.apache.derby.impl.sql.execute.IndexConstantAction;
048:
049: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;
050: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
051:
052: import org.apache.derby.catalog.UUID;
053:
054: import java.util.Hashtable;
055: import java.util.Vector;
056:
057: /**
058: * A TableElementList represents the list of columns and other table elements
059: * such as constraints in a CREATE TABLE or ALTER TABLE statement.
060: *
061: * @author Jeff Lichtman
062: */
063:
064: public class TableElementList extends QueryTreeNodeVector {
065: private int numColumns;
066: private TableDescriptor td;
067:
068: /**
069: * Add a TableElementNode to this TableElementList
070: *
071: * @param tableElement The TableElementNode to add to this list
072: */
073:
074: public void addTableElement(TableElementNode tableElement) {
075: addElement(tableElement);
076: if ((tableElement instanceof ColumnDefinitionNode)
077: || tableElement.getElementType() == TableElementNode.AT_DROP_COLUMN) {
078: numColumns++;
079: }
080: }
081:
082: /**
083: * Convert this object to a String. See comments in QueryTreeNode.java
084: * for how this should be done for tree printing.
085: *
086: * @return This object as a String
087: */
088:
089: public String toString() {
090: if (SanityManager.DEBUG) {
091: StringBuffer buffer = new StringBuffer("");
092:
093: for (int index = 0; index < size(); index++) {
094: buffer.append(elementAt(index).toString()).append("\n");
095: }
096:
097: return buffer.toString();
098: } else {
099: return "";
100: }
101: }
102:
103: /**
104: * Validate this TableElementList. This includes checking for
105: * duplicate columns names, and checking that user types really exist.
106: *
107: * @param ddlStmt DDLStatementNode which contains this list
108: * @param dd DataDictionary to use
109: * @param td TableDescriptor for table, if existing table.
110: *
111: * @exception StandardException Thrown on error
112: */
113: void validate(DDLStatementNode ddlStmt, DataDictionary dd,
114: TableDescriptor td) throws StandardException {
115: this .td = td;
116: int numAutoCols = 0;
117:
118: int size = size();
119: Hashtable columnHT = new Hashtable(size + 2, (float) .999);
120: Hashtable constraintHT = new Hashtable(size + 2, (float) .999);
121: //all the primary key/unique key constraints for this table
122: Vector constraintsVector = new Vector();
123:
124: //special case for alter table (td is not null in case of alter table)
125: if (td != null) {
126: //In case of alter table, get the already existing primary key and unique
127: //key constraints for this table. And then we will compare them with new
128: //primary key/unique key constraint column lists.
129: ConstraintDescriptorList cdl = dd
130: .getConstraintDescriptors(td);
131: ConstraintDescriptor cd;
132:
133: if (cdl != null) //table does have some pre-existing constraints defined on it
134: {
135: for (int i = 0; i < cdl.size(); i++) {
136: cd = cdl.elementAt(i);
137: //if the constraint type is not primary key or unique key, ignore it.
138: if (cd.getConstraintType() == DataDictionary.PRIMARYKEY_CONSTRAINT
139: || cd.getConstraintType() == DataDictionary.UNIQUE_CONSTRAINT)
140: constraintsVector.addElement(cd);
141: }
142: }
143: }
144:
145: int tableType = TableDescriptor.BASE_TABLE_TYPE;
146: if (ddlStmt instanceof CreateTableNode)
147: tableType = ((CreateTableNode) ddlStmt).tableType;
148:
149: for (int index = 0; index < size; index++) {
150: TableElementNode tableElement = (TableElementNode) elementAt(index);
151:
152: if (tableElement instanceof ColumnDefinitionNode) {
153: ColumnDefinitionNode cdn = (ColumnDefinitionNode) elementAt(index);
154: if (tableType == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE
155: && (cdn.getDataTypeServices().getTypeId()
156: .isLongConcatableTypeId() || cdn
157: .getDataTypeServices().getTypeId()
158: .isUserDefinedTypeId())) {
159: throw StandardException.newException(
160: SQLState.LANG_LONG_DATA_TYPE_NOT_ALLOWED,
161: cdn.getColumnName());
162: }
163: checkForDuplicateColumns(ddlStmt, columnHT, cdn
164: .getColumnName());
165: cdn.checkUserType(td);
166: cdn.bindAndValidateDefault(dd, td);
167:
168: cdn.validateAutoincrement(dd, td, tableType);
169:
170: if (tableElement instanceof ModifyColumnNode) {
171: ModifyColumnNode mcdn = (ModifyColumnNode) cdn;
172: mcdn.checkExistingConstraints(td);
173: } else if (cdn.isAutoincrementColumn())
174: numAutoCols++;
175: } else if (tableElement.getElementType() == TableElementNode.AT_DROP_COLUMN) {
176: String colName = tableElement.getName();
177: if (td.getColumnDescriptor(colName) == null) {
178: throw StandardException.newException(
179: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,
180: colName, td.getQualifiedName());
181: }
182: break;
183: }
184:
185: /* The rest of this method deals with validating constraints */
186: if (!(tableElement.hasConstraint())) {
187: continue;
188: }
189:
190: ConstraintDefinitionNode cdn = (ConstraintDefinitionNode) tableElement;
191:
192: cdn.bind(ddlStmt, dd);
193:
194: //if constraint is primary key or unique key, add it to the vector
195: if (cdn.getConstraintType() == DataDictionary.PRIMARYKEY_CONSTRAINT
196: || cdn.getConstraintType() == DataDictionary.UNIQUE_CONSTRAINT) {
197: /* In case of create table, the vector can have only ConstraintDefinitionNode
198: * elements. In case of alter table, it can have both ConstraintDefinitionNode
199: * (for new constraints) and ConstraintDescriptor(for pre-existing constraints).
200: */
201:
202: Object destConstraint;
203: String destName = null;
204: String[] destColumnNames = null;
205:
206: for (int i = 0; i < constraintsVector.size(); i++) {
207:
208: destConstraint = constraintsVector.elementAt(i);
209: if (destConstraint instanceof ConstraintDefinitionNode) {
210: ConstraintDefinitionNode destCDN = (ConstraintDefinitionNode) destConstraint;
211: destName = destCDN.getConstraintMoniker();
212: destColumnNames = destCDN.getColumnList()
213: .getColumnNames();
214: } else if (destConstraint instanceof ConstraintDescriptor) {
215: //will come here only for pre-existing constraints in case of alter table
216: ConstraintDescriptor destCD = (ConstraintDescriptor) destConstraint;
217: destName = destCD.getConstraintName();
218: destColumnNames = destCD.getColumnDescriptors()
219: .getColumnNames();
220: }
221: //check if there are multiple constraints with same set of columns
222: if (columnsMatch(cdn.getColumnList()
223: .getColumnNames(), destColumnNames))
224: throw StandardException
225: .newException(
226: SQLState.LANG_MULTIPLE_CONSTRAINTS_WITH_SAME_COLUMNS,
227: cdn.getConstraintMoniker(),
228: destName);
229: }
230: constraintsVector.addElement(cdn);
231: }
232:
233: /* Make sure that there are no duplicate constraint names in the list */
234: if (cdn instanceof ConstraintDefinitionNode)
235: checkForDuplicateConstraintNames(ddlStmt, constraintHT,
236: cdn.getConstraintMoniker());
237:
238: /* Make sure that the constraint we are trying to drop exists */
239: if (cdn.getConstraintType() == DataDictionary.DROP_CONSTRAINT) {
240: /*
241: ** If no schema descriptor, then must be an invalid
242: ** schema name.
243: */
244:
245: String dropConstraintName = cdn.getConstraintMoniker();
246:
247: if (dropConstraintName != null) {
248:
249: String dropSchemaName = cdn.getDropSchemaName();
250:
251: SchemaDescriptor sd = dropSchemaName == null ? td
252: .getSchemaDescriptor()
253: : getSchemaDescriptor(dropSchemaName);
254:
255: ConstraintDescriptor cd = dd
256: .getConstraintDescriptorByName(td, sd,
257: dropConstraintName, false);
258: if (cd == null) {
259: throw StandardException
260: .newException(
261: SQLState.LANG_DROP_NON_EXISTENT_CONSTRAINT,
262: (sd.getSchemaName() + "." + dropConstraintName),
263: td.getQualifiedName());
264: }
265: /* Statement is dependendent on the ConstraintDescriptor */
266: getCompilerContext().createDependency(cd);
267: }
268: }
269:
270: if (cdn.hasPrimaryKeyConstraint()) {
271: // for PRIMARY KEY, check that columns are unique
272: verifyUniqueColumnList(ddlStmt, cdn);
273:
274: if (td == null) {
275: // in CREATE TABLE so set PRIMARY KEY columns to NOT NULL
276: setColumnListToNotNull(cdn);
277: } else {
278: // in ALTER TABLE so raise error if any columns are nullable
279: checkForNullColumns(cdn, td);
280: }
281: } else if (cdn.hasUniqueKeyConstraint()) {
282: // for UNIQUE, check that columns are unique and NOT NULL
283: verifyUniqueColumnList(ddlStmt, cdn);
284: checkForNullColumns(cdn, td);
285: } else if (cdn.hasForeignKeyConstraint()) {
286: // for FOREIGN KEY, check that columns are unique
287: verifyUniqueColumnList(ddlStmt, cdn);
288: }
289: }
290:
291: /* Can have only one autoincrement column in DB2 mode */
292: if (numAutoCols > 1)
293: throw StandardException
294: .newException(SQLState.LANG_MULTIPLE_AUTOINCREMENT_COLUMNS);
295:
296: }
297:
298: /**
299: * Count the number of constraints of the specified type.
300: *
301: * @param constraintType The constraint type to search for.
302: *
303: * @return int The number of constraints of the specified type.
304: */
305: public int countConstraints(int constraintType) {
306: int numConstraints = 0;
307: int size = size();
308:
309: for (int index = 0; index < size; index++) {
310: ConstraintDefinitionNode cdn;
311: TableElementNode element = (TableElementNode) elementAt(index);
312:
313: if (!(element instanceof ConstraintDefinitionNode)) {
314: continue;
315: }
316:
317: cdn = (ConstraintDefinitionNode) element;
318:
319: if (constraintType == cdn.getConstraintType()) {
320: numConstraints++;
321: }
322: }
323:
324: return numConstraints;
325: }
326:
327: /**
328: * Count the number of columns.
329: *
330: * @return int The number of columns.
331: */
332: public int countNumberOfColumns() {
333: return numColumns;
334: }
335:
336: /**
337: * Fill in the ColumnInfo[] for this table element list.
338: *
339: * @param colInfos The ColumnInfo[] to be filled in.
340: *
341: * @return int The number of constraints in the create table.
342: */
343: public int genColumnInfos(ColumnInfo[] colInfos) {
344: int numConstraints = 0;
345: int size = size();
346:
347: for (int index = 0; index < size; index++) {
348: if (((TableElementNode) elementAt(index)).getElementType() == TableElementNode.AT_DROP_COLUMN) {
349: colInfos[index] = new ColumnInfo(
350: ((TableElementNode) elementAt(index)).getName(),
351: null, null, null, null, null, ColumnInfo.DROP,
352: 0, 0, 0);
353: break;
354: }
355:
356: if (!(elementAt(index) instanceof ColumnDefinitionNode)) {
357: if (SanityManager.DEBUG) {
358: SanityManager
359: .ASSERT(
360: elementAt(index) instanceof ConstraintDefinitionNode,
361: "elementAt(index) expected to be instanceof "
362: + "ConstraintDefinitionNode");
363: }
364:
365: /* Remember how many constraints that we've seen */
366: numConstraints++;
367: continue;
368: }
369:
370: ColumnDefinitionNode coldef = (ColumnDefinitionNode) elementAt(index);
371:
372: colInfos[index - numConstraints] = new ColumnInfo(
373: coldef.getColumnName(),
374: coldef.getDataTypeServices(),
375: coldef.getDefaultValue(),
376: coldef.getDefaultInfo(),
377: (UUID) null,
378: coldef.getOldDefaultUUID(),
379: coldef.getAction(),
380: (coldef.isAutoincrementColumn() ? coldef
381: .getAutoincrementStart() : 0),
382: (coldef.isAutoincrementColumn() ? coldef
383: .getAutoincrementIncrement() : 0),
384: (coldef.isAutoincrementColumn() ? coldef
385: .getAutoinc_create_or_modify_Start_Increment()
386: : -1));
387:
388: /* Remember how many constraints that we've seen */
389: if (coldef.hasConstraint()) {
390: numConstraints++;
391: }
392: }
393:
394: return numConstraints;
395: }
396:
397: /**
398: * Append goobered up ResultColumns to the table's RCL.
399: * This is useful for binding check constraints for CREATE and ALTER TABLE.
400: *
401: * @param table The table in question.
402: *
403: * @exception StandardException Thrown on error
404: */
405: public void appendNewColumnsToRCL(FromBaseTable table)
406: throws StandardException {
407: int size = size();
408: ResultColumnList rcl = table.getResultColumns();
409: TableName exposedName = table.getTableName();
410:
411: for (int index = 0; index < size; index++) {
412: if (elementAt(index) instanceof ColumnDefinitionNode) {
413: ColumnDefinitionNode cdn = (ColumnDefinitionNode) elementAt(index);
414: ResultColumn resultColumn;
415: ValueNode valueNode;
416:
417: /* Build a ResultColumn/BaseColumnNode pair for the column */
418: valueNode = (ValueNode) getNodeFactory().getNode(
419: C_NodeTypes.BASE_COLUMN_NODE,
420: cdn.getColumnName(), exposedName,
421: cdn.getDataTypeServices(), getContextManager());
422:
423: resultColumn = (ResultColumn) getNodeFactory().getNode(
424: C_NodeTypes.RESULT_COLUMN,
425: cdn.getDataTypeServices(), valueNode,
426: getContextManager());
427: resultColumn.setName(cdn.getColumnName());
428: rcl.addElement(resultColumn);
429: }
430: }
431: }
432:
433: /**
434: * Bind and validate all of the check constraints in this list against
435: * the specified FromList.
436: *
437: * @param fromList The FromList in question.
438: *
439: * @exception StandardException Thrown on error
440: */
441: void bindAndValidateCheckConstraints(FromList fromList)
442: throws StandardException {
443: CompilerContext cc;
444: FromBaseTable table = (FromBaseTable) fromList.elementAt(0);
445: int size = size();
446:
447: cc = getCompilerContext();
448:
449: Vector aggregateVector = new Vector();
450:
451: for (int index = 0; index < size; index++) {
452: ConstraintDefinitionNode cdn;
453: TableElementNode element = (TableElementNode) elementAt(index);
454: ValueNode checkTree;
455:
456: if (!(element instanceof ConstraintDefinitionNode)) {
457: continue;
458: }
459:
460: cdn = (ConstraintDefinitionNode) element;
461:
462: if (cdn.getConstraintType() != DataDictionary.CHECK_CONSTRAINT) {
463: continue;
464: }
465:
466: checkTree = cdn.getCheckCondition();
467:
468: // bind the check condition
469: // verify that it evaluates to a boolean
470: final int previousReliability = cc.getReliability();
471: try {
472: /* Each check constraint can have its own set of dependencies.
473: * These dependencies need to be shared with the prepared
474: * statement as well. We create a new auxiliary provider list
475: * for the check constraint, "push" it on the compiler context
476: * by swapping it with the current auxiliary provider list
477: * and the "pop" it when we're done by restoring the old
478: * auxiliary provider list.
479: */
480: ProviderList apl = new ProviderList();
481:
482: ProviderList prevAPL = cc
483: .getCurrentAuxiliaryProviderList();
484: cc.setCurrentAuxiliaryProviderList(apl);
485:
486: // Tell the compiler context to only allow deterministic nodes
487: cc.setReliability(CompilerContext.CHECK_CONSTRAINT);
488: checkTree = checkTree.bindExpression(fromList,
489: (SubqueryList) null, aggregateVector);
490:
491: // no aggregates, please
492: if (aggregateVector.size() != 0) {
493: throw StandardException.newException(
494: SQLState.LANG_INVALID_CHECK_CONSTRAINT, cdn
495: .getConstraintText());
496: }
497:
498: checkTree = checkTree.checkIsBoolean();
499: cdn.setCheckCondition(checkTree);
500:
501: /* Save the APL off in the constraint node */
502: if (apl.size() > 0) {
503: cdn.setAuxiliaryProviderList(apl);
504: }
505:
506: // Restore the previous AuxiliaryProviderList
507: cc.setCurrentAuxiliaryProviderList(prevAPL);
508: } finally {
509: cc.setReliability(previousReliability);
510: }
511:
512: /* We have a valid check constraint, now build an array of
513: * 1-based columnIds that the constraint references.
514: */
515: ResultColumnList rcl = table.getResultColumns();
516: int numReferenced = rcl.countReferencedColumns();
517: int[] checkColumnReferences = new int[numReferenced];
518:
519: rcl.recordColumnReferences(checkColumnReferences, 1);
520: cdn.setCheckColumnReferences(checkColumnReferences);
521:
522: /* Now we build a list with only the referenced columns and
523: * copy it to the cdn. Thus we can build the array of
524: * column names for the referenced columns during generate().
525: */
526: ResultColumnList refRCL = (ResultColumnList) getNodeFactory()
527: .getNode(C_NodeTypes.RESULT_COLUMN_LIST,
528: getContextManager());
529: rcl.copyReferencedColumnsToNewList(refRCL);
530:
531: /* A column check constraint can only refer to that column. If this is a
532: * column constraint, we should have an RCL with that column
533: */
534: if (cdn.getColumnList() != null) {
535: String colName = ((ResultColumn) (cdn.getColumnList()
536: .elementAt(0))).getName();
537: if (numReferenced > 1
538: || !colName.equals(((ResultColumn) (refRCL
539: .elementAt(0))).getName()))
540: throw StandardException.newException(
541: SQLState.LANG_DB2_INVALID_CHECK_CONSTRAINT,
542: colName);
543:
544: }
545: cdn.setColumnList(refRCL);
546:
547: /* Clear the column references in the RCL so each check constraint
548: * starts with a clean list.
549: */
550: rcl.clearColumnReferences();
551: }
552: }
553:
554: /**
555: * Fill in the ConstraintConstantAction[] for this create/alter table.
556: *
557: * @param conActions The ConstraintConstantAction[] to be filled in.
558: * @param tableName The name of the Table being created.
559: * @param tableSd The schema for that table.
560: * @param dd The DataDictionary
561: *
562: * @exception StandardException Thrown on failure
563: */
564: void genConstraintActions(ConstraintConstantAction[] conActions,
565: String tableName, SchemaDescriptor tableSd,
566: DataDictionary dd) throws StandardException {
567: int size = size();
568: int conActionIndex = 0;
569: for (int index = 0; index < size; index++) {
570: String[] columnNames = null;
571: TableElementNode ten = (TableElementNode) elementAt(index);
572: IndexConstantAction indexAction = null;
573:
574: if (!ten.hasConstraint()) {
575: continue;
576: }
577:
578: if (ten instanceof ColumnDefinitionNode) {
579: continue;
580: }
581:
582: ConstraintDefinitionNode constraintDN = (ConstraintDefinitionNode) ten;
583:
584: if (constraintDN.getColumnList() != null) {
585: columnNames = new String[constraintDN.getColumnList()
586: .size()];
587: constraintDN.getColumnList().exportNames(columnNames);
588: }
589:
590: int constraintType = constraintDN.getConstraintType();
591: String constraintText = constraintDN.getConstraintText();
592:
593: /*
594: ** If the constraint is not named (e.g.
595: ** create table x (x int primary key)), then
596: ** the constraintSd is the same as the table.
597: */
598: String constraintName = constraintDN.getConstraintMoniker();
599:
600: /* At execution time, we will generate a unique name for the backing
601: * index (for CREATE CONSTRAINT) and we will look up the conglomerate
602: * name (for DROP CONSTRAINT).
603: */
604: if (constraintDN.requiresBackingIndex()) {
605: indexAction = genIndexAction(constraintDN
606: .requiresUniqueIndex(), null, constraintDN,
607: columnNames, true, tableSd, tableName,
608: constraintType, dd);
609: }
610:
611: if (constraintType == DataDictionary.DROP_CONSTRAINT) {
612: conActions[conActionIndex] = getGenericConstantActionFactory()
613: .getDropConstraintConstantAction(
614: constraintName,
615: constraintDN.getDropSchemaName(), /// FiX
616: tableName, td.getUUID(),
617: tableSd.getSchemaName(), indexAction,
618: constraintDN.getDropBehavior(),
619: constraintDN.getVerifyType());
620: } else {
621: ProviderList apl = constraintDN
622: .getAuxiliaryProviderList();
623: ConstraintInfo refInfo = null;
624: ProviderInfo[] providerInfos = null;
625:
626: if (constraintDN instanceof FKConstraintDefinitionNode) {
627: refInfo = ((FKConstraintDefinitionNode) constraintDN)
628: .getReferencedConstraintInfo();
629: }
630:
631: /* Create the ProviderInfos, if the constraint is dependent on any Providers */
632: if (apl != null && apl.size() > 0) {
633: /* Get all the dependencies for the current statement and transfer
634: * them to this view.
635: */
636: DependencyManager dm = dd.getDependencyManager();
637: providerInfos = dm.getPersistentProviderInfos(apl);
638: } else {
639: providerInfos = new ProviderInfo[0];
640: // System.out.println("TABLE ELEMENT LIST EMPTY");
641: }
642:
643: conActions[conActionIndex++] = getGenericConstantActionFactory()
644: .getCreateConstraintConstantAction(
645: constraintName,
646: constraintType,
647: tableName,
648: ((td != null) ? td.getUUID()
649: : (UUID) null),
650: tableSd.getSchemaName(), columnNames,
651: indexAction, constraintText, true, // enabled
652: refInfo, providerInfos);
653: }
654: }
655: }
656:
657: //check if one array is same as another
658: private boolean columnsMatch(String[] columnNames1,
659: String[] columnNames2) {
660: int srcCount, srcSize, destCount, destSize;
661: boolean match = true;
662:
663: if (columnNames1.length != columnNames2.length)
664: return false;
665:
666: srcSize = columnNames1.length;
667: destSize = columnNames2.length;
668:
669: for (srcCount = 0; srcCount < srcSize; srcCount++) {
670: match = false;
671: for (destCount = 0; destCount < destSize; destCount++) {
672: if (columnNames1[srcCount]
673: .equals(columnNames2[destCount])) {
674: match = true;
675: break;
676: }
677: }
678: if (match == false)
679: return false;
680: }
681:
682: return true;
683: }
684:
685: private IndexConstantAction genIndexAction(boolean isUnique,
686: String indexName, ConstraintDefinitionNode cdn,
687: String[] columnNames, boolean isConstraint,
688: SchemaDescriptor sd, String tableName, int constraintType,
689: DataDictionary dd) throws StandardException {
690: if (indexName == null) {
691: indexName = cdn.getBackingIndexName(dd);
692: }
693:
694: if (constraintType == DataDictionary.DROP_CONSTRAINT) {
695: return getGenericConstantActionFactory()
696: .getDropIndexConstantAction(null, indexName,
697: tableName, sd.getSchemaName(),
698: td.getUUID(), td.getHeapConglomerateId());
699: } else {
700: boolean[] isAscending = new boolean[columnNames.length];
701: for (int i = 0; i < isAscending.length; i++)
702: isAscending[i] = true;
703: return getGenericConstantActionFactory()
704: .getCreateIndexConstantAction(
705: isUnique,
706: "BTREE", // indexType
707: sd.getSchemaName(),
708: indexName,
709: tableName,
710: ((td != null) ? td.getUUID() : (UUID) null),
711: 0, // conglomId
712: columnNames, isAscending, isConstraint,
713: cdn.getBackingIndexUUID(),
714: cdn.getProperties());
715: }
716: }
717:
718: /**
719: * Check to make sure that there are no duplicate column names
720: * in the list. (The comparison here is case sensitive.
721: * The work of converting column names that are not quoted
722: * identifiers to upper case is handled by the parser.)
723: * RESOLVE: This check will also be performed by alter table.
724: *
725: * @param ddlStmt DDLStatementNode which contains this list
726: * @param ht Hashtable for enforcing uniqueness.
727: * @param colName Column name to check for.
728: *
729: * @exception StandardException Thrown on error
730: */
731: private void checkForDuplicateColumns(DDLStatementNode ddlStmt,
732: Hashtable ht, String colName) throws StandardException {
733: Object object = ht.put(colName, colName);
734: if (object != null) {
735: /* RESOLVE - different error messages for create and alter table */
736: if (ddlStmt instanceof CreateTableNode) {
737: throw StandardException.newException(
738: SQLState.LANG_DUPLICATE_COLUMN_NAME_CREATE,
739: colName);
740: }
741: }
742: }
743:
744: /**
745: * Check to make sure that there are no duplicate constraint names
746: * in the list. (The comparison here is case sensitive.
747: * The work of converting column names that are not quoted
748: * identifiers to upper case is handled by the parser.)
749: * RESOLVE: This check will also be performed by alter table.
750: *
751: * @param ddlStmt DDLStatementNode which contains this list
752: *
753: * @exception StandardException Thrown on error
754: */
755: private void checkForDuplicateConstraintNames(
756: DDLStatementNode ddlStmt, Hashtable ht,
757: String constraintName) throws StandardException {
758: if (constraintName == null)
759: return;
760:
761: Object object = ht.put(constraintName, constraintName);
762: if (object != null) {
763:
764: /* RESOLVE - different error messages for create and alter table */
765: if (ddlStmt instanceof CreateTableNode) {
766: /* RESOLVE - new error message */
767: throw StandardException.newException(
768: SQLState.LANG_DUPLICATE_CONSTRAINT_NAME_CREATE,
769: constraintName);
770: }
771: }
772: }
773:
774: /**
775: * Verify that a primary/unique table constraint has a valid column list.
776: * (All columns in table and no duplicates.)
777: *
778: * @param ddlStmt The outer DDLStatementNode
779: * @param cdn The ConstraintDefinitionNode
780: *
781: * @exception StandardException Thrown if the column list is invalid
782: */
783: private void verifyUniqueColumnList(DDLStatementNode ddlStmt,
784: ConstraintDefinitionNode cdn) throws StandardException {
785: String invalidColName;
786:
787: /* Verify that every column in the list appears in the table's list of columns */
788: if (ddlStmt instanceof CreateTableNode) {
789: invalidColName = cdn.getColumnList()
790: .verifyCreateConstraintColumnList(this );
791: if (invalidColName != null) {
792: throw StandardException
793: .newException(
794: SQLState.LANG_INVALID_CREATE_CONSTRAINT_COLUMN_LIST,
795: ddlStmt.getRelativeName(),
796: invalidColName);
797: }
798: } else {
799: /* RESOLVE - alter table will need to get table descriptor */
800: }
801:
802: /* Check the uniqueness of the column names within the list */
803: invalidColName = cdn.getColumnList().verifyUniqueNames(false);
804: if (invalidColName != null) {
805: throw StandardException.newException(
806: SQLState.LANG_DUPLICATE_CONSTRAINT_COLUMN_NAME,
807: invalidColName);
808: }
809: }
810:
811: /**
812: * Set all columns in that appear in a PRIMARY KEY constraint in a CREATE TABLE statement to NOT NULL.
813: *
814: * @param cdn The ConstraintDefinitionNode for a PRIMARY KEY constraint
815: */
816: private void setColumnListToNotNull(ConstraintDefinitionNode cdn) {
817: ResultColumnList rcl = cdn.getColumnList();
818: int rclSize = rcl.size();
819: for (int index = 0; index < rclSize; index++) {
820: String colName = ((ResultColumn) rcl.elementAt(index))
821: .getName();
822: DataTypeDescriptor dtd = getColumnDataTypeDescriptor(colName);
823: dtd.setNullability(false);
824: }
825: }
826:
827: private void checkForNullColumns(ConstraintDefinitionNode cdn,
828: TableDescriptor td) throws StandardException {
829: ResultColumnList rcl = cdn.getColumnList();
830: int rclSize = rcl.size();
831: for (int index = 0; index < rclSize; index++) {
832: String colName = ((ResultColumn) rcl.elementAt(index))
833: .getName();
834: DataTypeDescriptor dtd;
835: if (td == null) {
836: dtd = getColumnDataTypeDescriptor(colName);
837: } else {
838: dtd = getColumnDataTypeDescriptor(colName, td);
839: }
840: // todo dtd may be null if the column does not exist, we should check that first
841: if (dtd != null && dtd.isNullable()) {
842: throw StandardException
843: .newException(
844: SQLState.LANG_DB2_ADD_UNIQUE_OR_PRIMARY_KEY_ON_NULL_COLS,
845: colName);
846: }
847: }
848: }
849:
850: private DataTypeDescriptor getColumnDataTypeDescriptor(
851: String colName) {
852: int size = size();
853:
854: for (int index = 0; index < size; index++) {
855: TableElementNode tableElement = (TableElementNode) elementAt(index);
856:
857: if (tableElement instanceof ColumnDefinitionNode) {
858: ColumnDefinitionNode cdn = (ColumnDefinitionNode) tableElement;
859: if (colName.equals(cdn.getColumnName())) {
860: return cdn.getDataTypeServices();
861: }
862: }
863: }
864: return null;
865: }
866:
867: private DataTypeDescriptor getColumnDataTypeDescriptor(
868: String colName, TableDescriptor td) {
869: // check existing columns
870: ColumnDescriptor cd = td.getColumnDescriptor(colName);
871: if (cd != null) {
872: return cd.getType();
873: }
874: // check for new columns
875: return getColumnDataTypeDescriptor(colName);
876: }
877:
878: /**
879: * Determine whether or not the parameter matches a column name in this list.
880: *
881: * @param colName The column name to search for.
882: *
883: * @return boolean Whether or not a match is found.
884: */
885: public boolean containsColumnName(String colName) {
886: int size = size();
887: for (int index = 0; index < size; index++) {
888: TableElementNode tableElement = (TableElementNode) elementAt(index);
889:
890: if (tableElement instanceof ColumnDefinitionNode) {
891: if (colName
892: .equals(((ColumnDefinitionNode) tableElement)
893: .getName())) {
894: return true;
895: }
896: }
897: }
898:
899: return false;
900: }
901: }
|