--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.CreateConstraintConstantAction\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.execute;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+\r
+import org.apache.derby.catalog.UUID;\r
+import org.apache.derby.iapi.services.uuid.UUIDFactory;\r
+import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DDUtils;\r
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.ForeignKeyConstraintDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ReferencedKeyConstraintDescriptor;\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.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+import org.apache.derby.iapi.sql.depend.Provider;\r
+import org.apache.derby.iapi.sql.depend.ProviderInfo;\r
+\r
+import org.apache.derby.iapi.sql.execute.ConstantAction;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.services.loader.ClassFactory;\r
+\r
+/**\r
+ * This class describes actions that are ALWAYS performed for a\r
+ * constraint creation at Execution time.\r
+ *\r
+ * @version 0.1\r
+ */\r
+\r
+public class CreateConstraintConstantAction extends ConstraintConstantAction\r
+{\r
+ String[] columnNames;\r
+ private String constraintText;\r
+\r
+ private ConstraintInfo otherConstraintInfo;\r
+ private ClassFactory cf;\r
+\r
+ /*\r
+ ** Is this constraint to be created as enabled or not.\r
+ ** The only way to create a disabled constraint is by\r
+ ** publishing a disabled constraint.\r
+ */\r
+ private boolean enabled;\r
+\r
+ private ProviderInfo[] providerInfo;\r
+\r
+ // CONSTRUCTORS\r
+\r
+ /**\r
+ * Make one of these puppies.\r
+ *\r
+ * @param constraintName Constraint name.\r
+ * @param constraintType Constraint type.\r
+ * @param tableName Table name.\r
+ * @param tableId UUID of table.\r
+ * @param schemaName the schema that table and constraint lives in.\r
+ * @param columnNames String[] for column names\r
+ * @param indexAction IndexConstantAction for constraint (if necessary)\r
+ * @param constraintText Text for check constraint\r
+ * RESOLVE - the next parameter should go away once we use UUIDs\r
+ * (Generated constraint names will be based off of uuids)\r
+ * @param enabled Should the constraint be created as enabled \r
+ * (enabled == true), or disabled (enabled == false).\r
+ * @param otherConstraint information about the constraint that this references\r
+ * @param providerInfo Information on all the Providers\r
+ */\r
+ CreateConstraintConstantAction(\r
+ String constraintName,\r
+ int constraintType,\r
+ String tableName,\r
+ UUID tableId,\r
+ String schemaName,\r
+ String[] columnNames,\r
+ IndexConstantAction indexAction,\r
+ String constraintText,\r
+ boolean enabled,\r
+ ConstraintInfo otherConstraint,\r
+ ProviderInfo[] providerInfo)\r
+ {\r
+ super(constraintName, constraintType, tableName, \r
+ tableId, schemaName, indexAction);\r
+ this.columnNames = columnNames;\r
+ this.constraintText = constraintText;\r
+ this.enabled = enabled;\r
+ this.otherConstraintInfo = otherConstraint;\r
+ this.providerInfo = providerInfo;\r
+ }\r
+\r
+ // INTERFACE METHODS\r
+\r
+\r
+ /**\r
+ * This is the guts of the Execution-time logic for CREATE CONSTRAINT.\r
+ * <P>\r
+ * A constraint is represented as:\r
+ * <UL>\r
+ * <LI> ConstraintDescriptor.\r
+ * </UL>\r
+ * If a backing index is required then the index will\r
+ * be created through an CreateIndexConstantAction setup\r
+ * by the compiler.\r
+ * <BR>\r
+ * Dependencies are created as:\r
+ * <UL>\r
+ * <LI> ConstraintDescriptor depends on all the providers collected\r
+ * at compile time and passed into the constructor.\r
+ * <LI> For a FOREIGN KEY constraint ConstraintDescriptor depends\r
+ * on the ConstraintDescriptor for the referenced constraints\r
+ * and the privileges required to create the constraint.\r
+ * </UL>\r
+\r
+ * @see ConstraintDescriptor\r
+ * @see CreateIndexConstantAction\r
+ * @see ConstantAction#executeConstantAction\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ */\r
+ public void executeConstantAction( Activation activation )\r
+ throws StandardException\r
+ {\r
+ boolean forCreateTable;\r
+ ConglomerateDescriptor conglomDesc = null;\r
+ ConglomerateDescriptor[] conglomDescs = null;\r
+ ConstraintDescriptor conDesc = null;\r
+ TableDescriptor td = null;\r
+ UUID indexId = null;\r
+ String uniqueName;\r
+ String backingIndexName;\r
+\r
+ /* RESOLVE - blow off not null constraints for now (and probably for ever) */\r
+ if (constraintType == DataDictionary.NOTNULL_CONSTRAINT)\r
+ {\r
+ return;\r
+ }\r
+\r
+ LanguageConnectionContext lcc = activation.getLanguageConnectionContext();\r
+ DataDictionary dd = lcc.getDataDictionary();\r
+ DependencyManager dm = dd.getDependencyManager();\r
+ TransactionController tc = lcc.getTransactionExecute();\r
+\r
+ cf = lcc.getLanguageConnectionFactory().getClassFactory();\r
+\r
+ /* Remember whether or not we are doing a create table */\r
+ forCreateTable = activation.getForCreateTable();\r
+\r
+ /*\r
+ ** Inform the data dictionary that we are about to write to it.\r
+ ** There are several calls to data dictionary "get" methods here\r
+ ** that might be done in "read" mode in the data dictionary, but\r
+ ** it seemed safer to do this whole operation in "write" mode.\r
+ **\r
+ ** We tell the data dictionary we're done writing at the end of\r
+ ** the transaction.\r
+ */\r
+ dd.startWriting(lcc);\r
+\r
+ /* Table gets locked in AlterTableConstantAction */\r
+\r
+ /*\r
+ ** If the schema descriptor is null, then\r
+ ** we must have just read ourselves in. \r
+ ** So we will get the corresponding schema\r
+ ** descriptor from the data dictionary.\r
+ */\r
+\r
+ SchemaDescriptor sd = dd.getSchemaDescriptor(schemaName, tc, true);\r
+ \r
+ /* Try to get the TableDescriptor from\r
+ * the Activation. We will go to the\r
+ * DD if not there. (It should always be\r
+ * there except when in a target.)\r
+ */\r
+ td = activation.getDDLTableDescriptor();\r
+\r
+ if (td == null)\r
+ {\r
+ /* tableId will be non-null if adding a\r
+ * constraint to an existing table.\r
+ */\r
+ if (tableId != null)\r
+ {\r
+ td = dd.getTableDescriptor(tableId);\r
+ }\r
+ else\r
+ {\r
+ td = dd.getTableDescriptor(tableName, sd);\r
+ }\r
+\r
+ if (td == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tableName);\r
+ }\r
+ activation.setDDLTableDescriptor(td);\r
+ }\r
+\r
+ /* Generate the UUID for the backing index. This will become the\r
+ * constraint's name, if no name was specified.\r
+ */\r
+ UUIDFactory uuidFactory = dd.getUUIDFactory();\r
+ \r
+ /* Create the index, if there's one for this constraint */\r
+ if (indexAction != null)\r
+ {\r
+ if ( indexAction.getIndexName() == null )\r
+ {\r
+ /* Set the index name */\r
+ backingIndexName = uuidFactory.createUUID().toString();\r
+ indexAction.setIndexName(backingIndexName);\r
+ }\r
+ else { backingIndexName = indexAction.getIndexName(); }\r
+\r
+\r
+ /* Create the index */\r
+ indexAction.executeConstantAction(activation);\r
+\r
+ /* Get the conglomerate descriptor for the backing index */\r
+ conglomDescs = td.getConglomerateDescriptors();\r
+\r
+ for (int index = 0; index < conglomDescs.length; index++)\r
+ {\r
+ conglomDesc = conglomDescs[index];\r
+\r
+ /* Check for conglomerate being an index first, since\r
+ * name is null for heap.\r
+ */\r
+ if (conglomDesc.isIndex() && \r
+ backingIndexName.equals(conglomDesc.getConglomerateName()))\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(conglomDesc != null,\r
+ "conglomDesc is expected to be non-null after search for backing index");\r
+ SanityManager.ASSERT(conglomDesc.isIndex(),\r
+ "conglomDesc is expected to be indexable after search for backing index");\r
+ SanityManager.ASSERT(conglomDesc.getConglomerateName().equals(backingIndexName),\r
+ "conglomDesc name expected to be the same as backing index name after search for backing index");\r
+ }\r
+\r
+ indexId = conglomDesc.getUUID();\r
+ }\r
+\r
+ UUID constraintId = uuidFactory.createUUID();\r
+\r
+ /* Now, lets create the constraint descriptor */\r
+ DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();\r
+ switch (constraintType)\r
+ {\r
+ case DataDictionary.PRIMARYKEY_CONSTRAINT:\r
+ conDesc = ddg.newPrimaryKeyConstraintDescriptor(\r
+ td, constraintName,\r
+ false, //deferable,\r
+ false, //initiallyDeferred,\r
+ genColumnPositions(td, false), //int[],\r
+ constraintId, \r
+ indexId, \r
+ sd,\r
+ enabled,\r
+ 0 // referenceCount\r
+ );\r
+ dd.addConstraintDescriptor(conDesc, tc);\r
+ break;\r
+\r
+ case DataDictionary.UNIQUE_CONSTRAINT:\r
+ conDesc = ddg.newUniqueConstraintDescriptor(\r
+ td, constraintName,\r
+ false, //deferable,\r
+ false, //initiallyDeferred,\r
+ genColumnPositions(td, false), //int[],\r
+ constraintId, \r
+ indexId, \r
+ sd,\r
+ enabled,\r
+ 0 // referenceCount\r
+ );\r
+ dd.addConstraintDescriptor(conDesc, tc);\r
+ break;\r
+\r
+ case DataDictionary.CHECK_CONSTRAINT:\r
+ conDesc = ddg.newCheckConstraintDescriptor(\r
+ td, constraintName,\r
+ false, //deferable,\r
+ false, //initiallyDeferred,\r
+ constraintId, \r
+ constraintText, \r
+ new ReferencedColumnsDescriptorImpl(genColumnPositions(td, false)), //int[],\r
+ sd,\r
+ enabled\r
+ );\r
+ dd.addConstraintDescriptor(conDesc, tc);\r
+ break;\r
+\r
+ case DataDictionary.FOREIGNKEY_CONSTRAINT:\r
+ ReferencedKeyConstraintDescriptor referencedConstraint = DDUtils.locateReferencedConstraint\r
+ ( dd, td, constraintName, columnNames, otherConstraintInfo );\r
+ DDUtils.validateReferentialActions(dd, td, constraintName, otherConstraintInfo,columnNames);\r
+ \r
+ conDesc = ddg.newForeignKeyConstraintDescriptor(\r
+ td, constraintName,\r
+ false, //deferable,\r
+ false, //initiallyDeferred,\r
+ genColumnPositions(td, false), //int[],\r
+ constraintId,\r
+ indexId,\r
+ sd,\r
+ referencedConstraint,\r
+ enabled,\r
+ otherConstraintInfo.getReferentialActionDeleteRule(),\r
+ otherConstraintInfo.getReferentialActionUpdateRule()\r
+ );\r
+\r
+ // try to create the constraint first, because it\r
+ // is expensive to do the bulk check, find obvious\r
+ // errors first\r
+ dd.addConstraintDescriptor(conDesc, tc);\r
+\r
+ /* No need to do check if we're creating a \r
+ * table.\r
+ */\r
+ if ( (! forCreateTable) && \r
+ dd.activeConstraint( conDesc ) )\r
+ {\r
+ validateFKConstraint(tc, \r
+ dd, \r
+ (ForeignKeyConstraintDescriptor)conDesc, \r
+ referencedConstraint,\r
+ ((CreateIndexConstantAction)indexAction).getIndexTemplateRow());\r
+ }\r
+ \r
+ /* Create stored dependency on the referenced constraint */\r
+ dm.addDependency(conDesc, referencedConstraint, lcc.getContextManager());\r
+ //store constraint's dependency on REFERENCES privileges in the dependeny system\r
+ storeConstraintDependenciesOnPrivileges(activation, conDesc, referencedConstraint.getTableId()); \r
+ break;\r
+\r
+ default:\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT("contraintType (" + constraintType + \r
+ ") has unexpected value");\r
+ }\r
+ break;\r
+ }\r
+\r
+ /* Create stored dependencies for each provider */\r
+ if (providerInfo != null)\r
+ {\r
+ for (int ix = 0; ix < providerInfo.length; ix++)\r
+ {\r
+ Provider provider = null;\r
+ \r
+ /* We should always be able to find the Provider */\r
+ provider = (Provider) providerInfo[ix].\r
+ getDependableFinder().\r
+ getDependable(dd, \r
+ providerInfo[ix].getObjectId());\r
+\r
+ dm.addDependency(conDesc, provider, lcc.getContextManager());\r
+ }\r
+ }\r
+\r
+ /* Finally, invalidate off of the table descriptor(s)\r
+ * to ensure that any dependent statements get\r
+ * re-compiled.\r
+ */\r
+ if (! forCreateTable)\r
+ {\r
+ dm.invalidateFor(td, DependencyManager.CREATE_CONSTRAINT, lcc);\r
+ }\r
+ if (constraintType == DataDictionary.FOREIGNKEY_CONSTRAINT)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(conDesc != null,\r
+ "conDesc expected to be non-null");\r
+\r
+ if (! (conDesc instanceof ForeignKeyConstraintDescriptor))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "conDesc expected to be instance of ForeignKeyConstraintDescriptor, not " +\r
+ conDesc.getClass().getName());\r
+ }\r
+ }\r
+ dm.invalidateFor(\r
+ ((ForeignKeyConstraintDescriptor)conDesc).\r
+ getReferencedConstraint().\r
+ getTableDescriptor(),\r
+ DependencyManager.CREATE_CONSTRAINT, lcc);\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Is the constant action for a foreign key\r
+ *\r
+ * @return true/false\r
+ */\r
+ boolean isForeignKeyConstraint()\r
+ { \r
+ return (constraintType == DataDictionary.FOREIGNKEY_CONSTRAINT);\r
+ }\r
+\r
+ /**\r
+ * Generate an array of column positions for the column list in\r
+ * the constraint.\r
+ *\r
+ * @param td The TableDescriptor for the table in question\r
+ * @param columnsMustBeOrderable true for primaryKey and unique constraints\r
+ *\r
+ * @return int[] The column positions.\r
+ */\r
+ private int[] genColumnPositions(TableDescriptor td,\r
+ boolean columnsMustBeOrderable)\r
+ throws StandardException\r
+ {\r
+ int[] baseColumnPositions;\r
+\r
+ // Translate the base column names to column positions\r
+ baseColumnPositions = new int[columnNames.length];\r
+ for (int i = 0; i < columnNames.length; i++)\r
+ {\r
+ ColumnDescriptor columnDescriptor;\r
+\r
+ // Look up the column in the data dictionary\r
+ columnDescriptor = td.getColumnDescriptor(columnNames[i]);\r
+ if (columnDescriptor == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, \r
+ columnNames[i],\r
+ tableName);\r
+ }\r
+\r
+ // Don't allow a column to be created on a non-orderable type\r
+ // (for primaryKey and unique constraints)\r
+ if ( columnsMustBeOrderable &&\r
+ ( ! columnDescriptor.getType().getTypeId().orderable(\r
+ cf))\r
+ )\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_COLUMN_NOT_ORDERABLE_DURING_EXECUTION, \r
+ columnDescriptor.getType().getTypeId().getSQLTypeName());\r
+ }\r
+\r
+ // Remember the position in the base table of each column\r
+ baseColumnPositions[i] = columnDescriptor.getPosition();\r
+ }\r
+\r
+ return baseColumnPositions;\r
+ }\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // ACCESSORS\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+\r
+\r
+ /**\r
+ * Get the text defining this constraint.\r
+ *\r
+ * @return constraint text\r
+ */\r
+ String getConstraintText() { return constraintText; }\r
+\r
+ public String toString()\r
+ {\r
+ // Do not put this under SanityManager.DEBUG - it is needed for\r
+ // error reporting.\r
+ StringBuffer strbuf = new StringBuffer();\r
+ strbuf.append( "CREATE CONSTRAINT " + constraintName );\r
+ strbuf.append("\n=========================\n");\r
+\r
+ if (columnNames == null)\r
+ {\r
+ strbuf.append("columnNames == null\n");\r
+ }\r
+ else\r
+ {\r
+ for (int ix=0; ix < columnNames.length; ix++)\r
+ {\r
+ strbuf.append("\n\tcol["+ix+"]"+columnNames[ix].toString());\r
+ }\r
+ }\r
+ \r
+ strbuf.append("\n");\r
+ strbuf.append(constraintText);\r
+ strbuf.append("\n");\r
+ if (otherConstraintInfo != null)\r
+ {\r
+ strbuf.append(otherConstraintInfo.toString());\r
+ }\r
+ strbuf.append("\n");\r
+ return strbuf.toString();\r
+ }\r
+}\r