1 | /* |
2 | |
3 | Derby - Class org.apache.derby.iapi.sql.dictionary.ReferencedKeyConstraintDescriptor |
4 | |
5 | Copyright 1998, 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.reference.SQLState; |
26 | import org.apache.derby.iapi.services.sanity.SanityManager; |
27 | import org.apache.derby.iapi.sql.StatementType; |
28 | import org.apache.derby.iapi.services.io.StoredFormatIds; |
29 | import org.apache.derby.catalog.UUID; |
30 | /** |
31 | * A ReferencedConstraintDeescriptor is a primary key or a unique |
32 | * key that is referenced by a foreign key. |
33 | * |
34 | * @author Jamie |
35 | */ |
36 | public class ReferencedKeyConstraintDescriptor extends KeyConstraintDescriptor |
37 | { |
38 | /** |
39 | public interface to this descriptor: |
40 | <ol> |
41 | <li>public boolean hasSelfReferencingFK(ConstraintDescriptorList cdl, int type) |
42 | throws StandardException;</li> |
43 | <li>public ConstraintDescriptorList getForeignKeyConstraints(int type) throws StandardException;</li> |
44 | <li>public boolean isReferenced();</li> |
45 | <li>public int getReferenceCount();</li> |
46 | <li>public int incrementReferenceCount();</li> |
47 | <li>public int decrementReferenceCount();</li> |
48 | </ol> |
49 | */ |
50 | |
51 | //Implementation |
52 | private final int constraintType; |
53 | |
54 | int referenceCount; |
55 | |
56 | // enabled foreign keys |
57 | private ConstraintDescriptorList fkEnabledConstraintList; |
58 | // all foreign keys |
59 | private ConstraintDescriptorList fkConstraintList; |
60 | |
61 | private boolean checkedSelfReferencing; |
62 | private boolean hasSelfReferencing; |
63 | |
64 | /** |
65 | * Constructor for a KeyConstraintDescriptorImpl |
66 | * |
67 | * @param constraintType The type of the constraint |
68 | * @param dataDictionary The data dictionary that this descriptor lives in |
69 | * @param table The descriptor of the table the constraint is on |
70 | * @param constraintName The name of the constraint. |
71 | * @param deferrable If the constraint can be deferred. |
72 | * @param initiallyDeferred If the constraint starts life deferred. |
73 | * @param columns columns involved in the constraint |
74 | * @param constraintId UUID of constraint |
75 | * @param indexId The UUID for the backing index |
76 | * @param schemaDesc The SchemaDescriptor for the constraint |
77 | * @param isEnabled is the constraint enabled? |
78 | * @param referenceCount number of FKs (enabled only) |
79 | */ |
80 | protected ReferencedKeyConstraintDescriptor(int constraintType, |
81 | DataDictionary dataDictionary, |
82 | TableDescriptor table, |
83 | String constraintName, |
84 | boolean deferrable, |
85 | boolean initiallyDeferred, |
86 | int[] columns, |
87 | UUID constraintId, |
88 | UUID indexId, |
89 | SchemaDescriptor schemaDesc, |
90 | boolean isEnabled, |
91 | int referenceCount |
92 | ) |
93 | { |
94 | super(dataDictionary, table, constraintName, deferrable, |
95 | initiallyDeferred, columns, |
96 | constraintId, indexId, schemaDesc, isEnabled); |
97 | this.referenceCount = referenceCount; |
98 | this.constraintType = constraintType; |
99 | } |
100 | |
101 | public final int getConstraintType() { |
102 | return constraintType; |
103 | } |
104 | |
105 | /** |
106 | * Am I referenced by a FK on the same table? |
107 | * |
108 | * @param cdl ConstraintDescriptorList for the table |
109 | * @param type ConstraintDescriptor.(ENABLED|DISABLED|ALL) |
110 | * |
111 | * @return true/false |
112 | * |
113 | * @exception StandardException on error |
114 | */ |
115 | public boolean hasSelfReferencingFK(ConstraintDescriptorList cdl, int type) |
116 | throws StandardException |
117 | { |
118 | if (SanityManager.DEBUG) |
119 | { |
120 | checkType(type); |
121 | } |
122 | |
123 | if (checkedSelfReferencing) |
124 | { |
125 | return hasSelfReferencing; |
126 | } |
127 | |
128 | ConstraintDescriptor cd; |
129 | ForeignKeyConstraintDescriptor fkcd; |
130 | /* Get a full list of referencing keys, if caller |
131 | * passed in null CDL. |
132 | */ |
133 | if (cdl == null) |
134 | { |
135 | cdl = getForeignKeyConstraints(type); |
136 | } |
137 | int cdlSize = cdl.size(); |
138 | |
139 | for (int index = 0; index < cdlSize; index++) |
140 | { |
141 | cd = (ConstraintDescriptor) cdl.elementAt(index); |
142 | if (! (cd instanceof ForeignKeyConstraintDescriptor)) |
143 | { |
144 | continue; |
145 | } |
146 | |
147 | fkcd = (ForeignKeyConstraintDescriptor) cd; |
148 | if (fkcd.getReferencedConstraintId().equals(getUUID())) |
149 | { |
150 | hasSelfReferencing = true; |
151 | break; |
152 | } |
153 | } |
154 | return hasSelfReferencing; |
155 | } |
156 | |
157 | |
158 | /** |
159 | * Am I referenced by a FK on another table? |
160 | * @param type ConstraintDescriptor.(ENABLED|DISABLED|ALL) |
161 | * @return true/false |
162 | * @exception StandardException on error |
163 | */ |
164 | public boolean hasNonSelfReferencingFK(int type) |
165 | throws StandardException |
166 | { |
167 | |
168 | boolean hasNonSelfReferenceFk = false; |
169 | |
170 | if (SanityManager.DEBUG) |
171 | { |
172 | checkType(type); |
173 | } |
174 | |
175 | ConstraintDescriptor cd; |
176 | ForeignKeyConstraintDescriptor fkcd; |
177 | // Get a full list of referencing keys, |
178 | ConstraintDescriptorList cdl = getForeignKeyConstraints(type); |
179 | int cdlSize = cdl.size(); |
180 | |
181 | for (int index = 0; index < cdlSize; index++) |
182 | { |
183 | cd = (ConstraintDescriptor) cdl.elementAt(index); |
184 | if (! (cd instanceof ForeignKeyConstraintDescriptor)) |
185 | { |
186 | continue; |
187 | } |
188 | |
189 | fkcd = (ForeignKeyConstraintDescriptor) cd; |
190 | if(!(fkcd.getTableId().equals(getTableId()))) |
191 | { |
192 | hasNonSelfReferenceFk = true; |
193 | break; |
194 | } |
195 | } |
196 | return hasNonSelfReferenceFk; |
197 | } |
198 | |
199 | |
200 | |
201 | /** |
202 | * Get the referencing foreign key constraints |
203 | * |
204 | * @param type ConstraintDescriptor.(ENABLED|DISABLED|ALL) |
205 | * |
206 | * @return the list of constraints (ConstraintDescriptorListImpl) |
207 | * |
208 | * @exception StandardException on error |
209 | */ |
210 | public ConstraintDescriptorList getForeignKeyConstraints(int type) |
211 | throws StandardException |
212 | { |
213 | if (SanityManager.DEBUG) |
214 | { |
215 | checkType(type); |
216 | } |
217 | |
218 | // optimized for this case |
219 | if (type == ENABLED) |
220 | { |
221 | // optimization to avoid any lookups if we know we |
222 | // aren't referenced. |
223 | if (!isReferenced()) |
224 | { |
225 | return new ConstraintDescriptorList(); |
226 | } |
227 | else if (fkEnabledConstraintList != null) |
228 | { |
229 | return fkEnabledConstraintList; |
230 | } |
231 | else if (fkConstraintList == null) |
232 | { |
233 | fkConstraintList = getDataDictionary().getForeignKeys(constraintId); |
234 | } |
235 | fkEnabledConstraintList = fkConstraintList.getConstraintDescriptorList(true); |
236 | return fkEnabledConstraintList; |
237 | } |
238 | |
239 | // not optimized for this case |
240 | else if (type == DISABLED) |
241 | { |
242 | if (fkConstraintList == null) |
243 | { |
244 | fkConstraintList = getDataDictionary().getForeignKeys(constraintId); |
245 | } |
246 | return fkConstraintList.getConstraintDescriptorList(false); |
247 | } |
248 | else |
249 | { |
250 | if (fkConstraintList == null) |
251 | { |
252 | fkConstraintList = getDataDictionary().getForeignKeys(constraintId); |
253 | } |
254 | return fkConstraintList; |
255 | } |
256 | } |
257 | |
258 | /** |
259 | * Is this constraint referenced? Returns |
260 | * true if there are enabled fks that |
261 | * reference this constraint. |
262 | * |
263 | * @return false |
264 | */ |
265 | public boolean isReferenced() |
266 | { |
267 | return referenceCount != 0; |
268 | } |
269 | |
270 | /** |
271 | * Get the number of enabled fks that |
272 | * reference this key. |
273 | * |
274 | * @return the number of fks |
275 | */ |
276 | public int getReferenceCount() |
277 | { |
278 | return referenceCount; |
279 | } |
280 | |
281 | /** |
282 | * Bump the reference count by one. |
283 | * |
284 | * @return the number of fks |
285 | */ |
286 | public int incrementReferenceCount() |
287 | { |
288 | return referenceCount++; |
289 | } |
290 | |
291 | /** |
292 | * Decrement the reference count by one. |
293 | * |
294 | * @return the number of fks |
295 | */ |
296 | public int decrementReferenceCount() |
297 | { |
298 | return referenceCount--; |
299 | } |
300 | |
301 | /** |
302 | * Does this constraint need to fire on this type of |
303 | * DML? For referenced keys, fire if referenced by |
304 | * a fk, and stmt is delete or bulk insert replace, |
305 | * or stmt is update and columns intersect. |
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 boolean needsToFire(int stmtType, int[] modifiedCols) |
314 | { |
315 | /* |
316 | ** If we are disabled, we never fire |
317 | */ |
318 | if (!isEnabled) |
319 | { |
320 | return false; |
321 | } |
322 | |
323 | if (!isReferenced() || |
324 | (stmtType == StatementType.INSERT)) |
325 | { |
326 | return false; |
327 | } |
328 | |
329 | if (stmtType == StatementType.DELETE || |
330 | stmtType == StatementType.BULK_INSERT_REPLACE) |
331 | { |
332 | return true; |
333 | } |
334 | |
335 | // if update, only relevant if columns intersect |
336 | return doColumnsIntersect(modifiedCols, getReferencedColumns()); |
337 | } |
338 | |
339 | private void checkType(int type) throws StandardException |
340 | { |
341 | if (SanityManager.DEBUG) |
342 | { |
343 | switch (type) |
344 | { |
345 | case ENABLED: |
346 | case DISABLED: |
347 | case ALL: |
348 | break; |
349 | default: |
350 | SanityManager.THROWASSERT("constraint type "+type+" is invalid"); |
351 | } |
352 | } |
353 | } |
354 | |
355 | } |