Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }