001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.RIBulkChecker
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.iapi.error.StandardException;
027: import org.apache.derby.iapi.sql.ResultSet;
028:
029: import org.apache.derby.iapi.types.BooleanDataValue;
030: import org.apache.derby.iapi.types.DataValueDescriptor;
031: import org.apache.derby.iapi.types.RowLocation;
032: import org.apache.derby.iapi.sql.execute.ExecRow;
033: import org.apache.derby.iapi.sql.LanguageProperties;
034:
035: import org.apache.derby.iapi.store.access.ConglomerateController;
036: import org.apache.derby.iapi.store.access.GenericScanController;
037: import org.apache.derby.iapi.store.access.GroupFetchScanController;
038: import org.apache.derby.iapi.store.access.ScanController;
039: import org.apache.derby.iapi.store.access.TransactionController;
040: import org.apache.derby.iapi.types.DataValueDescriptor;
041:
042: import org.apache.derby.iapi.services.io.FormatableBitSet;
043:
044: /**
045: * Do a merge run comparing all the foreign keys from the
046: * foreign key conglomerate against the referenced keys
047: * from the primary key conglomerate. The scanControllers
048: * are passed in by the caller (caller controls locking on
049: * said conglomerates).
050: * <p>
051: * The comparision is done via a merge. Consequently,
052: * it is imperative that the scans are on keyed conglomerates
053: * (indexes) and that the referencedKeyScan is a unique scan.
054: * <p>
055: * Performance is no worse than N + M where N is foreign key
056: * rows and M is primary key rows.
057: * <p>
058: * Bulk fetch is used to further speed performance. The
059: * fetch size is LanguageProperties.BULK_FETCH_DEFAULT
060: *
061: * @see LanguageProperties
062: */
063: public class RIBulkChecker {
064: private static final int EQUAL = 0;
065: private static final int GREATER_THAN = 1;
066: private static final int LESS_THAN = -1;
067:
068: private FKInfo fkInfo;
069: private GroupFetchScanController referencedKeyScan;
070: private DataValueDescriptor[][] referencedKeyRowArray;
071: private GroupFetchScanController foreignKeyScan;
072: private DataValueDescriptor[][] foreignKeyRowArray;
073: private ConglomerateController unreferencedCC;
074: private int failedCounter;
075: private boolean quitOnFirstFailure;
076: private int numColumns;
077: private int currRefRowIndex;
078: private int currFKRowIndex;
079: private int lastRefRowIndex;
080: private int lastFKRowIndex;
081: private ExecRow firstRowToFail;
082:
083: /**
084: * Create a RIBulkChecker
085: *
086: * @param referencedKeyScan scan of the referenced key's
087: * backing index. must be unique
088: * @param foreignKeyScan scan of the foreign key's
089: * backing index
090: * @param templateRow a template row for the indexes.
091: * Will be cloned when it is used.
092: * Must be a full index row.
093: * @param quitOnFirstFailure quit on first unreferenced key
094: * @param unreferencedCC put unreferenced keys here
095: * @param firstRowToFail the first row that fails the constraint
096: * is copied to this, if non-null
097: */
098: public RIBulkChecker(GroupFetchScanController referencedKeyScan,
099: GroupFetchScanController foreignKeyScan,
100: ExecRow templateRow, boolean quitOnFirstFailure,
101: ConglomerateController unreferencedCC,
102: ExecRow firstRowToFail) {
103: this .referencedKeyScan = referencedKeyScan;
104: this .foreignKeyScan = foreignKeyScan;
105: this .quitOnFirstFailure = quitOnFirstFailure;
106: this .unreferencedCC = unreferencedCC;
107: this .firstRowToFail = firstRowToFail;
108:
109: foreignKeyRowArray = new DataValueDescriptor[LanguageProperties.BULK_FETCH_DEFAULT_INT][];
110: foreignKeyRowArray[0] = templateRow.getRowArrayClone();
111: referencedKeyRowArray = new DataValueDescriptor[LanguageProperties.BULK_FETCH_DEFAULT_INT][];
112: referencedKeyRowArray[0] = templateRow.getRowArrayClone();
113: failedCounter = 0;
114: numColumns = templateRow.getRowArray().length - 1;
115: currFKRowIndex = -1;
116: currRefRowIndex = -1;
117: }
118:
119: /**
120: * Perform the check.
121: *
122: * @return the number of failed rows
123: *
124: * @exception StandardException on error
125: */
126: public int doCheck() throws StandardException {
127: DataValueDescriptor[] foreignKey;
128: DataValueDescriptor[] referencedKey;
129:
130: int compareResult;
131:
132: referencedKey = getNextRef();
133:
134: /*
135: ** For each foreign key
136: **
137: ** while (fk > pk)
138: ** next pk
139: ** if no next pk
140: ** failed
141: **
142: ** if fk != pk
143: ** failed
144: */
145: while ((foreignKey = getNextFK()) != null) {
146: /*
147: ** If all of the foreign key is not null and there are no
148: ** referenced keys, then everything fails
149: ** ANSI standard says the referential constraint is
150: ** satisfied if either at least one of the values of the
151: ** referencing columns(i.e., foreign key) is null or the
152: ** value of each referencing column is equal to the
153: ** corresponding referenced column in the referenced table
154: */
155: if (!anyNull(foreignKey) && referencedKey == null) {
156: do {
157: failure(foreignKey);
158: if (quitOnFirstFailure) {
159: return 1;
160: }
161: } while ((foreignKey = getNextFK()) != null);
162: return failedCounter;
163: }
164:
165: while ((compareResult = greaterThan(foreignKey,
166: referencedKey)) == GREATER_THAN) {
167: if ((referencedKey = getNextRef()) == null) {
168: do {
169: failure(foreignKey);
170: if (quitOnFirstFailure) {
171: return 1;
172: }
173: } while ((foreignKey = getNextFK()) != null);
174: return failedCounter;
175: }
176: }
177:
178: if (compareResult != EQUAL) {
179: failure(foreignKey);
180: if (quitOnFirstFailure) {
181: return 1;
182: }
183: }
184: }
185: return failedCounter;
186: }
187:
188: /*
189: * Use bulk fetch to get the next set of rows,
190: * or read the next out of our internal array.
191: */
192: private DataValueDescriptor[] getNextFK() throws StandardException {
193: if ((currFKRowIndex > lastFKRowIndex) || (currFKRowIndex == -1)) {
194: int rowCount = foreignKeyScan.fetchNextGroup(
195: foreignKeyRowArray, (RowLocation[]) null);
196:
197: if (rowCount == 0) {
198: currFKRowIndex = -1;
199: return null;
200: }
201:
202: lastFKRowIndex = rowCount - 1;
203: currFKRowIndex = 0;
204: }
205:
206: return foreignKeyRowArray[currFKRowIndex++];
207: }
208:
209: /*
210: * Use bulk fetch to get the next set of rows,
211: * or read the next out of our internal array.
212: */
213: private DataValueDescriptor[] getNextRef() throws StandardException {
214: if ((currRefRowIndex > lastRefRowIndex)
215: || (currRefRowIndex == -1)) {
216: int rowCount = referencedKeyScan.fetchNextGroup(
217: referencedKeyRowArray, (RowLocation[]) null);
218:
219: if (rowCount == 0) {
220: currRefRowIndex = -1;
221: return null;
222: }
223:
224: lastRefRowIndex = rowCount - 1;
225: currRefRowIndex = 0;
226: }
227:
228: return referencedKeyRowArray[currRefRowIndex++];
229: }
230:
231: private void failure(DataValueDescriptor[] foreignKeyRow)
232: throws StandardException {
233: if (failedCounter == 0) {
234: if (firstRowToFail != null) {
235: firstRowToFail.setRowArray(foreignKeyRow);
236: // clone it
237: firstRowToFail.setRowArray(firstRowToFail
238: .getRowArrayClone());
239: }
240: }
241:
242: failedCounter++;
243: if (unreferencedCC != null) {
244: unreferencedCC.insert(foreignKeyRow);
245: }
246: }
247:
248: /*
249: ** Returns true if any of the foreign keys are null
250: ** otherwise, false.
251: */
252: private boolean anyNull(DataValueDescriptor[] fkRowArray)
253: throws StandardException {
254: DataValueDescriptor fkCol;
255:
256: /*
257: ** Check all columns excepting the row location.
258: */
259: for (int i = 0; i < numColumns; i++) {
260: fkCol = (DataValueDescriptor) fkRowArray[i];
261:
262: /*
263: ** If ANY column in the fk is null,
264: ** return true
265: */
266: if (fkCol.isNull()) {
267: return true;
268: }
269: }
270: return false;
271:
272: }
273:
274: private int greaterThan(DataValueDescriptor[] fkRowArray,
275: DataValueDescriptor[] refRowArray) throws StandardException {
276: DataValueDescriptor fkCol;
277: DataValueDescriptor refCol;
278: int result;
279:
280: /*
281: ** If ANY column in the fk is null,
282: ** it is assumed to be equal
283: */
284: if (anyNull(fkRowArray))
285: return EQUAL;
286:
287: for (int i = 0; i < numColumns; i++) {
288: fkCol = (DataValueDescriptor) fkRowArray[i];
289: refCol = (DataValueDescriptor) refRowArray[i];
290:
291: result = fkCol.compare(refCol);
292:
293: if (result == 1) {
294: return GREATER_THAN;
295: } else if (result == -1) {
296: return LESS_THAN;
297: }
298:
299: /*
300: ** If they are equal, go on to the next
301: ** column.
302: */
303: }
304:
305: /*
306: ** If we got here they must be equal
307: */
308: return EQUAL;
309: }
310: }
|