1 | /* |
2 | |
3 | Derby - Class org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.iapi.sql.dictionary; |
22 | |
23 | import org.apache.derby.iapi.error.StandardException; |
24 | |
25 | import org.apache.derby.iapi.sql.depend.Provider; |
26 | import org.apache.derby.iapi.sql.depend.Dependent; |
27 | |
28 | import org.apache.derby.catalog.UUID; |
29 | |
30 | import org.apache.derby.iapi.reference.SQLState; |
31 | import org.apache.derby.iapi.services.sanity.SanityManager; |
32 | import org.apache.derby.iapi.sql.StatementType; |
33 | import org.apache.derby.catalog.DependableFinder; |
34 | import org.apache.derby.catalog.Dependable; |
35 | import org.apache.derby.iapi.services.io.StoredFormatIds; |
36 | import org.apache.derby.iapi.error.StandardException; |
37 | import org.apache.derby.iapi.sql.depend.DependencyManager; |
38 | import org.apache.derby.iapi.sql.depend.Dependent; |
39 | import org.apache.derby.iapi.sql.depend.Dependency; |
40 | import org.apache.derby.iapi.sql.depend.Provider; |
41 | import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; |
42 | |
43 | /** |
44 | * This class is used to get information from a ConstraintDescriptor. |
45 | * A ConstraintDescriptor can represent a constraint on a table or on a |
46 | * column. |
47 | * |
48 | * @version 0.1 |
49 | * @author Jeff Lichtman |
50 | */ |
51 | |
52 | public abstract class ConstraintDescriptor |
53 | extends TupleDescriptor |
54 | implements UniqueTupleDescriptor, Provider, Dependent |
55 | { |
56 | // used to indicate what type of constraints we |
57 | // are interested in |
58 | public static final int ENABLED = 1; |
59 | public static final int DISABLED = 2; |
60 | public static final int ALL = 3; |
61 | |
62 | // field that we want users to be able to know about |
63 | public static final int SYSCONSTRAINTS_STATE_FIELD = 6; |
64 | |
65 | TableDescriptor table; |
66 | String constraintName; |
67 | boolean deferrable; |
68 | boolean initiallyDeferred; |
69 | boolean isEnabled; |
70 | int[] referencedColumns; |
71 | UUID constraintId; |
72 | SchemaDescriptor schemaDesc; |
73 | ColumnDescriptorList colDL; |
74 | |
75 | /** |
76 | * Constructor for a ConstraintDescriptor |
77 | * |
78 | * @param dataDictionary The data dictionary that this descriptor lives in |
79 | * @param table The descriptor of the table the constraint is on |
80 | * @param constraintName The name of the constraint. |
81 | * @param deferrable If the constraint can be deferred. |
82 | * @param initiallyDeferred If the constraint starts life deferred. |
83 | * @param referencedColumns columns that the constraint references |
84 | * @param constraintId UUID of constraint |
85 | * @param schemaDesc SchemaDescriptor |
86 | */ |
87 | |
88 | ConstraintDescriptor( |
89 | DataDictionary dataDictionary, |
90 | TableDescriptor table, |
91 | String constraintName, |
92 | boolean deferrable, |
93 | boolean initiallyDeferred, |
94 | int[] referencedColumns, |
95 | UUID constraintId, |
96 | SchemaDescriptor schemaDesc, |
97 | boolean isEnabled |
98 | ) |
99 | { |
100 | super( dataDictionary ); |
101 | |
102 | this.table = table; |
103 | this.constraintName = constraintName; |
104 | this.deferrable = deferrable; |
105 | this.initiallyDeferred = initiallyDeferred; |
106 | this.referencedColumns = referencedColumns; |
107 | this.constraintId = constraintId; |
108 | this.schemaDesc = schemaDesc; |
109 | this.isEnabled = isEnabled; |
110 | } |
111 | |
112 | |
113 | /** |
114 | * Gets the UUID of the table the constraint is on. |
115 | * |
116 | * @return The UUID of the table the constraint is on. |
117 | */ |
118 | public UUID getTableId() |
119 | { |
120 | return table.getUUID(); |
121 | } |
122 | |
123 | /** |
124 | * Sets the UUID of the constraint. |
125 | * |
126 | * @param constraintId The constraint Id. |
127 | */ |
128 | public void setConstraintId(UUID constraintId) |
129 | { |
130 | this.constraintId = constraintId; |
131 | } |
132 | |
133 | /** |
134 | * Gets the UUID of the constraint. |
135 | * |
136 | * @return The UUID of the constraint. |
137 | */ |
138 | public UUID getUUID() |
139 | { |
140 | return constraintId; |
141 | } |
142 | |
143 | /** |
144 | * Gets the name of the constraint. |
145 | * |
146 | * @return A String containing the name of the constraint. |
147 | */ |
148 | public String getConstraintName() |
149 | { |
150 | return constraintName; |
151 | } |
152 | |
153 | /** |
154 | * Gets an identifier telling what type of descriptor it is |
155 | * (UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK). |
156 | * |
157 | * @return An identifier telling what type of descriptor it is |
158 | * (UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK). |
159 | */ |
160 | public abstract int getConstraintType(); |
161 | |
162 | public abstract UUID getConglomerateId(); |
163 | |
164 | /** |
165 | * Get the text of the constraint. (Only non-null/meaningful for check |
166 | * constraints.) |
167 | * @return The constraint text. |
168 | */ |
169 | public String getConstraintText() |
170 | { |
171 | return null; |
172 | } |
173 | |
174 | /** |
175 | * Returns TRUE if the constraint is deferrable |
176 | * (we will probably not do deferrable constraints in the |
177 | * initial release, but I want this to be part of the interface). |
178 | * |
179 | * @return TRUE if the constraint is deferrable, FALSE if not |
180 | */ |
181 | public boolean deferrable() |
182 | { |
183 | return deferrable; |
184 | } |
185 | |
186 | /** |
187 | * Returns TRUE if the constraint is initially deferred |
188 | * (we will probably not do initially deferred constraints |
189 | * in the initial release, but I want this to be part of the interface). |
190 | * |
191 | * @return TRUE if the constraint is initially deferred, |
192 | * FALSE if not |
193 | */ |
194 | public boolean initiallyDeferred() |
195 | { |
196 | return initiallyDeferred; |
197 | } |
198 | |
199 | /** |
200 | * Returns an array of column ids (i.e. ordinal positions) for |
201 | * the columns referenced in this table for a primary key, unique |
202 | * key, referential, or check constraint. |
203 | * |
204 | * @return An array of column ids for those constraints that can |
205 | * be on columns (primary, unique key, referential |
206 | * constraints, and check constraints). For check and |
207 | * unique constraints, it returns an array of columns ids |
208 | * that are referenced in the constraint. For primary key |
209 | * and referential constraints, it returns an array of |
210 | * column ids for the columns in this table (i.e. the |
211 | * primary key columns for a primary key constraint, |
212 | * and the foreign key columns for a foreign key |
213 | * constraint. |
214 | */ |
215 | public int[] getReferencedColumns() |
216 | { |
217 | return referencedColumns; |
218 | } |
219 | |
220 | /** |
221 | * Does this constraint have a backing index? |
222 | * |
223 | * @return boolean Whether or not there is a backing index for this constraint. |
224 | */ |
225 | public abstract boolean hasBackingIndex(); |
226 | |
227 | /** |
228 | * Get the SchemaDescriptor for the schema that this constraint |
229 | * belongs to. |
230 | * |
231 | * @return SchemaDescriptor The SchemaDescriptor for this constraint. |
232 | */ |
233 | public SchemaDescriptor getSchemaDescriptor() |
234 | { |
235 | return schemaDesc; |
236 | } |
237 | |
238 | /** |
239 | RESOLVE: For now the ConstraintDescriptor code stores the array of key |
240 | columns in the field 'otherColumns'. Jerry plans to re-organize things. |
241 | For now to minimize his rototill I've implemented this function on the |
242 | old structures. All new code should use getKeyColumns to get a constraint's |
243 | key columns. |
244 | |
245 | @see org.apache.derby.iapi.sql.dictionary.KeyConstraintDescriptor#getKeyColumns |
246 | */ |
247 | public int[] getKeyColumns() |
248 | { |
249 | return getReferencedColumns(); |
250 | } |
251 | |
252 | /** |
253 | * Is this constraint active? |
254 | * |
255 | * @return true/false |
256 | */ |
257 | public boolean isEnabled() |
258 | { |
259 | return isEnabled; |
260 | } |
261 | |
262 | /** |
263 | * Set the constraint to enabled. |
264 | * Does not update the data dictionary |
265 | */ |
266 | public void setEnabled() |
267 | { |
268 | isEnabled = true; |
269 | } |
270 | |
271 | /** |
272 | * Set the constraint to disabled. |
273 | * Does not update the data dictionary |
274 | */ |
275 | public void setDisabled() |
276 | { |
277 | isEnabled = false; |
278 | } |
279 | |
280 | /** |
281 | * Is this constraint referenced? Return |
282 | * false. Overridden by ReferencedKeyConstraints. |
283 | * |
284 | * @return false |
285 | */ |
286 | public boolean isReferenced() |
287 | { |
288 | return false; |
289 | } |
290 | |
291 | /** |
292 | * Get the number of enabled fks that |
293 | * reference this key. Overriden by |
294 | * ReferencedKeyConstraints. |
295 | * |
296 | * @return the number of fks |
297 | */ |
298 | public int getReferenceCount() |
299 | { |
300 | return 0; |
301 | } |
302 | |
303 | /** |
304 | * Does this constraint need to fire on this type of |
305 | * DML? |
306 | * |
307 | * @param stmtType the type of DML |
308 | * (StatementType.INSERT|StatementType.UPDATE|StatementType.DELETE) |
309 | * @param modifiedCols the columns modified, or null for all |
310 | * |
311 | * @return true/false |
312 | */ |
313 | public abstract boolean needsToFire(int stmtType, int[] modifiedCols); |
314 | |
315 | /** |
316 | * Get the table descriptor upon which this constraint |
317 | * is declared. |
318 | * |
319 | * @return the table descriptor |
320 | */ |
321 | public TableDescriptor getTableDescriptor() |
322 | { |
323 | return table; |
324 | } |
325 | |
326 | /** |
327 | * Get the column descriptors for all the columns |
328 | * referenced by this constraint. |
329 | * |
330 | * @return the column descriptor list |
331 | * |
332 | * @exception StandardException on error |
333 | */ |
334 | public ColumnDescriptorList getColumnDescriptors() |
335 | throws StandardException |
336 | { |
337 | if (colDL == null) |
338 | { |
339 | DataDictionary dd = getDataDictionary(); |
340 | colDL = new ColumnDescriptorList(); |
341 | |
342 | int[] refCols = getReferencedColumns(); |
343 | for (int i = 0; i < refCols.length; i++) |
344 | { |
345 | colDL.add(table.getColumnDescriptor(refCols[i])); |
346 | } |
347 | } |
348 | return colDL; |
349 | } |
350 | |
351 | /** |
352 | * Indicates whether the column descriptor list is |
353 | * type comparable with the constraints columns. The |
354 | * types have to be identical AND in the same order |
355 | * to succeed. |
356 | * |
357 | * @param otherColumns the columns to compare |
358 | * |
359 | * @return true/false |
360 | * |
361 | * @exception StandardException on error |
362 | */ |
363 | public boolean areColumnsComparable(ColumnDescriptorList otherColumns) |
364 | throws StandardException |
365 | { |
366 | ColumnDescriptor myColumn; |
367 | ColumnDescriptor otherColumn; |
368 | |
369 | ColumnDescriptorList myColDl = getColumnDescriptors(); |
370 | |
371 | /* |
372 | ** Check the lenghts of the lists |
373 | */ |
374 | if (otherColumns.size() != myColDl.size()) |
375 | { |
376 | return false; |
377 | } |
378 | |
379 | int mySize = myColDl.size(); |
380 | int otherSize = otherColumns.size(); |
381 | int index; |
382 | for (index = 0; index < mySize && index < otherSize; index++) |
383 | { |
384 | myColumn = (ColumnDescriptor) myColDl.elementAt(index); |
385 | otherColumn = (ColumnDescriptor) otherColumns.elementAt(index); |
386 | |
387 | /* |
388 | ** Just compare the types. Note that this will |
389 | ** say a decimal(x,y) != numeric(x,y) even though |
390 | ** it does. |
391 | */ |
392 | if (!(myColumn.getType()).isExactTypeAndLengthMatch( |
393 | (otherColumn.getType()))) |
394 | { |
395 | break; |
396 | } |
397 | } |
398 | |
399 | return (index == mySize && index == otherSize); |
400 | } |
401 | |
402 | /** |
403 | * Does a column intersect with our referenced columns |
404 | * @param columnArray columns to check |
405 | * |
406 | * Note-- this is not a static method. |
407 | */ |
408 | public boolean columnIntersects(int columnArray[]) |
409 | { |
410 | // call static method. |
411 | return doColumnsIntersect(getReferencedColumns(), columnArray); |
412 | } |
413 | |
414 | /** |
415 | * Does a column in the input set intersect with |
416 | * our referenced columns? |
417 | * |
418 | * @param otherColumns the columns to compare. If |
419 | * null, asssumed to mean all columns |
420 | * |
421 | * @param referencedColumns the columns referenced by the caller |
422 | * |
423 | * @return true/false |
424 | */ |
425 | static boolean doColumnsIntersect(int[] otherColumns, int[] referencedColumns) |
426 | { |
427 | /* |
428 | ** It is assumed that if otherColumns is null, then |
429 | ** all other columns are modified. In this case, |
430 | ** it is assumed that it intersects with some column |
431 | ** of ours, so just return true. |
432 | */ |
433 | if ((otherColumns == null) || (referencedColumns == null)) |
434 | { |
435 | return true; |
436 | } |
437 | |
438 | for (int outer = 0; outer < referencedColumns.length; outer++) |
439 | { |
440 | for (int inner = 0; inner < otherColumns.length; inner++) |
441 | { |
442 | if (referencedColumns[outer] == otherColumns[inner]) |
443 | { |
444 | return true; |
445 | } |
446 | } |
447 | } |
448 | return false; |
449 | } |
450 | |
451 | /** |
452 | * Convert the ColumnDescriptor to a String. |
453 | * |
454 | * @return A String representation of this ColumnDescriptor |
455 | */ |
456 | |
457 | public String toString() |
458 | { |
459 | if (SanityManager.DEBUG) |
460 | { |
461 | String tableDesc = |
462 | "table: " + |
463 | table.getQualifiedName() + "(" + |
464 | table.getUUID()+","+ |
465 | table.getTableType()+")"; |
466 | |
467 | return tableDesc + "\n"+ |
468 | "constraintName: " + constraintName + "\n" + |
469 | "constraintId: " + constraintId + "\n" + |
470 | "deferrable: " + deferrable + "\n" + |
471 | "initiallyDeferred: " + initiallyDeferred + "\n" + |
472 | "referencedColumns: " + referencedColumns + "\n" + |
473 | "schemaDesc: " + schemaDesc + "\n" |
474 | ; |
475 | } |
476 | else |
477 | { |
478 | return ""; |
479 | } |
480 | } |
481 | |
482 | //////////////////////////////////////////////////////////////////// |
483 | // |
484 | // PROVIDER INTERFACE |
485 | // |
486 | //////////////////////////////////////////////////////////////////// |
487 | |
488 | /** |
489 | @return the stored form of this provider |
490 | |
491 | @see Dependable#getDependableFinder |
492 | */ |
493 | public DependableFinder getDependableFinder() |
494 | { |
495 | return getDependableFinder(StoredFormatIds.CONSTRAINT_DESCRIPTOR_FINDER_V01_ID); |
496 | } |
497 | |
498 | /** |
499 | * Return the name of this Provider. (Useful for errors.) |
500 | * |
501 | * @return String The name of this provider. |
502 | */ |
503 | public String getObjectName() |
504 | { |
505 | return constraintName; |
506 | } |
507 | |
508 | /** |
509 | * Get the provider's UUID |
510 | * |
511 | * @return The provider's UUID |
512 | */ |
513 | public UUID getObjectID() |
514 | { |
515 | return constraintId; |
516 | } |
517 | |
518 | /** |
519 | * Get the provider's type. |
520 | * |
521 | * @return char The provider's type. |
522 | */ |
523 | public String getClassType() |
524 | { |
525 | return Dependable.CONSTRAINT; |
526 | } |
527 | |
528 | ////////////////////////////////////////////////////// |
529 | // |
530 | // DEPENDENT INTERFACE |
531 | // |
532 | ////////////////////////////////////////////////////// |
533 | /** |
534 | * Check that all of the dependent's dependencies are valid. |
535 | * |
536 | * @return true if the dependent is currently valid |
537 | */ |
538 | public synchronized boolean isValid() |
539 | { |
540 | return true; |
541 | } |
542 | |
543 | /** |
544 | * Prepare to mark the dependent as invalid (due to at least one of |
545 | * its dependencies being invalid). |
546 | * |
547 | * @param action The action causing the invalidation |
548 | * @param p the provider |
549 | * |
550 | * @exception StandardException thrown if unable to make it invalid |
551 | */ |
552 | public void prepareToInvalidate(Provider p, int action, |
553 | LanguageConnectionContext lcc) |
554 | throws StandardException |
555 | { |
556 | DependencyManager dm = getDataDictionary().getDependencyManager(); |
557 | |
558 | switch (action) |
559 | { |
560 | /* |
561 | ** A SET CONSTRAINT stmt will throw an SET_CONSTRAINTS action |
562 | ** when enabling/disabling constraints. We'll ignore it. |
563 | ** Same for SET TRIGGERS |
564 | */ |
565 | case DependencyManager.SET_CONSTRAINTS_ENABLE: |
566 | case DependencyManager.SET_CONSTRAINTS_DISABLE: |
567 | case DependencyManager.SET_TRIGGERS_ENABLE: |
568 | case DependencyManager.SET_TRIGGERS_DISABLE: |
569 | break; |
570 | |
571 | /* |
572 | ** Currently, the only thing we are depenedent |
573 | ** on is another constraint or an alias.. |
574 | */ |
575 | default: |
576 | throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT, |
577 | dm.getActionString(action), |
578 | p.getObjectName(), "CONSTRAINT", constraintName); |
579 | } |
580 | } |
581 | |
582 | /** |
583 | * Mark the dependent as invalid (due to at least one of |
584 | * its dependencies being invalid). Always an error |
585 | * for a constraint -- should never have gotten here. |
586 | * |
587 | * @param action The action causing the invalidation |
588 | * |
589 | * @exception StandardException thrown if called in sanity mode |
590 | */ |
591 | public void makeInvalid(int action, LanguageConnectionContext lcc) |
592 | throws StandardException |
593 | { |
594 | /* |
595 | ** SET_CONSTRAINTS/TRIGGERS is the only valid action |
596 | */ |
597 | if ((action != DependencyManager.SET_CONSTRAINTS_DISABLE) && |
598 | (action != DependencyManager.SET_CONSTRAINTS_ENABLE) && |
599 | (action != DependencyManager.SET_TRIGGERS_ENABLE) && |
600 | (action != DependencyManager.SET_TRIGGERS_DISABLE) |
601 | ) |
602 | { |
603 | /* |
604 | ** We should never get here, we should have barfed on |
605 | ** prepareToInvalidate(). |
606 | */ |
607 | if (SanityManager.DEBUG) |
608 | { |
609 | DependencyManager dm; |
610 | |
611 | dm = getDataDictionary().getDependencyManager(); |
612 | |
613 | SanityManager.THROWASSERT("makeInvalid("+ |
614 | dm.getActionString(action)+ |
615 | ") not expected to get called"); |
616 | } |
617 | } |
618 | } |
619 | |
620 | /** |
621 | * Attempt to revalidate the dependent. Meaningless |
622 | * for constraints. |
623 | */ |
624 | public void makeValid(LanguageConnectionContext lcc) |
625 | { |
626 | } |
627 | |
628 | /** @see TupleDescriptor#getDescriptorName */ |
629 | public String getDescriptorName() { return constraintName; } |
630 | |
631 | public String getDescriptorType() { return "Constraint"; } |
632 | } |