Source code: org/apache/derby/impl/sql/execute/RIBulkChecker.java
1 /*
2
3 Derby - Class org.apache.derby.impl.sql.execute.RIBulkChecker
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.impl.sql.execute;
22
23 import org.apache.derby.iapi.services.sanity.SanityManager;
24
25 import org.apache.derby.iapi.error.StandardException;
26 import org.apache.derby.iapi.sql.ResultSet;
27
28 import org.apache.derby.iapi.types.BooleanDataValue;
29 import org.apache.derby.iapi.types.DataValueDescriptor;
30 import org.apache.derby.iapi.types.RowLocation;
31 import org.apache.derby.iapi.sql.execute.ExecRow;
32 import org.apache.derby.iapi.sql.LanguageProperties;
33
34 import org.apache.derby.iapi.store.access.ConglomerateController;
35 import org.apache.derby.iapi.store.access.GenericScanController;
36 import org.apache.derby.iapi.store.access.GroupFetchScanController;
37 import org.apache.derby.iapi.store.access.ScanController;
38 import org.apache.derby.iapi.store.access.TransactionController;
39 import org.apache.derby.iapi.types.DataValueDescriptor;
40
41 import org.apache.derby.iapi.services.io.FormatableBitSet;
42
43 /**
44 * Do a merge run comparing all the foreign keys from the
45 * foreign key conglomerate against the referenced keys
46 * from the primary key conglomerate. The scanControllers
47 * are passed in by the caller (caller controls locking on
48 * said conglomerates).
49 * <p>
50 * The comparision is done via a merge. Consequently,
51 * it is imperative that the scans are on keyed conglomerates
52 * (indexes) and that the referencedKeyScan is a unique scan.
53 * <p>
54 * Performance is no worse than N + M where N is foreign key
55 * rows and M is primary key rows.
56 * <p>
57 * Bulk fetch is used to further speed performance. The
58 * fetch size is LanguageProperties.BULK_FETCH_DEFAULT
59 *
60 * @see LanguageProperties
61 */
62 public class RIBulkChecker
63 {
64 private static final int EQUAL = 0;
65 private static final int GREATER_THAN = 1;
66 private static final int LESS_THAN = -1;
67
68 private FKInfo fkInfo;
69 private GroupFetchScanController referencedKeyScan;
70 private DataValueDescriptor[][] referencedKeyRowArray;
71 private GroupFetchScanController foreignKeyScan;
72 private DataValueDescriptor[][] foreignKeyRowArray;
73 private ConglomerateController unreferencedCC;
74 private int failedCounter;
75 private boolean quitOnFirstFailure;
76 private int numColumns;
77 private int currRefRowIndex;
78 private int currFKRowIndex;
79 private int lastRefRowIndex;
80 private int lastFKRowIndex;
81 private ExecRow firstRowToFail;
82
83 /**
84 * Create a RIBulkChecker
85 *
86 * @param referencedKeyScan scan of the referenced key's
87 * backing index. must be unique
88 * @param foreignKeyScan scan of the foreign key's
89 * backing index
90 * @param templateRow a template row for the indexes.
91 * Will be cloned when it is used.
92 * Must be a full index row.
93 * @param quitOnFirstFailure quit on first unreferenced key
94 * @param unreferencedKeysCC put unreferenced keys here
95 * @param firstRowToFail the first row that fails the constraint
96 * is copied to this, if non-null
97 */
98 public RIBulkChecker
99 (
100 GroupFetchScanController referencedKeyScan,
101 GroupFetchScanController foreignKeyScan,
102 ExecRow templateRow,
103 boolean quitOnFirstFailure,
104 ConglomerateController unreferencedCC,
105 ExecRow firstRowToFail
106 )
107 {
108 this.referencedKeyScan = referencedKeyScan;
109 this.foreignKeyScan = foreignKeyScan;
110 this.quitOnFirstFailure = quitOnFirstFailure;
111 this.unreferencedCC = unreferencedCC;
112 this.firstRowToFail = firstRowToFail;
113
114 foreignKeyRowArray = new DataValueDescriptor[LanguageProperties.BULK_FETCH_DEFAULT_INT][];
115 foreignKeyRowArray[0] = templateRow.getRowArrayClone();
116 referencedKeyRowArray = new DataValueDescriptor[LanguageProperties.BULK_FETCH_DEFAULT_INT][];
117 referencedKeyRowArray[0]= templateRow.getRowArrayClone();
118 failedCounter = 0;
119 numColumns = templateRow.getRowArray().length - 1;
120 currFKRowIndex = -1;
121 currRefRowIndex = -1;
122 }
123
124 /**
125 * Perform the check.
126 *
127 * @return the number of failed rows
128 *
129 * @exception StandardException on error
130 */
131 public int doCheck()
132 throws StandardException
133 {
134 DataValueDescriptor[] foreignKey;
135 DataValueDescriptor[] referencedKey;
136
137 int compareResult;
138
139 referencedKey = getNextRef();
140
141 /*
142 ** For each foreign key
143 **
144 ** while (fk > pk)
145 ** next pk
146 ** if no next pk
147 ** failed
148 **
149 ** if fk != pk
150 ** failed
151 */
152 while ((foreignKey = getNextFK()) != null)
153 {
154 /*
155 ** If all of the foreign key is not null and there are no
156 ** referenced keys, then everything fails
157 ** ANSI standard says the referential constraint is
158 ** satisfied if either at least one of the values of the
159 ** referencing columns(i.e., foreign key) is null or the
160 ** value of each referencing column is equal to the
161 ** corresponding referenced column in the referenced table
162 */
163 if (!anyNull(foreignKey) && referencedKey == null)
164 {
165 do
166 {
167 failure(foreignKey);
168 if (quitOnFirstFailure)
169 {
170 return 1;
171 }
172 } while ((foreignKey = getNextFK()) != null);
173 return failedCounter;
174 }
175
176 while ((compareResult = greaterThan(foreignKey, referencedKey)) == GREATER_THAN)
177 {
178 if ((referencedKey = getNextRef()) == null)
179 {
180 do
181 {
182 failure(foreignKey);
183 if (quitOnFirstFailure)
184 {
185 return 1;
186 }
187 } while ((foreignKey = getNextFK()) != null);
188 return failedCounter;
189 }
190 }
191
192 if (compareResult != EQUAL)
193 {
194 failure(foreignKey);
195 if (quitOnFirstFailure)
196 {
197 return 1;
198 }
199 }
200 }
201 return failedCounter;
202 }
203
204
205 /*
206 * Use bulk fetch to get the next set of rows,
207 * or read the next out of our internal array.
208 */
209 private DataValueDescriptor[] getNextFK()
210 throws StandardException
211 {
212 if ((currFKRowIndex > lastFKRowIndex) ||
213 (currFKRowIndex == -1))
214 {
215 int rowCount =
216 foreignKeyScan.fetchNextGroup(foreignKeyRowArray, (RowLocation[]) null);
217
218 if (rowCount == 0)
219 {
220 currFKRowIndex = -1;
221 return null;
222 }
223
224 lastFKRowIndex = rowCount - 1;
225 currFKRowIndex = 0;
226 }
227
228 return foreignKeyRowArray[currFKRowIndex++];
229 }
230
231 /*
232 * Use bulk fetch to get the next set of rows,
233 * or read the next out of our internal array.
234 */
235 private DataValueDescriptor[] getNextRef()
236 throws StandardException
237 {
238 if ((currRefRowIndex > lastRefRowIndex) ||
239 (currRefRowIndex == -1))
240 {
241 int rowCount =
242 referencedKeyScan.fetchNextGroup(referencedKeyRowArray, (RowLocation[]) null);
243
244 if (rowCount == 0)
245 {
246 currRefRowIndex = -1;
247 return null;
248 }
249
250 lastRefRowIndex = rowCount - 1;
251 currRefRowIndex = 0;
252 }
253
254 return referencedKeyRowArray[currRefRowIndex++];
255 }
256
257 private void failure(DataValueDescriptor[] foreignKeyRow)
258 throws StandardException
259 {
260 if (failedCounter == 0)
261 {
262 if (firstRowToFail != null)
263 {
264 firstRowToFail.setRowArray(foreignKeyRow);
265 // clone it
266 firstRowToFail.setRowArray(firstRowToFail.getRowArrayClone());
267 }
268 }
269
270 failedCounter++;
271 if (unreferencedCC != null)
272 {
273 unreferencedCC.insert(foreignKeyRow);
274 }
275 }
276 /*
277 ** Returns true if any of the foreign keys are null
278 ** otherwise, false.
279 */
280 private boolean anyNull(DataValueDescriptor[] fkRowArray)
281 throws StandardException
282 {
283 DataValueDescriptor fkCol;
284
285 /*
286 ** Check all columns excepting the row location.
287 */
288 for (int i = 0; i < numColumns; i++)
289 {
290 fkCol = (DataValueDescriptor)fkRowArray[i];
291
292 /*
293 ** If ANY column in the fk is null,
294 ** return true
295 */
296 if (fkCol.isNull())
297 {
298 return true;
299 }
300 }
301 return false;
302
303 }
304
305 private int greaterThan(DataValueDescriptor[] fkRowArray, DataValueDescriptor[] refRowArray)
306 throws StandardException
307 {
308 DataValueDescriptor fkCol;
309 DataValueDescriptor refCol;
310 int result;
311
312 /*
313 ** If ANY column in the fk is null,
314 ** it is assumed to be equal
315 */
316 if (anyNull(fkRowArray))
317 return EQUAL;
318
319 for (int i = 0; i < numColumns; i++)
320 {
321 fkCol = (DataValueDescriptor)fkRowArray[i];
322 refCol = (DataValueDescriptor)refRowArray[i];
323
324 result = fkCol.compare(refCol);
325
326 if (result == 1)
327 {
328 return GREATER_THAN;
329 }
330 else if (result == -1)
331 {
332 return LESS_THAN;
333 }
334
335 /*
336 ** If they are equal, go on to the next
337 ** column.
338 */
339 }
340
341 /*
342 ** If we got here they must be equal
343 */
344 return EQUAL;
345 }
346 }