001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.CreateConstraintConstantAction
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.execute;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025:
026: import org.apache.derby.catalog.UUID;
027: import org.apache.derby.iapi.services.uuid.UUIDFactory;
028: import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
029:
030: import org.apache.derby.iapi.error.StandardException;
031:
032: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
033:
034: import org.apache.derby.iapi.sql.dictionary.DDUtils;
035: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
036: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
037: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
038: import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
039: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
040: import org.apache.derby.iapi.sql.dictionary.ForeignKeyConstraintDescriptor;
041: import org.apache.derby.iapi.sql.dictionary.ReferencedKeyConstraintDescriptor;
042: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
043: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
044:
045: import org.apache.derby.iapi.reference.SQLState;
046:
047: import org.apache.derby.iapi.sql.depend.DependencyManager;
048: import org.apache.derby.iapi.sql.depend.Provider;
049: import org.apache.derby.iapi.sql.depend.ProviderInfo;
050:
051: import org.apache.derby.iapi.sql.execute.ConstantAction;
052:
053: import org.apache.derby.iapi.sql.Activation;
054:
055: import org.apache.derby.iapi.store.access.TransactionController;
056: import org.apache.derby.iapi.services.loader.ClassFactory;
057:
058: /**
059: * This class describes actions that are ALWAYS performed for a
060: * constraint creation at Execution time.
061: *
062: * @version 0.1
063: * @author Jerry Brenner
064: */
065:
066: public class CreateConstraintConstantAction extends
067: ConstraintConstantAction {
068: String[] columnNames;
069: private String constraintText;
070:
071: private ConstraintInfo otherConstraintInfo;
072: private ClassFactory cf;
073:
074: /*
075: ** Is this constraint to be created as enabled or not.
076: ** The only way to create a disabled constraint is by
077: ** publishing a disabled constraint.
078: */
079: private boolean enabled;
080:
081: private ProviderInfo[] providerInfo;
082:
083: // CONSTRUCTORS
084:
085: /**
086: * Make one of these puppies.
087: *
088: * @param constraintName Constraint name.
089: * @param constraintType Constraint type.
090: * @param tableName Table name.
091: * @param tableId UUID of table.
092: * @param schemaName the schema that table and constraint lives in.
093: * @param columnNames String[] for column names
094: * @param indexAction IndexConstantAction for constraint (if necessary)
095: * @param constraintText Text for check constraint
096: * RESOLVE - the next parameter should go away once we use UUIDs
097: * (Generated constraint names will be based off of uuids)
098: * @param enabled Should the constraint be created as enabled
099: * (enabled == true), or disabled (enabled == false).
100: * @param otherConstraint information about the constraint that this references
101: * @param providerInfo Information on all the Providers
102: */
103: CreateConstraintConstantAction(String constraintName,
104: int constraintType, String tableName, UUID tableId,
105: String schemaName, String[] columnNames,
106: IndexConstantAction indexAction, String constraintText,
107: boolean enabled, ConstraintInfo otherConstraint,
108: ProviderInfo[] providerInfo) {
109: super (constraintName, constraintType, tableName, tableId,
110: schemaName, indexAction);
111: this .columnNames = columnNames;
112: this .constraintText = constraintText;
113: this .enabled = enabled;
114: this .otherConstraintInfo = otherConstraint;
115: this .providerInfo = providerInfo;
116: }
117:
118: // INTERFACE METHODS
119:
120: /**
121: * This is the guts of the Execution-time logic for CREATE CONSTRAINT.
122: *
123: * @see ConstantAction#executeConstantAction
124: *
125: * @exception StandardException Thrown on failure
126: */
127: public void executeConstantAction(Activation activation)
128: throws StandardException {
129: boolean forCreateTable;
130: ConglomerateDescriptor conglomDesc = null;
131: ConglomerateDescriptor[] conglomDescs = null;
132: ConstraintDescriptor conDesc = null;
133: TableDescriptor td = null;
134: UUID indexId = null;
135: String uniqueName;
136: String backingIndexName;
137:
138: /* RESOLVE - blow off not null constraints for now (and probably for ever) */
139: if (constraintType == DataDictionary.NOTNULL_CONSTRAINT) {
140: return;
141: }
142:
143: LanguageConnectionContext lcc = activation
144: .getLanguageConnectionContext();
145: DataDictionary dd = lcc.getDataDictionary();
146: DependencyManager dm = dd.getDependencyManager();
147: TransactionController tc = lcc.getTransactionExecute();
148:
149: cf = lcc.getLanguageConnectionFactory().getClassFactory();
150:
151: /* Remember whether or not we are doing a create table */
152: forCreateTable = activation.getForCreateTable();
153:
154: /*
155: ** Inform the data dictionary that we are about to write to it.
156: ** There are several calls to data dictionary "get" methods here
157: ** that might be done in "read" mode in the data dictionary, but
158: ** it seemed safer to do this whole operation in "write" mode.
159: **
160: ** We tell the data dictionary we're done writing at the end of
161: ** the transaction.
162: */
163: dd.startWriting(lcc);
164:
165: /* Table gets locked in AlterTableConstantAction */
166:
167: /*
168: ** If the schema descriptor is null, then
169: ** we must have just read ourselves in.
170: ** So we will get the corresponding schema
171: ** descriptor from the data dictionary.
172: */
173:
174: SchemaDescriptor sd = dd.getSchemaDescriptor(schemaName, tc,
175: true);
176:
177: /* Try to get the TableDescriptor from
178: * the Activation. We will go to the
179: * DD if not there. (It should always be
180: * there except when in a target.)
181: */
182: td = activation.getDDLTableDescriptor();
183:
184: if (td == null) {
185: /* tableId will be non-null if adding a
186: * constraint to an existing table.
187: */
188: if (tableId != null) {
189: td = dd.getTableDescriptor(tableId);
190: } else {
191: td = dd.getTableDescriptor(tableName, sd);
192: }
193:
194: if (td == null) {
195: throw StandardException.newException(
196: SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION,
197: tableName);
198: }
199: activation.setDDLTableDescriptor(td);
200: }
201:
202: /* Generate the UUID for the backing index. This will become the
203: * constraint's name, if no name was specified.
204: */
205: UUIDFactory uuidFactory = dd.getUUIDFactory();
206:
207: /* Create the index, if there's one for this constraint */
208: if (indexAction != null) {
209: if (indexAction.getIndexName() == null) {
210: /* Set the index name */
211: backingIndexName = uuidFactory.createUUID().toString();
212: indexAction.setIndexName(backingIndexName);
213: } else {
214: backingIndexName = indexAction.getIndexName();
215: }
216:
217: /* Create the index */
218: indexAction.executeConstantAction(activation);
219:
220: /* Get the conglomerate descriptor for the backing index */
221: conglomDescs = td.getConglomerateDescriptors();
222:
223: for (int index = 0; index < conglomDescs.length; index++) {
224: conglomDesc = conglomDescs[index];
225:
226: /* Check for conglomerate being an index first, since
227: * name is null for heap.
228: */
229: if (conglomDesc.isIndex()
230: && backingIndexName.equals(conglomDesc
231: .getConglomerateName())) {
232: break;
233: }
234: }
235:
236: if (SanityManager.DEBUG) {
237: SanityManager
238: .ASSERT(conglomDesc != null,
239: "conglomDesc is expected to be non-null after search for backing index");
240: SanityManager
241: .ASSERT(conglomDesc.isIndex(),
242: "conglomDesc is expected to be indexable after search for backing index");
243: SanityManager
244: .ASSERT(
245: conglomDesc.getConglomerateName()
246: .equals(backingIndexName),
247: "conglomDesc name expected to be the same as backing index name after search for backing index");
248: }
249:
250: indexId = conglomDesc.getUUID();
251: }
252:
253: // if no constraintId was specified, we should generate one. this handles
254: // the two cases of Source creation and Target replication. At the source
255: // database, we allocate a new UUID. At the Target, we just use the UUID that
256: // the Source sent along.
257: UUID constraintId = uuidFactory.createUUID();
258:
259: /* Now, lets create the constraint descriptor */
260: DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
261: switch (constraintType) {
262: case DataDictionary.PRIMARYKEY_CONSTRAINT:
263: conDesc = ddg.newPrimaryKeyConstraintDescriptor(td,
264: constraintName, false, //deferable,
265: false, //initiallyDeferred,
266: genColumnPositions(td, false), //int[],
267: constraintId, indexId, sd, enabled, 0 // referenceCount
268: );
269: dd.addConstraintDescriptor(conDesc, tc);
270: break;
271:
272: case DataDictionary.UNIQUE_CONSTRAINT:
273: conDesc = ddg.newUniqueConstraintDescriptor(td,
274: constraintName, false, //deferable,
275: false, //initiallyDeferred,
276: genColumnPositions(td, false), //int[],
277: constraintId, indexId, sd, enabled, 0 // referenceCount
278: );
279: dd.addConstraintDescriptor(conDesc, tc);
280: break;
281:
282: case DataDictionary.CHECK_CONSTRAINT:
283: conDesc = ddg.newCheckConstraintDescriptor(td,
284: constraintName,
285: false, //deferable,
286: false, //initiallyDeferred,
287: constraintId, constraintText,
288: new ReferencedColumnsDescriptorImpl(
289: genColumnPositions(td, false)), //int[],
290: sd, enabled);
291: dd.addConstraintDescriptor(conDesc, tc);
292: break;
293:
294: case DataDictionary.FOREIGNKEY_CONSTRAINT:
295: ReferencedKeyConstraintDescriptor referencedConstraint = DDUtils
296: .locateReferencedConstraint(dd, td, constraintName,
297: columnNames, otherConstraintInfo);
298: DDUtils.validateReferentialActions(dd, td, constraintName,
299: otherConstraintInfo, columnNames);
300:
301: conDesc = ddg.newForeignKeyConstraintDescriptor(
302: td,
303: constraintName,
304: false, //deferable,
305: false, //initiallyDeferred,
306: genColumnPositions(td, false), //int[],
307: constraintId, indexId, sd, referencedConstraint,
308: enabled, otherConstraintInfo
309: .getReferentialActionDeleteRule(),
310: otherConstraintInfo
311: .getReferentialActionUpdateRule());
312:
313: // try to create the constraint first, because it
314: // is expensive to do the bulk check, find obvious
315: // errors first
316: dd.addConstraintDescriptor(conDesc, tc);
317:
318: /* No need to do check if we're creating a
319: * table.
320: */
321: if ((!forCreateTable) && dd.activeConstraint(conDesc)) {
322: validateFKConstraint(tc, dd,
323: (ForeignKeyConstraintDescriptor) conDesc,
324: referencedConstraint,
325: ((CreateIndexConstantAction) indexAction)
326: .getIndexTemplateRow());
327: }
328:
329: /* Create stored dependency on the referenced constraint */
330: dm.addDependency(conDesc, referencedConstraint, lcc
331: .getContextManager());
332: //store constraint's dependency on REFERENCES privileges in the dependeny system
333: storeConstraintDependenciesOnPrivileges(activation,
334: conDesc, referencedConstraint.getTableId());
335: break;
336:
337: default:
338: if (SanityManager.DEBUG) {
339: SanityManager.THROWASSERT("contraintType ("
340: + constraintType + ") has unexpected value");
341: }
342: break;
343: }
344:
345: /* Create stored dependencies for each provider */
346: if (providerInfo != null) {
347: for (int ix = 0; ix < providerInfo.length; ix++) {
348: Provider provider = null;
349:
350: /* We should always be able to find the Provider */
351: try {
352: provider = (Provider) providerInfo[ix]
353: .getDependableFinder().getDependable(
354: providerInfo[ix].getObjectId());
355: } catch (java.sql.SQLException te) {
356: if (SanityManager.DEBUG) {
357: SanityManager
358: .THROWASSERT("unexpected java.sql.SQLException - "
359: + te);
360: }
361: }
362: dm.addDependency(conDesc, provider, lcc
363: .getContextManager());
364: }
365: }
366:
367: /* Finally, invalidate off of the table descriptor(s)
368: * to ensure that any dependent statements get
369: * re-compiled.
370: */
371: if (!forCreateTable) {
372: dm.invalidateFor(td, DependencyManager.CREATE_CONSTRAINT,
373: lcc);
374: }
375: if (constraintType == DataDictionary.FOREIGNKEY_CONSTRAINT) {
376: if (SanityManager.DEBUG) {
377: SanityManager.ASSERT(conDesc != null,
378: "conDesc expected to be non-null");
379:
380: if (!(conDesc instanceof ForeignKeyConstraintDescriptor)) {
381: SanityManager
382: .THROWASSERT("conDesc expected to be instance of ForeignKeyConstraintDescriptor, not "
383: + conDesc.getClass().getName());
384: }
385: }
386: dm.invalidateFor(((ForeignKeyConstraintDescriptor) conDesc)
387: .getReferencedConstraint().getTableDescriptor(),
388: DependencyManager.CREATE_CONSTRAINT, lcc);
389: }
390: }
391:
392: /**
393: * Is the constant action for a foreign key
394: *
395: * @return true/false
396: */
397: public boolean isForeignKeyConstraint() {
398: return (constraintType == DataDictionary.FOREIGNKEY_CONSTRAINT);
399: }
400:
401: /**
402: * Generate an array of column positions for the column list in
403: * the constraint.
404: *
405: * @param td The TableDescriptor for the table in question
406: * @param columnsMustBeOrderable true for primaryKey and unique constraints
407: *
408: * @return int[] The column positions.
409: */
410: private int[] genColumnPositions(TableDescriptor td,
411: boolean columnsMustBeOrderable) throws StandardException {
412: int[] baseColumnPositions;
413:
414: // Translate the base column names to column positions
415: baseColumnPositions = new int[columnNames.length];
416: for (int i = 0; i < columnNames.length; i++) {
417: ColumnDescriptor columnDescriptor;
418:
419: // Look up the column in the data dictionary
420: columnDescriptor = td.getColumnDescriptor(columnNames[i]);
421: if (columnDescriptor == null) {
422: throw StandardException.newException(
423: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,
424: columnNames[i], tableName);
425: }
426:
427: // Don't allow a column to be created on a non-orderable type
428: // (for primaryKey and unique constraints)
429: if (columnsMustBeOrderable
430: && (!columnDescriptor.getType().getTypeId()
431: .orderable(cf))) {
432: throw StandardException
433: .newException(
434: SQLState.LANG_COLUMN_NOT_ORDERABLE_DURING_EXECUTION,
435: columnDescriptor.getType().getTypeId()
436: .getSQLTypeName());
437: }
438:
439: // Remember the position in the base table of each column
440: baseColumnPositions[i] = columnDescriptor.getPosition();
441: }
442:
443: return baseColumnPositions;
444: }
445:
446: ///////////////////////////////////////////////////////////////////////
447: //
448: // ACCESSORS
449: //
450: ///////////////////////////////////////////////////////////////////////
451:
452: /**
453: * Get the names of the columns touched by this constraint.
454: *
455: * @return the array of touched column names.
456: */
457: public String[] getColumnNames() {
458: return columnNames;
459: }
460:
461: /**
462: * Get the text defining this constraint.
463: *
464: * @return constraint text
465: */
466: public String getConstraintText() {
467: return constraintText;
468: }
469:
470: public String toString() {
471: // Do not put this under SanityManager.DEBUG - it is needed for
472: // error reporting.
473: StringBuffer strbuf = new StringBuffer();
474: strbuf.append("CREATE CONSTRAINT " + constraintName);
475: strbuf.append("\n=========================\n");
476:
477: if (columnNames == null) {
478: strbuf.append("columnNames == null\n");
479: } else {
480: for (int ix = 0; ix < columnNames.length; ix++) {
481: strbuf.append("\n\tcol[" + ix + "]"
482: + columnNames[ix].toString());
483: }
484: }
485:
486: strbuf.append("\n");
487: strbuf.append(constraintText);
488: strbuf.append("\n");
489: if (otherConstraintInfo != null) {
490: strbuf.append(otherConstraintInfo.toString());
491: }
492: strbuf.append("\n");
493: return strbuf.toString();
494: }
495: }
|