--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.TableElementList\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.compile;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.StringDataValue;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+import org.apache.derby.iapi.sql.depend.ProviderInfo;\r
+import org.apache.derby.iapi.sql.depend.ProviderList;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.impl.sql.execute.ColumnInfo;\r
+import org.apache.derby.impl.sql.execute.ConstraintInfo;\r
+import org.apache.derby.impl.sql.execute.ConstraintConstantAction;\r
+import org.apache.derby.impl.sql.execute.IndexConstantAction;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;\r
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;\r
+\r
+import org.apache.derby.catalog.UUID;\r
+\r
+import java.util.Hashtable;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * A TableElementList represents the list of columns and other table elements\r
+ * such as constraints in a CREATE TABLE or ALTER TABLE statement.\r
+ *\r
+ */\r
+\r
+public class TableElementList extends QueryTreeNodeVector\r
+{\r
+ private int numColumns;\r
+ private TableDescriptor td;\r
+\r
+ /**\r
+ * Add a TableElementNode to this TableElementList\r
+ *\r
+ * @param tableElement The TableElementNode to add to this list\r
+ */\r
+\r
+ public void addTableElement(TableElementNode tableElement)\r
+ {\r
+ addElement(tableElement);\r
+ if ((tableElement instanceof ColumnDefinitionNode) ||\r
+ tableElement.getElementType() == TableElementNode.AT_DROP_COLUMN)\r
+ {\r
+ numColumns++;\r
+ }\r
+ } \r
+\r
+ /**\r
+ * Convert this object to a String. See comments in QueryTreeNode.java\r
+ * for how this should be done for tree printing.\r
+ *\r
+ * @return This object as a String\r
+ */\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ StringBuffer buffer = new StringBuffer("");\r
+\r
+ for (int index = 0; index < size(); index++)\r
+ {\r
+ buffer.append(elementAt(index).toString()).append("\n");\r
+ }\r
+\r
+ return buffer.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Use the passed schema descriptor's collation type to set the collation\r
+ * of the character string types in create table node\r
+ * @param sd\r
+ */\r
+ void setCollationTypesOnCharacterStringColumns(SchemaDescriptor sd) {\r
+ int size = size();\r
+ int collationType = sd.getCollationType();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ TableElementNode tableElement = (TableElementNode) elementAt(index);\r
+\r
+ if (tableElement instanceof ColumnDefinitionNode)\r
+ {\r
+ ColumnDefinitionNode cdn = (ColumnDefinitionNode) elementAt(index);\r
+ if (cdn.getDataTypeServices().getTypeId().isStringTypeId()) {\r
+ cdn.getDataTypeServices().setCollationType(collationType);\r
+ cdn.getDataTypeServices().setCollationDerivation(StringDataValue.COLLATION_DERIVATION_IMPLICIT);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Validate this TableElementList. This includes checking for\r
+ * duplicate columns names, and checking that user types really exist.\r
+ *\r
+ * @param ddlStmt DDLStatementNode which contains this list\r
+ * @param dd DataDictionary to use\r
+ * @param td TableDescriptor for table, if existing table.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ void validate(DDLStatementNode ddlStmt,\r
+ DataDictionary dd,\r
+ TableDescriptor td)\r
+ throws StandardException\r
+ {\r
+ this.td = td;\r
+ int numAutoCols = 0;\r
+\r
+ int size = size();\r
+ Hashtable columnHT = new Hashtable(size + 2, (float) .999);\r
+ Hashtable constraintHT = new Hashtable(size + 2, (float) .999);\r
+ //all the primary key/unique key constraints for this table\r
+ Vector constraintsVector = new Vector();\r
+\r
+ //special case for alter table (td is not null in case of alter table)\r
+ if (td != null)\r
+ {\r
+ //In case of alter table, get the already existing primary key and unique\r
+ //key constraints for this table. And then we will compare them with new\r
+ //primary key/unique key constraint column lists.\r
+ ConstraintDescriptorList cdl = dd.getConstraintDescriptors(td);\r
+ ConstraintDescriptor cd;\r
+\r
+ if (cdl != null) //table does have some pre-existing constraints defined on it\r
+ {\r
+ for (int i=0; i<cdl.size();i++)\r
+ {\r
+ cd = cdl.elementAt(i);\r
+ //if the constraint type is not primary key or unique key, ignore it.\r
+ if (cd.getConstraintType() == DataDictionary.PRIMARYKEY_CONSTRAINT ||\r
+ cd.getConstraintType() == DataDictionary.UNIQUE_CONSTRAINT)\r
+ constraintsVector.addElement(cd);\r
+ }\r
+ }\r
+ }\r
+\r
+ int tableType = TableDescriptor.BASE_TABLE_TYPE;\r
+ if (ddlStmt instanceof CreateTableNode)\r
+ tableType = ((CreateTableNode)ddlStmt).tableType;\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ TableElementNode tableElement = (TableElementNode) elementAt(index);\r
+\r
+ if (tableElement instanceof ColumnDefinitionNode)\r
+ {\r
+ ColumnDefinitionNode cdn = (ColumnDefinitionNode) elementAt(index);\r
+ if (tableType == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE &&\r
+ (cdn.getDataTypeServices().getTypeId().isLongConcatableTypeId() ||\r
+ cdn.getDataTypeServices().getTypeId().isUserDefinedTypeId()))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_LONG_DATA_TYPE_NOT_ALLOWED, cdn.getColumnName());\r
+ }\r
+ checkForDuplicateColumns(ddlStmt, columnHT, cdn.getColumnName());\r
+ cdn.checkUserType(td);\r
+ cdn.bindAndValidateDefault(dd, td);\r
+\r
+ cdn.validateAutoincrement(dd, td, tableType);\r
+\r
+ if (tableElement instanceof ModifyColumnNode)\r
+ {\r
+ ModifyColumnNode mcdn = (ModifyColumnNode)cdn;\r
+ mcdn.checkExistingConstraints(td);\r
+ mcdn.useExistingCollation(td);\r
+ } else if (cdn.isAutoincrementColumn())\r
+ numAutoCols ++;\r
+ }\r
+ else if (tableElement.getElementType() == TableElementNode.AT_DROP_COLUMN)\r
+ {\r
+ String colName = tableElement.getName();\r
+ if (td.getColumnDescriptor(colName) == null)\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,\r
+ colName,\r
+ td.getQualifiedName());\r
+ }\r
+ break;\r
+ }\r
+\r
+ /* The rest of this method deals with validating constraints */\r
+ if (! (tableElement.hasConstraint()))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ ConstraintDefinitionNode cdn = (ConstraintDefinitionNode) tableElement;\r
+\r
+ cdn.bind(ddlStmt, dd);\r
+\r
+ //if constraint is primary key or unique key, add it to the vector\r
+ if (cdn.getConstraintType() == DataDictionary.PRIMARYKEY_CONSTRAINT ||\r
+ cdn.getConstraintType() == DataDictionary.UNIQUE_CONSTRAINT)\r
+ {\r
+ /* In case of create table, the vector can have only ConstraintDefinitionNode\r
+ * elements. In case of alter table, it can have both ConstraintDefinitionNode\r
+ * (for new constraints) and ConstraintDescriptor(for pre-existing constraints).\r
+ */\r
+\r
+ Object destConstraint;\r
+ String destName = null;\r
+ String[] destColumnNames = null;\r
+\r
+ for (int i=0; i<constraintsVector.size();i++)\r
+ {\r
+\r
+ destConstraint = constraintsVector.elementAt(i);\r
+ if (destConstraint instanceof ConstraintDefinitionNode)\r
+ {\r
+ ConstraintDefinitionNode destCDN = (ConstraintDefinitionNode)destConstraint;\r
+ destName = destCDN.getConstraintMoniker();\r
+ destColumnNames = destCDN.getColumnList().getColumnNames();\r
+ }\r
+ else if (destConstraint instanceof ConstraintDescriptor)\r
+ {\r
+ //will come here only for pre-existing constraints in case of alter table\r
+ ConstraintDescriptor destCD = (ConstraintDescriptor)destConstraint;\r
+ destName = destCD.getConstraintName();\r
+ destColumnNames = destCD.getColumnDescriptors().getColumnNames();\r
+ }\r
+ //check if there are multiple constraints with same set of columns\r
+ if (columnsMatch(cdn.getColumnList().getColumnNames(), destColumnNames))\r
+ throw StandardException.newException(SQLState.LANG_MULTIPLE_CONSTRAINTS_WITH_SAME_COLUMNS,\r
+ cdn.getConstraintMoniker(), destName);\r
+ }\r
+ constraintsVector.addElement(cdn);\r
+ }\r
+\r
+ /* Make sure that there are no duplicate constraint names in the list */\r
+ if (cdn instanceof ConstraintDefinitionNode)\r
+ checkForDuplicateConstraintNames(ddlStmt, constraintHT, cdn.getConstraintMoniker());\r
+\r
+ /* Make sure that the constraint we are trying to drop exists */\r
+ if (cdn.getConstraintType() == DataDictionary.DROP_CONSTRAINT)\r
+ {\r
+ /*\r
+ ** If no schema descriptor, then must be an invalid\r
+ ** schema name.\r
+ */\r
+\r
+ String dropConstraintName = cdn.getConstraintMoniker();\r
+\r
+ if (dropConstraintName != null) {\r
+\r
+ String dropSchemaName = cdn.getDropSchemaName();\r
+\r
+ SchemaDescriptor sd = dropSchemaName == null ? td.getSchemaDescriptor() :\r
+ getSchemaDescriptor(dropSchemaName);\r
+\r
+ ConstraintDescriptor cd =\r
+ dd.getConstraintDescriptorByName(\r
+ td, sd, dropConstraintName,\r
+ false);\r
+ if (cd == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DROP_NON_EXISTENT_CONSTRAINT,\r
+ (sd.getSchemaName() + "."+ dropConstraintName),\r
+ td.getQualifiedName());\r
+ }\r
+ /* Statement is dependendent on the ConstraintDescriptor */\r
+ getCompilerContext().createDependency(cd);\r
+ }\r
+ }\r
+\r
+ if (cdn.hasPrimaryKeyConstraint())\r
+ {\r
+ // for PRIMARY KEY, check that columns are unique\r
+ verifyUniqueColumnList(ddlStmt, cdn);\r
+\r
+ if (td == null)\r
+ {\r
+ // in CREATE TABLE so set PRIMARY KEY columns to NOT NULL\r
+ setColumnListToNotNull(cdn);\r
+ }\r
+ else\r
+ {\r
+ // in ALTER TABLE so raise error if any columns are nullable\r
+ checkForNullColumns(cdn, td);\r
+ }\r
+ }\r
+ else if (cdn.hasUniqueKeyConstraint())\r
+ {\r
+ // for UNIQUE, check that columns are unique and NOT NULL\r
+ verifyUniqueColumnList(ddlStmt, cdn);\r
+ checkForNullColumns(cdn, td);\r
+ }\r
+ else if (cdn.hasForeignKeyConstraint())\r
+ {\r
+ // for FOREIGN KEY, check that columns are unique\r
+ verifyUniqueColumnList(ddlStmt, cdn);\r
+ }\r
+ }\r
+\r
+ /* Can have only one autoincrement column in DB2 mode */\r
+ if (numAutoCols > 1)\r
+ throw StandardException.newException(SQLState.LANG_MULTIPLE_AUTOINCREMENT_COLUMNS);\r
+\r
+ }\r
+\r
+ /**\r
+ * Count the number of constraints of the specified type.\r
+ *\r
+ * @param constraintType The constraint type to search for.\r
+ *\r
+ * @return int The number of constraints of the specified type.\r
+ */\r
+ public int countConstraints(int constraintType)\r
+ {\r
+ int numConstraints = 0;\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ConstraintDefinitionNode cdn;\r
+ TableElementNode element = (TableElementNode) elementAt(index);\r
+\r
+ if (! (element instanceof ConstraintDefinitionNode))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ cdn = (ConstraintDefinitionNode) element;\r
+\r
+ if (constraintType == cdn.getConstraintType())\r
+ {\r
+ numConstraints++;\r
+ }\r
+ }\r
+\r
+ return numConstraints;\r
+ }\r
+\r
+ /**\r
+ * Count the number of columns.\r
+ *\r
+ * @return int The number of columns.\r
+ */\r
+ public int countNumberOfColumns()\r
+ {\r
+ return numColumns;\r
+ }\r
+\r
+ /**\r
+ * Fill in the ColumnInfo[] for this table element list.\r
+ * \r
+ * @param colInfos The ColumnInfo[] to be filled in.\r
+ *\r
+ * @return int The number of constraints in the create table.\r
+ */\r
+ public int genColumnInfos(ColumnInfo[] colInfos)\r
+ {\r
+ int numConstraints = 0;\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ if (((TableElementNode) elementAt(index)).getElementType() == TableElementNode.AT_DROP_COLUMN)\r
+ {\r
+ colInfos[index] = new ColumnInfo(\r
+ ((TableElementNode) elementAt(index)).getName(),\r
+ null, null, null, null, null,\r
+ ColumnInfo.DROP, 0, 0, 0);\r
+ break;\r
+ }\r
+\r
+ if (! (elementAt(index) instanceof ColumnDefinitionNode))\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT( elementAt(index) instanceof ConstraintDefinitionNode,\r
+ "elementAt(index) expected to be instanceof " +\r
+ "ConstraintDefinitionNode");\r
+ }\r
+\r
+ /* Remember how many constraints that we've seen */\r
+ numConstraints++;\r
+ continue;\r
+ }\r
+\r
+ ColumnDefinitionNode coldef = (ColumnDefinitionNode) elementAt(index);\r
+\r
+ colInfos[index - numConstraints] = \r
+ new ColumnInfo(coldef.getColumnName(),\r
+ coldef.getDataTypeServices(),\r
+ coldef.getDefaultValue(),\r
+ coldef.getDefaultInfo(),\r
+ (UUID) null,\r
+ coldef.getOldDefaultUUID(),\r
+ coldef.getAction(),\r
+ (coldef.isAutoincrementColumn() ? \r
+ coldef.getAutoincrementStart() : 0),\r
+ (coldef.isAutoincrementColumn() ? \r
+ coldef.getAutoincrementIncrement() : 0),\r
+ (coldef.isAutoincrementColumn() ? \r
+ coldef.getAutoinc_create_or_modify_Start_Increment() : -1));\r
+\r
+ /* Remember how many constraints that we've seen */\r
+ if (coldef.hasConstraint())\r
+ {\r
+ numConstraints++;\r
+ }\r
+ }\r
+\r
+ return numConstraints;\r
+ }\r
+ /**\r
+ * Append goobered up ResultColumns to the table's RCL.\r
+ * This is useful for binding check constraints for CREATE and ALTER TABLE.\r
+ *\r
+ * @param table The table in question.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void appendNewColumnsToRCL(FromBaseTable table)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ ResultColumnList rcl = table.getResultColumns();\r
+ TableName exposedName = table.getTableName();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ if (elementAt(index) instanceof ColumnDefinitionNode)\r
+ {\r
+ ColumnDefinitionNode cdn = (ColumnDefinitionNode) elementAt(index);\r
+ ResultColumn resultColumn;\r
+ ValueNode valueNode;\r
+\r
+ /* Build a ResultColumn/BaseColumnNode pair for the column */\r
+ valueNode = (ValueNode) getNodeFactory().getNode(\r
+ C_NodeTypes.BASE_COLUMN_NODE,\r
+ cdn.getColumnName(),\r
+ exposedName,\r
+ cdn.getDataTypeServices(),\r
+ getContextManager());\r
+\r
+ resultColumn = (ResultColumn) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN,\r
+ cdn.getDataTypeServices(), \r
+ valueNode,\r
+ getContextManager());\r
+ resultColumn.setName(cdn.getColumnName());\r
+ rcl.addElement(resultColumn);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Bind and validate all of the check constraints in this list against\r
+ * the specified FromList. \r
+ *\r
+ * @param fromList The FromList in question.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ void bindAndValidateCheckConstraints(FromList fromList)\r
+ throws StandardException\r
+ {\r
+ CompilerContext cc;\r
+ FromBaseTable table = (FromBaseTable) fromList.elementAt(0);\r
+ int size = size();\r
+\r
+ cc = getCompilerContext();\r
+\r
+ Vector aggregateVector = new Vector();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ConstraintDefinitionNode cdn;\r
+ TableElementNode element = (TableElementNode) elementAt(index);\r
+ ValueNode checkTree;\r
+\r
+ if (! (element instanceof ConstraintDefinitionNode))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ cdn = (ConstraintDefinitionNode) element;\r
+\r
+ if (cdn.getConstraintType() != DataDictionary.CHECK_CONSTRAINT)\r
+ {\r
+ continue;\r
+ }\r
+\r
+ checkTree = cdn.getCheckCondition();\r
+\r
+ // bind the check condition\r
+ // verify that it evaluates to a boolean\r
+ final int previousReliability = cc.getReliability();\r
+ try\r
+ {\r
+ /* Each check constraint can have its own set of dependencies.\r
+ * These dependencies need to be shared with the prepared\r
+ * statement as well. We create a new auxiliary provider list\r
+ * for the check constraint, "push" it on the compiler context\r
+ * by swapping it with the current auxiliary provider list\r
+ * and the "pop" it when we're done by restoring the old \r
+ * auxiliary provider list.\r
+ */\r
+ ProviderList apl = new ProviderList();\r
+\r
+ ProviderList prevAPL = cc.getCurrentAuxiliaryProviderList();\r
+ cc.setCurrentAuxiliaryProviderList(apl);\r
+\r
+ // Tell the compiler context to only allow deterministic nodes\r
+ cc.setReliability( CompilerContext.CHECK_CONSTRAINT );\r
+ checkTree = checkTree.bindExpression(fromList, (SubqueryList) null,\r
+ aggregateVector);\r
+\r
+ // no aggregates, please\r
+ if (aggregateVector.size() != 0)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_CHECK_CONSTRAINT, cdn.getConstraintText());\r
+ }\r
+ \r
+ checkTree = checkTree.checkIsBoolean();\r
+ cdn.setCheckCondition(checkTree);\r
+\r
+ /* Save the APL off in the constraint node */\r
+ if (apl.size() > 0)\r
+ {\r
+ cdn.setAuxiliaryProviderList(apl);\r
+ }\r
+\r
+ // Restore the previous AuxiliaryProviderList\r
+ cc.setCurrentAuxiliaryProviderList(prevAPL);\r
+ }\r
+ finally\r
+ {\r
+ cc.setReliability(previousReliability);\r
+ }\r
+ \r
+ /* We have a valid check constraint, now build an array of\r
+ * 1-based columnIds that the constraint references.\r
+ */\r
+ ResultColumnList rcl = table.getResultColumns();\r
+ int numReferenced = rcl.countReferencedColumns();\r
+ int[] checkColumnReferences = new int[numReferenced];\r
+\r
+ rcl.recordColumnReferences(checkColumnReferences, 1);\r
+ cdn.setCheckColumnReferences(checkColumnReferences);\r
+\r
+ /* Now we build a list with only the referenced columns and\r
+ * copy it to the cdn. Thus we can build the array of\r
+ * column names for the referenced columns during generate().\r
+ */\r
+ ResultColumnList refRCL =\r
+ (ResultColumnList) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN_LIST,\r
+ getContextManager());\r
+ rcl.copyReferencedColumnsToNewList(refRCL);\r
+\r
+ /* A column check constraint can only refer to that column. If this is a\r
+ * column constraint, we should have an RCL with that column\r
+ */\r
+ if (cdn.getColumnList() != null)\r
+ {\r
+ String colName = ((ResultColumn)(cdn.getColumnList().elementAt(0))).getName();\r
+ if (numReferenced > 1 ||\r
+ !colName.equals(((ResultColumn)(refRCL.elementAt(0))).getName()))\r
+ throw StandardException.newException(SQLState.LANG_DB2_INVALID_CHECK_CONSTRAINT, colName);\r
+ \r
+ }\r
+ cdn.setColumnList(refRCL);\r
+\r
+ /* Clear the column references in the RCL so each check constraint\r
+ * starts with a clean list.\r
+ */\r
+ rcl.clearColumnReferences();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Fill in the ConstraintConstantAction[] for this create/alter table.\r
+ * \r
+ * @param conActions The ConstraintConstantAction[] to be filled in.\r
+ * @param tableName The name of the Table being created.\r
+ * @param tableSd The schema for that table.\r
+ * @param dd The DataDictionary\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ */\r
+ void genConstraintActions(\r
+ ConstraintConstantAction[] conActions,\r
+ String tableName,\r
+ SchemaDescriptor tableSd,\r
+ DataDictionary dd)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ int conActionIndex = 0;\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ String[] columnNames = null;\r
+ TableElementNode ten = (TableElementNode) elementAt(index);\r
+ IndexConstantAction indexAction = null;\r
+\r
+ if (! ten.hasConstraint())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ if (ten instanceof ColumnDefinitionNode)\r
+ {\r
+ continue;\r
+ }\r
+\r
+ ConstraintDefinitionNode constraintDN = (ConstraintDefinitionNode) ten;\r
+\r
+ if (constraintDN.getColumnList() != null)\r
+ {\r
+ columnNames = new String[constraintDN.getColumnList().size()];\r
+ constraintDN.getColumnList().exportNames(columnNames);\r
+ }\r
+\r
+ int constraintType = constraintDN.getConstraintType();\r
+ String constraintText = constraintDN.getConstraintText();\r
+\r
+ /*\r
+ ** If the constraint is not named (e.g.\r
+ ** create table x (x int primary key)), then\r
+ ** the constraintSd is the same as the table.\r
+ */\r
+ String constraintName = constraintDN.getConstraintMoniker();\r
+\r
+ /* At execution time, we will generate a unique name for the backing\r
+ * index (for CREATE CONSTRAINT) and we will look up the conglomerate\r
+ * name (for DROP CONSTRAINT).\r
+ */\r
+ if (constraintDN.requiresBackingIndex())\r
+ {\r
+ indexAction = genIndexAction(constraintDN.requiresUniqueIndex(),\r
+ null, constraintDN, \r
+ columnNames, true, tableSd, tableName,\r
+ constraintType, dd);\r
+ }\r
+\r
+ if (constraintType == DataDictionary.DROP_CONSTRAINT)\r
+ {\r
+ conActions[conActionIndex] = \r
+ getGenericConstantActionFactory().\r
+ getDropConstraintConstantAction(\r
+ constraintName, \r
+ constraintDN.getDropSchemaName(), /// FiX\r
+ tableName,\r
+ td.getUUID(),\r
+ tableSd.getSchemaName(),\r
+ indexAction,\r
+ constraintDN.getDropBehavior(),\r
+ constraintDN.getVerifyType());\r
+ }\r
+ else\r
+ {\r
+ ProviderList apl = constraintDN.getAuxiliaryProviderList();\r
+ ConstraintInfo refInfo = null;\r
+ ProviderInfo[] providerInfos = null;\r
+\r
+ if (constraintDN instanceof FKConstraintDefinitionNode)\r
+ {\r
+ refInfo = ((FKConstraintDefinitionNode)constraintDN).getReferencedConstraintInfo();\r
+ } \r
+\r
+ /* Create the ProviderInfos, if the constraint is dependent on any Providers */\r
+ if (apl != null && apl.size() > 0)\r
+ {\r
+ /* Get all the dependencies for the current statement and transfer\r
+ * them to this view.\r
+ */\r
+ DependencyManager dm = dd.getDependencyManager();\r
+ providerInfos = dm.getPersistentProviderInfos(apl);\r
+ }\r
+ else\r
+ {\r
+ providerInfos = new ProviderInfo[0];\r
+ // System.out.println("TABLE ELEMENT LIST EMPTY");\r
+ }\r
+\r
+ conActions[conActionIndex++] = \r
+ getGenericConstantActionFactory().\r
+ getCreateConstraintConstantAction(\r
+ constraintName, \r
+ constraintType,\r
+ tableName,\r
+ ((td != null) ? td.getUUID() : (UUID) null),\r
+ tableSd.getSchemaName(),\r
+ columnNames,\r
+ indexAction,\r
+ constraintText,\r
+ true, // enabled\r
+ refInfo,\r
+ providerInfos);\r
+ }\r
+ }\r
+ }\r
+\r
+ //check if one array is same as another \r
+ private boolean columnsMatch(String[] columnNames1, String[] columnNames2)\r
+ {\r
+ int srcCount, srcSize, destCount,destSize;\r
+ boolean match = true;\r
+\r
+ if (columnNames1.length != columnNames2.length)\r
+ return false;\r
+\r
+ srcSize = columnNames1.length;\r
+ destSize = columnNames2.length;\r
+\r
+ for (srcCount = 0; srcCount < srcSize; srcCount++)\r
+ {\r
+ match = false;\r
+ for (destCount = 0; destCount < destSize; destCount++) {\r
+ if (columnNames1[srcCount].equals(columnNames2[destCount])) {\r
+ match = true;\r
+ break;\r
+ }\r
+ }\r
+ if (match == false)\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ private IndexConstantAction genIndexAction(\r
+ boolean isUnique,\r
+ String indexName,\r
+ ConstraintDefinitionNode cdn,\r
+ String[] columnNames,\r
+ boolean isConstraint,\r
+ SchemaDescriptor sd,\r
+ String tableName,\r
+ int constraintType,\r
+ DataDictionary dd)\r
+ throws StandardException\r
+ {\r
+ if ( indexName == null ) { indexName = cdn.getBackingIndexName(dd); }\r
+\r
+ if (constraintType == DataDictionary.DROP_CONSTRAINT)\r
+ {\r
+ return getGenericConstantActionFactory().getDropIndexConstantAction(\r
+ null,\r
+ indexName,\r
+ tableName,\r
+ sd.getSchemaName(),\r
+ td.getUUID(),\r
+ td.getHeapConglomerateId());\r
+ }\r
+ else\r
+ {\r
+ boolean[] isAscending = new boolean[columnNames.length];\r
+ for (int i = 0; i < isAscending.length; i++)\r
+ isAscending[i] = true;\r
+ return getGenericConstantActionFactory().getCreateIndexConstantAction(\r
+ isUnique,\r
+ "BTREE", // indexType\r
+ sd.getSchemaName(),\r
+ indexName,\r
+ tableName,\r
+ ((td != null) ? td.getUUID() : (UUID) null),\r
+ columnNames,\r
+ isAscending,\r
+ isConstraint,\r
+ cdn.getBackingIndexUUID(),\r
+ cdn.getProperties());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Check to make sure that there are no duplicate column names\r
+ * in the list. (The comparison here is case sensitive.\r
+ * The work of converting column names that are not quoted\r
+ * identifiers to upper case is handled by the parser.)\r
+ * RESOLVE: This check will also be performed by alter table.\r
+ *\r
+ * @param ddlStmt DDLStatementNode which contains this list\r
+ * @param ht Hashtable for enforcing uniqueness.\r
+ * @param colName Column name to check for.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void checkForDuplicateColumns(DDLStatementNode ddlStmt,\r
+ Hashtable ht,\r
+ String colName)\r
+ throws StandardException\r
+ {\r
+ Object object = ht.put(colName, colName);\r
+ if (object != null)\r
+ {\r
+ /* RESOLVE - different error messages for create and alter table */\r
+ if (ddlStmt instanceof CreateTableNode)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_NAME_CREATE, colName);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Check to make sure that there are no duplicate constraint names\r
+ * in the list. (The comparison here is case sensitive.\r
+ * The work of converting column names that are not quoted\r
+ * identifiers to upper case is handled by the parser.)\r
+ * RESOLVE: This check will also be performed by alter table.\r
+ *\r
+ * @param ddlStmt DDLStatementNode which contains this list\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void checkForDuplicateConstraintNames(DDLStatementNode ddlStmt,\r
+ Hashtable ht,\r
+ String constraintName)\r
+ throws StandardException\r
+ {\r
+ if (constraintName == null)\r
+ return;\r
+\r
+ Object object = ht.put(constraintName, constraintName);\r
+ if (object != null) {\r
+\r
+ /* RESOLVE - different error messages for create and alter table */\r
+ if (ddlStmt instanceof CreateTableNode)\r
+ {\r
+ /* RESOLVE - new error message */\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_CONSTRAINT_NAME_CREATE, \r
+ constraintName);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Verify that a primary/unique table constraint has a valid column list.\r
+ * (All columns in table and no duplicates.)\r
+ *\r
+ * @param ddlStmt The outer DDLStatementNode\r
+ * @param cdn The ConstraintDefinitionNode\r
+ *\r
+ * @exception StandardException Thrown if the column list is invalid\r
+ */\r
+ private void verifyUniqueColumnList(DDLStatementNode ddlStmt,\r
+ ConstraintDefinitionNode cdn)\r
+ throws StandardException\r
+ {\r
+ String invalidColName;\r
+\r
+ /* Verify that every column in the list appears in the table's list of columns */\r
+ if (ddlStmt instanceof CreateTableNode)\r
+ {\r
+ invalidColName = cdn.getColumnList().verifyCreateConstraintColumnList(this);\r
+ if (invalidColName != null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_CREATE_CONSTRAINT_COLUMN_LIST, \r
+ ddlStmt.getRelativeName(),\r
+ invalidColName);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* RESOLVE - alter table will need to get table descriptor */\r
+ }\r
+\r
+ /* Check the uniqueness of the column names within the list */\r
+ invalidColName = cdn.getColumnList().verifyUniqueNames(false);\r
+ if (invalidColName != null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_CONSTRAINT_COLUMN_NAME, invalidColName);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Set all columns in that appear in a PRIMARY KEY constraint in a CREATE TABLE statement to NOT NULL.\r
+ *\r
+ * @param cdn The ConstraintDefinitionNode for a PRIMARY KEY constraint\r
+ */\r
+ private void setColumnListToNotNull(ConstraintDefinitionNode cdn)\r
+ {\r
+ ResultColumnList rcl = cdn.getColumnList();\r
+ int rclSize = rcl.size();\r
+ for (int index = 0; index < rclSize; index++)\r
+ {\r
+ String colName = ((ResultColumn) rcl.elementAt(index)).getName();\r
+ DataTypeDescriptor dtd = getColumnDataTypeDescriptor(colName);\r
+ dtd.setNullability(false);\r
+ }\r
+ }\r
+\r
+\r
+ private void checkForNullColumns(ConstraintDefinitionNode cdn, TableDescriptor td) throws StandardException\r
+ {\r
+ ResultColumnList rcl = cdn.getColumnList();\r
+ int rclSize = rcl.size();\r
+ for (int index = 0; index < rclSize; index++)\r
+ {\r
+ String colName = ((ResultColumn) rcl.elementAt(index)).getName();\r
+ DataTypeDescriptor dtd;\r
+ if (td == null)\r
+ {\r
+ dtd = getColumnDataTypeDescriptor(colName);\r
+ }\r
+ else\r
+ {\r
+ dtd = getColumnDataTypeDescriptor(colName, td);\r
+ }\r
+ // todo dtd may be null if the column does not exist, we should check that first\r
+ if (dtd != null && dtd.isNullable())\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DB2_ADD_UNIQUE_OR_PRIMARY_KEY_ON_NULL_COLS, colName);\r
+ }\r
+ }\r
+ }\r
+\r
+ private DataTypeDescriptor getColumnDataTypeDescriptor(String colName)\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ TableElementNode tableElement = (TableElementNode) elementAt(index);\r
+\r
+ if (tableElement instanceof ColumnDefinitionNode)\r
+ {\r
+ ColumnDefinitionNode cdn = (ColumnDefinitionNode) tableElement;\r
+ if (colName.equals(cdn.getColumnName()))\r
+ {\r
+ return cdn.getDataTypeServices();\r
+ }\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ private DataTypeDescriptor getColumnDataTypeDescriptor(String colName, TableDescriptor td)\r
+ {\r
+ // check existing columns\r
+ ColumnDescriptor cd = td.getColumnDescriptor(colName);\r
+ if (cd != null)\r
+ {\r
+ return cd.getType();\r
+ }\r
+ // check for new columns\r
+ return getColumnDataTypeDescriptor(colName);\r
+ }\r
+\r
+ /**\r
+ * Determine whether or not the parameter matches a column name in this list.\r
+ *\r
+ * @param colName The column name to search for.\r
+ *\r
+ * @return boolean Whether or not a match is found.\r
+ */\r
+ public boolean containsColumnName(String colName)\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ TableElementNode tableElement = (TableElementNode) elementAt(index);\r
+\r
+ if (tableElement instanceof ColumnDefinitionNode)\r
+ {\r
+ if (colName.equals(((ColumnDefinitionNode) tableElement).getName()))\r
+ {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+\r
+ return false;\r
+ }\r
+}\r
+\r