KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > execute > IndexChanger


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.execute.IndexChanger
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.execute;
23
24 import org.apache.derby.iapi.services.monitor.Monitor;
25 import org.apache.derby.iapi.services.sanity.SanityManager;
26 import org.apache.derby.iapi.error.StandardException;
27 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
28 import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
29 import org.apache.derby.iapi.sql.ResultDescription;
30 import org.apache.derby.iapi.sql.execute.CursorResultSet;
31 import org.apache.derby.iapi.sql.execute.ExecRow;
32 import org.apache.derby.iapi.sql.execute.ExecIndexRow;
33 import org.apache.derby.iapi.sql.execute.ExecutionContext;
34 import org.apache.derby.iapi.sql.execute.ExecutionFactory;
35
36 import org.apache.derby.iapi.reference.SQLState;
37
38 import org.apache.derby.iapi.sql.Activation;
39
40 import org.apache.derby.iapi.store.access.ConglomerateController;
41 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
42 import org.apache.derby.iapi.store.access.ScanController;
43 import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
44 import org.apache.derby.iapi.store.access.TransactionController;
45
46 import org.apache.derby.catalog.UUID;
47 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
48 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
49 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
50 import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
51 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
52
53 import org.apache.derby.iapi.types.DataValueDescriptor;
54 import org.apache.derby.iapi.types.RowLocation;
55
56 import org.apache.derby.iapi.services.io.FormatableBitSet;
57 import org.apache.derby.iapi.services.i18n.MessageService;
58 import java.util.Properties JavaDoc;
59
60 /**
61   Perform Index maintenace associated with DML operations for a single index.
62   */

63 public class IndexChanger
64 {
65     private IndexRowGenerator irg;
66     //Index Conglomerate ID
67
private long indexCID;
68     private DynamicCompiledOpenConglomInfo indexDCOCI;
69     private StaticCompiledOpenConglomInfo indexSCOCI;
70     private String JavaDoc indexName;
71     private ConglomerateController baseCC;
72     private TransactionController tc;
73     private int lockMode;
74     private FormatableBitSet baseRowReadMap;
75
76     private ConglomerateController indexCC = null;
77     private ScanController indexSC = null;
78
79     private LanguageConnectionContext lcc;
80
81     //
82
//Index rows used by this module to perform DML.
83
private ExecIndexRow ourIndexRow = null;
84     private ExecIndexRow ourUpdatedIndexRow = null;
85
86     private TemporaryRowHolderImpl rowHolder = null;
87     private boolean rowHolderPassedIn;
88     private int isolationLevel;
89     private Activation activation;
90     private boolean ownIndexSC = true;
91
92     /**
93       Create an IndexChanger
94
95       @param irg the IndexRowGenerator for the index.
96       @param indexCID the conglomerate id for the index.
97       @param indexSCOCI the SCOCI for the idexes.
98       @param indexDCOCI the DCOCI for the idexes.
99       @param baseCC the ConglomerateController for the base table.
100       @param tc The TransactionController
101       @param lockMode The lock mode (granularity) to use
102       @param baseRowReadMap Map of columns read in. 1 based.
103       @param isolationLevel Isolation level to use.
104       @param activation Current activation
105
106       @exception StandardException Thrown on error
107       */

108     public IndexChanger
109     (
110         IndexRowGenerator irg,
111         long indexCID,
112         StaticCompiledOpenConglomInfo indexSCOCI,
113         DynamicCompiledOpenConglomInfo indexDCOCI,
114         String JavaDoc indexName,
115         ConglomerateController baseCC,
116         TransactionController tc,
117         int lockMode,
118         FormatableBitSet baseRowReadMap,
119         int isolationLevel,
120         Activation activation
121     )
122          throws StandardException
123     {
124         this.irg = irg;
125         this.indexCID = indexCID;
126         this.indexSCOCI = indexSCOCI;
127         this.indexDCOCI = indexDCOCI;
128         this.baseCC = baseCC;
129         this.tc = tc;
130         this.lockMode = lockMode;
131         this.baseRowReadMap = baseRowReadMap;
132         this.rowHolderPassedIn = false;
133         this.isolationLevel = isolationLevel;
134         this.activation = activation;
135         this.indexName = indexName;
136
137         // activation will be null when called from DataDictionary
138
if (activation != null && activation.getIndexConglomerateNumber() == indexCID)
139         {
140             ownIndexSC = false;
141         }
142     
143         if (SanityManager.DEBUG)
144         {
145             SanityManager.ASSERT(tc != null,
146                 "TransactionController argument to constructor is null");
147             SanityManager.ASSERT(irg != null,
148                 "IndexRowGenerator argument to constructor is null");
149         }
150     }
151
152     /**
153      * Set the row holder for this changer to use.
154      * If the row holder is set, it wont bother
155      * saving copies of rows needed for deferred
156      * processing. Also, it will never close the
157      * passed in rowHolder.
158      *
159      * @param rowHolder the row holder
160      */

161     public void setRowHolder(TemporaryRowHolderImpl rowHolder)
162     {
163         this.rowHolder = rowHolder;
164         rowHolderPassedIn = (rowHolder != null);
165     }
166
167     /**
168      * Propagate the heap's ConglomerateController to
169      * this index changer.
170      *
171      * @param baseCC The heap's ConglomerateController.
172      */

173     public void setBaseCC(ConglomerateController baseCC)
174     {
175         this.baseCC = baseCC;
176     }
177
178     /**
179       Set the column values for 'ourIndexRow' to refer to
180       a base table row and location provided by the caller.
181       The idea here is to
182       @param baseRow a base table row.
183       @param baseRowLoc baseRowLoc baseRow's location
184       @exception StandardException Thrown on error
185       */

186     private void setOurIndexRow(ExecRow baseRow,
187                                 RowLocation baseRowLoc)
188          throws StandardException
189     {
190             if (ourIndexRow == null)
191                 ourIndexRow = irg.getIndexRowTemplate();
192         
193             irg.getIndexRow(baseRow, baseRowLoc, ourIndexRow, baseRowReadMap);
194     }
195
196     /**
197       Set the column values for 'ourUpdatedIndexRow' to refer to
198       a base table row and location provided by the caller.
199       The idea here is to
200       @param baseRow a base table row.
201       @param baseRowLoc baseRowLoc baseRow's location
202       @exception StandardException Thrown on error
203       */

204     private void setOurUpdatedIndexRow(ExecRow baseRow,
205                                 RowLocation baseRowLoc)
206          throws StandardException
207     {
208             if (ourUpdatedIndexRow == null)
209                 ourUpdatedIndexRow = irg.getIndexRowTemplate();
210         
211             irg.getIndexRow(baseRow, baseRowLoc, ourUpdatedIndexRow, baseRowReadMap);
212     }
213
214     /**
215      * Determine whether or not any columns in the current index
216      * row are being changed by the update. No need to update the
217      * index if no columns changed.
218      *
219      * @return Nothing.
220      *
221      * @exception StandardException Thrown on error
222      */

223     private boolean indexRowChanged()
224         throws StandardException
225     {
226         int numColumns = ourIndexRow.nColumns();
227         for (int index = 1; index <= numColumns; index++)
228         {
229             DataValueDescriptor oldOrderable = ourIndexRow.getColumn(index);
230             DataValueDescriptor newOrderable = ourUpdatedIndexRow.getColumn(index);
231             if (! (oldOrderable.compare(DataValueDescriptor.ORDER_OP_EQUALS, newOrderable, true, true)))
232             {
233                 return true;
234             }
235         }
236         return false;
237     }
238
239     private ExecIndexRow getDeferredIndexRowTemplate(ExecRow baseRow,
240                                                     RowLocation baseRowLoc)
241          throws StandardException
242     {
243         ExecIndexRow template;
244
245         template = irg.getIndexRowTemplate();
246
247         irg.getIndexRow(baseRow, baseRowLoc, template, baseRowReadMap);
248
249         return template;
250     }
251
252     /**
253       Position our index scan to 'ourIndexRow'.
254
255       <P>This creates the scan the first time it is called.
256
257       @exception StandardException Thrown on error
258       */

259     private void setScan()
260          throws StandardException
261     {
262         /* Get the SC from the activation if re-using */
263         if (! ownIndexSC)
264         {
265             indexSC = activation.getIndexScanController();
266         }
267         else if (indexSC == null)
268         {
269             RowLocation templateBaseRowLocation = baseCC.newRowLocationTemplate();
270             /* DataDictionary doesn't have compiled info */
271             if (indexSCOCI == null)
272             {
273                 indexSC =
274                     tc.openScan(
275                           indexCID,
276                           false, /* hold */
277                           TransactionController.OPENMODE_FORUPDATE, /* forUpdate */
278                           lockMode,
279                           isolationLevel,
280                           (FormatableBitSet)null, /* all fields */
281                           ourIndexRow.getRowArray(), /* startKeyValue */
282                           ScanController.GE, /* startSearchOp */
283                           null, /* qualifier */
284                           ourIndexRow.getRowArray(), /* stopKeyValue */
285                         ScanController.GT /* stopSearchOp */
286                           );
287             }
288             else
289             {
290                 indexSC =
291                     tc.openCompiledScan(
292                           false, /* hold */
293                           TransactionController.OPENMODE_FORUPDATE, /* forUpdate */
294                           lockMode,
295                           isolationLevel,
296                           (FormatableBitSet)null, /* all fields */
297                           ourIndexRow.getRowArray(), /* startKeyValue */
298                           ScanController.GE, /* startSearchOp */
299                           null, /* qualifier */
300                           ourIndexRow.getRowArray(), /* stopKeyValue */
301                           ScanController.GT, /* stopSearchOp */
302                           indexSCOCI,
303                           indexDCOCI
304                           );
305             }
306         }
307         else
308         {
309             indexSC.reopenScan(
310                                ourIndexRow.getRowArray(), /* startKeyValue */
311                                ScanController.GE, /* startSearchOperator */
312                                null, /* qualifier */
313                                ourIndexRow.getRowArray(), /* stopKeyValue */
314                                ScanController.GT /* stopSearchOperator */
315                                );
316         }
317     }
318
319     /**
320       Close our index Conglomerate Controller
321       */

322     private void closeIndexCC()
323         throws StandardException
324     {
325         if (indexCC != null)
326             indexCC.close();
327         indexCC = null;
328     }
329
330     /**
331       Close our index ScanController.
332       */

333     private void closeIndexSC()
334         throws StandardException
335     {
336         /* Only consider closing index SC if we own it. */
337         if (ownIndexSC && indexSC != null)
338         {
339             indexSC.close();
340             indexSC = null;
341         }
342     }
343
344     /**
345       Delete a row from our index. This assumes our index ScanController
346       is positioned before the row by setScan if we own the SC, otherwise
347       it is positioned on the row by the underlying index scan.
348       
349       <P>This verifies the row exists and is unique.
350       
351       @exception StandardException Thrown on error
352       */

353     private void doDelete()
354          throws StandardException
355     {
356         if (ownIndexSC)
357         {
358             if (! indexSC.next())
359             {
360                 // This means that the entry for the index does not exist, this
361
// is a serious problem with the index. Past fixed problems
362
// like track 3703 can leave db's in the field with this problem
363
// even though the bug in the code which caused it has long
364
// since been fixed. Then the problem can surface months later
365
// when the customer attempts to upgrade. By "ignoring" the
366
// missing row here the problem is automatically "fixed" and
367
// since the code is trying to delete the row anyway it doesn't
368
// seem like such a bad idea. It also then gives a tool to
369
// support to be able to fix some system catalog problems where
370
// they can delete the base rows by dropping the system objects
371
// like stored statements.
372

373                 if (SanityManager.DEBUG)
374                     SanityManager.THROWASSERT(
375                         "Index row "+RowUtil.toString(ourIndexRow)+
376                         " not found in conglomerateid " + indexCID +
377                         "Current scan = " + indexSC);
378
379                 Object JavaDoc[] args = new Object JavaDoc[2];
380                 args[0] = ourIndexRow.getRowArray()[ourIndexRow.getRowArray().length - 1];
381                 args[1] = new Long JavaDoc(indexCID);
382
383                 Monitor.getStream().println(MessageService.getCompleteMessage(
384                     SQLState.LANG_IGNORE_MISSING_INDEX_ROW_DURING_DELETE,
385                     args));
386
387                 // just return indicating the row has been deleted.
388
return;
389             }
390         }
391
392         indexSC.delete();
393     }
394
395     /**
396       Insert a row into our indes.
397       
398       <P>This opens our index ConglomeratController the first time it
399       is called.
400       
401       @exception StandardException Thrown on error
402       */

403     private void doInsert()
404          throws StandardException
405     {
406         insertAndCheckDups(ourIndexRow);
407     }
408
409     /**
410       Insert a row into the temporary conglomerate
411       
412       <P>This opens our deferred ConglomeratController the first time it
413       is called.
414       
415       @exception StandardException Thrown on error
416       */

417     private void doDeferredInsert()
418          throws StandardException
419     {
420         if (rowHolder == null)
421         {
422             Properties JavaDoc properties = new Properties JavaDoc();
423
424             // Get the properties on the index
425
openIndexCC().getInternalTablePropertySet(properties);
426
427             /*
428             ** Create our row holder. it is ok to skip passing
429             ** in the result description because if we don't already
430             ** have a row holder, then we are the only user of the
431             ** row holder (the description is needed when the row
432             ** holder is going to be handed to users for triggers).
433             */

434             rowHolder = new TemporaryRowHolderImpl(activation, properties,
435                                                    (ResultDescription) null);
436         }
437
438         /*
439         ** If the user of the IndexChanger already
440         ** had a row holder, then we don't need to
441         ** bother saving deferred inserts -- they
442         ** have already done so.
443         */

444         if (!rowHolderPassedIn)
445         {
446             rowHolder.insert(ourIndexRow);
447         }
448     }
449
450     /**
451      * Insert the given row into the given conglomerate and check for duplicate
452      * key error.
453      *
454      * @param row The row to insert
455      *
456      * @exception StandardException Thrown on duplicate key error
457      */

458     private void insertAndCheckDups(ExecIndexRow row)
459                 throws StandardException
460     {
461         openIndexCC();
462
463         int insertStatus = indexCC.insert(row.getRowArray());
464
465         if (insertStatus == ConglomerateController.ROWISDUPLICATE)
466         {
467             /*
468             ** We have a duplicate key error.
469             */

470             String JavaDoc indexOrConstraintName = indexName;
471             // now get table name, and constraint name if needed
472
LanguageConnectionContext lcc =
473                             activation.getLanguageConnectionContext();
474             DataDictionary dd = lcc.getDataDictionary();
475             //get the descriptors
476
ConglomerateDescriptor cd = dd.getConglomerateDescriptor(indexCID);
477
478             UUID tableID = cd.getTableID();
479             TableDescriptor td = dd.getTableDescriptor(tableID);
480             String JavaDoc tableName = td.getName();
481             
482             if (indexOrConstraintName == null) // no index name passed in
483
{
484                 ConstraintDescriptor conDesc = dd.getConstraintDescriptor(td,
485                                                                       cd.getUUID());
486                 indexOrConstraintName = conDesc.getConstraintName();
487             }
488
489             StandardException se =
490                 StandardException.newException(
491                 SQLState.LANG_DUPLICATE_KEY_CONSTRAINT, indexOrConstraintName, tableName);
492             throw se;
493         }
494         if (SanityManager.DEBUG)
495         {
496             if (insertStatus != 0)
497             {
498                 SanityManager.THROWASSERT("Unknown insert status " + insertStatus);
499             }
500         }
501     }
502
503
504     /**
505      * Open the ConglomerateController for this index if it isn't open yet.
506      *
507      * @return The ConglomerateController for this index.
508      *
509      * @exception StandardException Thrown on duplicate key error
510      */

511     private ConglomerateController openIndexCC()
512         throws StandardException
513     {
514         if (indexCC == null)
515         {
516             /* DataDictionary doesn't have compiled info */
517             if (indexSCOCI == null)
518             {
519                 indexCC =
520                     tc.openConglomerate(
521                         indexCID,
522                         false,
523                         (TransactionController.OPENMODE_FORUPDATE |
524                          TransactionController.OPENMODE_BASEROW_INSERT_LOCKED),
525                         lockMode,
526                         isolationLevel);
527             }
528             else
529             {
530                 indexCC =
531                     tc.openCompiledConglomerate(
532                         false,
533                         (TransactionController.OPENMODE_FORUPDATE |
534                          TransactionController.OPENMODE_BASEROW_INSERT_LOCKED),
535                         lockMode,
536                         isolationLevel,
537                         indexSCOCI,
538                         indexDCOCI);
539             }
540         }
541
542         return indexCC;
543     }
544
545     /**
546       Open this IndexChanger.
547
548       @exception StandardException Thrown on error
549       */

550     public void open()
551          throws StandardException
552     {
553     }
554
555     /**
556       Perform index maintenance to support a delete of a base table row.
557
558       @param baseRow the base table row.
559       @param baseRowLocation the base table row's location.
560       @exception StandardException Thrown on error
561       */

562     public void delete(ExecRow baseRow,
563                        RowLocation baseRowLocation)
564          throws StandardException
565     {
566         setOurIndexRow(baseRow, baseRowLocation);
567         setScan();
568         doDelete();
569     }
570
571     /**
572       Perform index maintenance to support an update of a base table row.
573
574       @param oldBaseRow the old image of the base table row.
575       @param newBaseRow the new image of the base table row.
576       @param baseRowLocation the base table row's location.
577
578       @exception StandardException Thrown on error
579       */

580     public void update(ExecRow oldBaseRow,
581                        ExecRow newBaseRow,
582                        RowLocation baseRowLocation
583                        )
584          throws StandardException
585     {
586         setOurIndexRow(oldBaseRow, baseRowLocation);
587         setOurUpdatedIndexRow(newBaseRow, baseRowLocation);
588
589         /* We skip the update in the degenerate case
590          * where none of the key columns changed.
591          * (From an actual customer case.)
592          */

593         if (indexRowChanged())
594         {
595             setScan();
596             doDelete();
597             insertForUpdate(newBaseRow, baseRowLocation);
598         }
599     }
600
601     /**
602       Perform index maintenance to support an insert of a base table row.
603
604       @param newRow the base table row.
605       @param baseRowLocation the base table row's location.
606
607       @exception StandardException Thrown on error
608       */

609     public void insert(ExecRow newRow, RowLocation baseRowLocation)
610          throws StandardException
611     {
612         setOurIndexRow(newRow, baseRowLocation);
613         doInsert();
614     }
615
616     /**
617       If we're updating a unique index, the inserts have to be
618       deferred. This is to avoid uniqueness violations that are only
619       temporary. If we do all the deletes first, only "true" uniqueness
620       violations can happen. We do this here, rather than in open(),
621       because this is the only operation that requires deferred inserts,
622       and we only want to create the conglomerate if necessary.
623
624       @param newRow the base table row.
625       @param baseRowLocation the base table row's location.
626
627       @exception StandardException Thrown on error
628     */

629     void insertForUpdate(ExecRow newRow, RowLocation baseRowLocation)
630          throws StandardException
631     {
632         setOurIndexRow(newRow, baseRowLocation);
633
634         if (irg.isUnique())
635         {
636             doDeferredInsert();
637         }
638         else
639         {
640             doInsert();
641         }
642     }
643
644     /**
645       Finish doing the changes for this index. This is intended for deferred
646       inserts for unique indexes. It has no effect unless we are doing an
647       update of a unique index.
648
649       @exception StandardException Thrown on error
650      */

651     public void finish()
652         throws StandardException
653     {
654         ExecRow deferredRow;
655         ExecIndexRow deferredIndexRow = new IndexRow();
656
657         /* Deferred processing only necessary for unique indexes */
658         if (rowHolder != null)
659         {
660             CursorResultSet rs = rowHolder.getResultSet();
661             try
662             {
663                 rs.open();
664                 while ((deferredRow = rs.getNextRow()) != null)
665                 {
666                     if (SanityManager.DEBUG)
667                     {
668                         if (!(deferredRow instanceof ExecIndexRow))
669                         {
670                             SanityManager.THROWASSERT("deferredRow isn't an instance "+
671                                 "of ExecIndexRow as expected. "+
672                                 "It is an "+deferredRow.getClass().getName());
673                         }
674                     }
675                     insertAndCheckDups((ExecIndexRow)deferredRow);
676                 }
677             }
678             finally
679             {
680                 rs.close();
681
682                 /*
683                 ** If row holder was passed in, let the
684                 ** client of this method clean it up.
685                 */

686                 if (!rowHolderPassedIn)
687                 {
688                     rowHolder.close();
689                 }
690             }
691         }
692     }
693
694     /**
695       Close this IndexChanger.
696
697       @exception StandardException Thrown on error
698       */

699     public void close()
700         throws StandardException
701     {
702         closeIndexCC();
703         closeIndexSC();
704         if (rowHolder != null && !rowHolderPassedIn)
705         {
706             rowHolder.close();
707         }
708         baseCC = null;
709     }
710 }
711
Popular Tags