001: /*
002:
003: Derby - Class org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor
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.iapi.sql.dictionary;
023:
024: import org.apache.derby.iapi.error.StandardException;
025:
026: import org.apache.derby.iapi.sql.depend.Provider;
027: import org.apache.derby.iapi.sql.depend.Dependent;
028:
029: import org.apache.derby.catalog.UUID;
030:
031: import org.apache.derby.iapi.reference.SQLState;
032: import org.apache.derby.iapi.services.sanity.SanityManager;
033: import org.apache.derby.catalog.DependableFinder;
034: import org.apache.derby.catalog.Dependable;
035: import org.apache.derby.iapi.services.io.StoredFormatIds;
036: import org.apache.derby.iapi.sql.depend.DependencyManager;
037: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
038:
039: import org.apache.derby.impl.sql.execute.DropConstraintConstantAction;
040:
041: /**
042: * This class is used to get information from a ConstraintDescriptor.
043: * A ConstraintDescriptor can represent a constraint on a table or on a
044: * column.
045: *
046: * @version 0.1
047: * @author Jeff Lichtman
048: */
049:
050: public abstract class ConstraintDescriptor extends TupleDescriptor
051: implements UniqueTupleDescriptor, Provider, Dependent {
052: // used to indicate what type of constraints we
053: // are interested in
054: public static final int ENABLED = 1;
055: public static final int DISABLED = 2;
056: public static final int ALL = 3;
057:
058: // field that we want users to be able to know about
059: public static final int SYSCONSTRAINTS_STATE_FIELD = 6;
060:
061: TableDescriptor table;
062: final String constraintName;
063: private final boolean deferrable;
064: private final boolean initiallyDeferred;
065: boolean isEnabled;
066: private final int[] referencedColumns;
067: final UUID constraintId;
068: private final SchemaDescriptor schemaDesc;
069: private ColumnDescriptorList colDL;
070:
071: /**
072: * Constructor for a ConstraintDescriptor
073: *
074: * @param dataDictionary The data dictionary that this descriptor lives in
075: * @param table The descriptor of the table the constraint is on
076: * @param constraintName The name of the constraint.
077: * @param deferrable If the constraint can be deferred.
078: * @param initiallyDeferred If the constraint starts life deferred.
079: * @param referencedColumns columns that the constraint references
080: * @param constraintId UUID of constraint
081: * @param schemaDesc SchemaDescriptor
082: */
083:
084: ConstraintDescriptor(DataDictionary dataDictionary,
085: TableDescriptor table, String constraintName,
086: boolean deferrable, boolean initiallyDeferred,
087: int[] referencedColumns, UUID constraintId,
088: SchemaDescriptor schemaDesc, boolean isEnabled) {
089: super (dataDictionary);
090:
091: this .table = table;
092: this .constraintName = constraintName;
093: this .deferrable = deferrable;
094: this .initiallyDeferred = initiallyDeferred;
095: this .referencedColumns = referencedColumns;
096: this .constraintId = constraintId;
097: this .schemaDesc = schemaDesc;
098: this .isEnabled = isEnabled;
099: }
100:
101: /**
102: * Gets the UUID of the table the constraint is on.
103: *
104: * @return The UUID of the table the constraint is on.
105: */
106: public UUID getTableId() {
107: return table.getUUID();
108: }
109:
110: /**
111: * Gets the UUID of the constraint.
112: *
113: * @return The UUID of the constraint.
114: */
115: public UUID getUUID() {
116: return constraintId;
117: }
118:
119: /**
120: * Gets the name of the constraint.
121: *
122: * @return A String containing the name of the constraint.
123: */
124: public String getConstraintName() {
125: return constraintName;
126: }
127:
128: /**
129: * Gets an identifier telling what type of descriptor it is
130: * (UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK).
131: *
132: * @return An identifier telling what type of descriptor it is
133: * (UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK).
134: */
135: public abstract int getConstraintType();
136:
137: public abstract UUID getConglomerateId();
138:
139: /**
140: * Get the text of the constraint. (Only non-null/meaningful for check
141: * constraints.)
142: * @return The constraint text.
143: */
144: public String getConstraintText() {
145: return null;
146: }
147:
148: /**
149: * Returns TRUE if the constraint is deferrable
150: * (we will probably not do deferrable constraints in the
151: * initial release, but I want this to be part of the interface).
152: *
153: * @return TRUE if the constraint is deferrable, FALSE if not
154: */
155: public boolean deferrable() {
156: return deferrable;
157: }
158:
159: /**
160: * Returns TRUE if the constraint is initially deferred
161: * (we will probably not do initially deferred constraints
162: * in the initial release, but I want this to be part of the interface).
163: *
164: * @return TRUE if the constraint is initially deferred,
165: * FALSE if not
166: */
167: public boolean initiallyDeferred() {
168: return initiallyDeferred;
169: }
170:
171: /**
172: * Returns an array of column ids (i.e. ordinal positions) for
173: * the columns referenced in this table for a primary key, unique
174: * key, referential, or check constraint.
175: *
176: * @return An array of column ids for those constraints that can
177: * be on columns (primary, unique key, referential
178: * constraints, and check constraints). For check and
179: * unique constraints, it returns an array of columns ids
180: * that are referenced in the constraint. For primary key
181: * and referential constraints, it returns an array of
182: * column ids for the columns in this table (i.e. the
183: * primary key columns for a primary key constraint,
184: * and the foreign key columns for a foreign key
185: * constraint.
186: */
187: public int[] getReferencedColumns() {
188: return referencedColumns;
189: }
190:
191: /**
192: * Does this constraint have a backing index?
193: *
194: * @return boolean Whether or not there is a backing index for this constraint.
195: */
196: public abstract boolean hasBackingIndex();
197:
198: /**
199: * Get the SchemaDescriptor for the schema that this constraint
200: * belongs to.
201: *
202: * @return SchemaDescriptor The SchemaDescriptor for this constraint.
203: */
204: public SchemaDescriptor getSchemaDescriptor() {
205: return schemaDesc;
206: }
207:
208: /**
209: RESOLVE: For now the ConstraintDescriptor code stores the array of key
210: columns in the field 'otherColumns'. Jerry plans to re-organize things.
211: For now to minimize his rototill I've implemented this function on the
212: old structures. All new code should use getKeyColumns to get a constraint's
213: key columns.
214:
215: @see org.apache.derby.iapi.sql.dictionary.KeyConstraintDescriptor#getKeyColumns
216: */
217: public int[] getKeyColumns() {
218: return getReferencedColumns();
219: }
220:
221: /**
222: * Is this constraint active?
223: *
224: * @return true/false
225: */
226: public boolean isEnabled() {
227: return isEnabled;
228: }
229:
230: /**
231: * Set the constraint to enabled.
232: * Does not update the data dictionary
233: */
234: public void setEnabled() {
235: isEnabled = true;
236: }
237:
238: /**
239: * Set the constraint to disabled.
240: * Does not update the data dictionary
241: */
242: public void setDisabled() {
243: isEnabled = false;
244: }
245:
246: /**
247: * Is this constraint referenced? Return
248: * false. Overridden by ReferencedKeyConstraints.
249: *
250: * @return false
251: */
252: public boolean isReferenced() {
253: return false;
254: }
255:
256: /**
257: * Get the number of enabled fks that
258: * reference this key. Overriden by
259: * ReferencedKeyConstraints.
260: *
261: * @return the number of fks
262: */
263: public int getReferenceCount() {
264: return 0;
265: }
266:
267: /**
268: * Does this constraint need to fire on this type of
269: * DML?
270: *
271: * @param stmtType the type of DML
272: * (StatementType.INSERT|StatementType.UPDATE|StatementType.DELETE)
273: * @param modifiedCols the columns modified, or null for all
274: *
275: * @return true/false
276: */
277: public abstract boolean needsToFire(int stmtType, int[] modifiedCols);
278:
279: /**
280: * Get the table descriptor upon which this constraint
281: * is declared.
282: *
283: * @return the table descriptor
284: */
285: public TableDescriptor getTableDescriptor() {
286: return table;
287: }
288:
289: /**
290: * Get the column descriptors for all the columns
291: * referenced by this constraint.
292: *
293: * @return the column descriptor list
294: *
295: * @exception StandardException on error
296: */
297: public ColumnDescriptorList getColumnDescriptors()
298: throws StandardException {
299: if (colDL == null) {
300: DataDictionary dd = getDataDictionary();
301: colDL = new ColumnDescriptorList();
302:
303: int[] refCols = getReferencedColumns();
304: for (int i = 0; i < refCols.length; i++) {
305: colDL.add(table.getColumnDescriptor(refCols[i]));
306: }
307: }
308: return colDL;
309: }
310:
311: /**
312: * Indicates whether the column descriptor list is
313: * type comparable with the constraints columns. The
314: * types have to be identical AND in the same order
315: * to succeed.
316: *
317: * @param otherColumns the columns to compare
318: *
319: * @return true/false
320: *
321: * @exception StandardException on error
322: */
323: public boolean areColumnsComparable(
324: ColumnDescriptorList otherColumns) throws StandardException {
325: ColumnDescriptor myColumn;
326: ColumnDescriptor otherColumn;
327:
328: ColumnDescriptorList myColDl = getColumnDescriptors();
329:
330: /*
331: ** Check the lenghts of the lists
332: */
333: if (otherColumns.size() != myColDl.size()) {
334: return false;
335: }
336:
337: int mySize = myColDl.size();
338: int otherSize = otherColumns.size();
339: int index;
340: for (index = 0; index < mySize && index < otherSize; index++) {
341: myColumn = (ColumnDescriptor) myColDl.elementAt(index);
342: otherColumn = (ColumnDescriptor) otherColumns
343: .elementAt(index);
344:
345: /*
346: ** Just compare the types. Note that this will
347: ** say a decimal(x,y) != numeric(x,y) even though
348: ** it does.
349: */
350: if (!(myColumn.getType())
351: .isExactTypeAndLengthMatch((otherColumn.getType()))) {
352: break;
353: }
354: }
355:
356: return (index == mySize && index == otherSize);
357: }
358:
359: /**
360: * Does a column intersect with our referenced columns
361: * @param columnArray columns to check
362: *
363: * Note-- this is not a static method.
364: */
365: public boolean columnIntersects(int columnArray[]) {
366: // call static method.
367: return doColumnsIntersect(getReferencedColumns(), columnArray);
368: }
369:
370: /**
371: * Does a column in the input set intersect with
372: * our referenced columns?
373: *
374: * @param otherColumns the columns to compare. If
375: * null, asssumed to mean all columns
376: *
377: * @param referencedColumns the columns referenced by the caller
378: *
379: * @return true/false
380: */
381: static boolean doColumnsIntersect(int[] otherColumns,
382: int[] referencedColumns) {
383: /*
384: ** It is assumed that if otherColumns is null, then
385: ** all other columns are modified. In this case,
386: ** it is assumed that it intersects with some column
387: ** of ours, so just return true.
388: */
389: if ((otherColumns == null) || (referencedColumns == null)) {
390: return true;
391: }
392:
393: for (int outer = 0; outer < referencedColumns.length; outer++) {
394: for (int inner = 0; inner < otherColumns.length; inner++) {
395: if (referencedColumns[outer] == otherColumns[inner]) {
396: return true;
397: }
398: }
399: }
400: return false;
401: }
402:
403: /**
404: * Convert the ColumnDescriptor to a String.
405: *
406: * @return A String representation of this ColumnDescriptor
407: */
408:
409: public String toString() {
410: if (SanityManager.DEBUG) {
411: String tableDesc = "table: " + table.getQualifiedName()
412: + "(" + table.getUUID() + ","
413: + table.getTableType() + ")";
414:
415: return tableDesc + "\n" + "constraintName: "
416: + constraintName + "\n" + "constraintId: "
417: + constraintId + "\n" + "deferrable: " + deferrable
418: + "\n" + "initiallyDeferred: " + initiallyDeferred
419: + "\n" + "referencedColumns: " + referencedColumns
420: + "\n" + "schemaDesc: " + schemaDesc + "\n";
421: } else {
422: return "";
423: }
424: }
425:
426: ////////////////////////////////////////////////////////////////////
427: //
428: // PROVIDER INTERFACE
429: //
430: ////////////////////////////////////////////////////////////////////
431:
432: /**
433: @return the stored form of this provider
434:
435: @see Dependable#getDependableFinder
436: */
437: public DependableFinder getDependableFinder() {
438: return getDependableFinder(StoredFormatIds.CONSTRAINT_DESCRIPTOR_FINDER_V01_ID);
439: }
440:
441: /**
442: * Return the name of this Provider. (Useful for errors.)
443: *
444: * @return String The name of this provider.
445: */
446: public String getObjectName() {
447: return constraintName;
448: }
449:
450: /**
451: * Get the provider's UUID
452: *
453: * @return The provider's UUID
454: */
455: public UUID getObjectID() {
456: return constraintId;
457: }
458:
459: /**
460: * Get the provider's type.
461: *
462: * @return char The provider's type.
463: */
464: public String getClassType() {
465: return Dependable.CONSTRAINT;
466: }
467:
468: //////////////////////////////////////////////////////
469: //
470: // DEPENDENT INTERFACE
471: //
472: //////////////////////////////////////////////////////
473: /**
474: * Check that all of the dependent's dependencies are valid.
475: *
476: * @return true if the dependent is currently valid
477: */
478: public synchronized boolean isValid() {
479: return true;
480: }
481:
482: /**
483: * Prepare to mark the dependent as invalid (due to at least one of
484: * its dependencies being invalid).
485: *
486: * @param action The action causing the invalidation
487: * @param p the provider
488: *
489: * @exception StandardException thrown if unable to make it invalid
490: */
491: public void prepareToInvalidate(Provider p, int action,
492: LanguageConnectionContext lcc) throws StandardException {
493: DependencyManager dm = getDataDictionary()
494: .getDependencyManager();
495:
496: switch (action) {
497: /*
498: ** A SET CONSTRAINT stmt will throw an SET_CONSTRAINTS action
499: ** when enabling/disabling constraints. We'll ignore it.
500: ** Same for SET TRIGGERS
501: */
502: case DependencyManager.SET_CONSTRAINTS_ENABLE:
503: case DependencyManager.SET_CONSTRAINTS_DISABLE:
504: case DependencyManager.SET_TRIGGERS_ENABLE:
505: case DependencyManager.SET_TRIGGERS_DISABLE:
506: //When REVOKE_PRIVILEGE gets sent (this happens for privilege
507: //types SELECT, UPDATE, DELETE, INSERT, REFERENCES, TRIGGER), we
508: //don't do anything here. Later in makeInvalid method, we make
509: //the ConstraintDescriptor drop itself.
510: case DependencyManager.REVOKE_PRIVILEGE:
511: break;
512:
513: /*
514: ** Currently, the only thing we are depenedent
515: ** on is another constraint or an alias..
516: */
517: //Notice that REVOKE_PRIVILEGE_RESTRICT is not caught earlier.
518: //It gets handled in this default: action where an exception
519: //will be thrown. This is because, if such an invalidation
520: //action type is ever received by a dependent, the dependent
521: //should throw an exception.
522: //In Derby, at this point, REVOKE_PRIVILEGE_RESTRICT gets sent
523: //when execute privilege on a routine is getting revoked.
524: //Currently, in Derby, a constraint can't depend on a routine
525: //and hence a REVOKE_PRIVILEGE_RESTRICT invalidation action
526: //should never be received by a ConstraintDescriptor. But this
527: //may change in future and when it does, the code to do the right
528: //thing is already here.
529: default:
530: throw StandardException.newException(
531: SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT, dm
532: .getActionString(action),
533: p.getObjectName(), "CONSTRAINT", constraintName);
534: }
535: }
536:
537: /**
538: * Mark the dependent as invalid (due to at least one of
539: * its dependencies being invalid). Always an error
540: * for a constraint -- should never have gotten here.
541: *
542: * @param action The action causing the invalidation
543: *
544: * @exception StandardException thrown if called in sanity mode
545: */
546: public void makeInvalid(int action, LanguageConnectionContext lcc)
547: throws StandardException {
548: /*
549: ** For ConstraintDescriptor, SET_CONSTRAINTS/TRIGGERS and
550: * REVOKE_PRIVILEGE are the only valid actions
551: */
552:
553: //Let's handle REVOKE_PRIVILEGE first
554: if (action == DependencyManager.REVOKE_PRIVILEGE) {
555: //At this point (Derby 10.2), only a FOREIGN KEY key constraint can
556: //depend on a privilege. None of the other constraint types
557: //can be dependent on a privilege becuse those constraint types
558: //can not reference a table/routine.
559: DropConstraintConstantAction.dropConstraintAndIndex(
560: getDataDictionary().getDependencyManager(), table,
561: getDataDictionary(), this , lcc
562: .getTransactionExecute(), lcc, true);
563: return;
564: }
565:
566: /*
567: ** Now, handle SET_CONSTRAINTS/TRIGGERS
568: */
569: if ((action != DependencyManager.SET_CONSTRAINTS_DISABLE)
570: && (action != DependencyManager.SET_CONSTRAINTS_ENABLE)
571: && (action != DependencyManager.SET_TRIGGERS_ENABLE)
572: && (action != DependencyManager.SET_TRIGGERS_DISABLE)) {
573: /*
574: ** We should never get here, we should have barfed on
575: ** prepareToInvalidate().
576: */
577: if (SanityManager.DEBUG) {
578: DependencyManager dm;
579:
580: dm = getDataDictionary().getDependencyManager();
581:
582: SanityManager.THROWASSERT("makeInvalid("
583: + dm.getActionString(action)
584: + ") not expected to get called");
585: }
586: }
587: }
588:
589: /**
590: * Attempt to revalidate the dependent. Meaningless
591: * for constraints.
592: */
593: public void makeValid(LanguageConnectionContext lcc) {
594: }
595:
596: /** @see TupleDescriptor#getDescriptorName */
597: public String getDescriptorName() {
598: return constraintName;
599: }
600:
601: public String getDescriptorType() {
602: return "Constraint";
603: }
604: }
|