KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.execute.UpdateResultSet
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.loader.GeneratedMethod;
25 import org.apache.derby.iapi.services.monitor.Monitor;
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
28 import org.apache.derby.iapi.services.stream.InfoStreams;
29 import org.apache.derby.iapi.services.io.StreamStorable;
30 import org.apache.derby.iapi.error.StandardException;
31 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
32 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
33 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
34 import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
35 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
36 import org.apache.derby.iapi.types.BooleanDataValue;
37 import org.apache.derby.iapi.types.DataValueDescriptor;
38 import org.apache.derby.iapi.types.RowLocation;
39 import org.apache.derby.iapi.sql.execute.ConstantAction;
40 import org.apache.derby.iapi.sql.execute.CursorResultSet;
41 import org.apache.derby.iapi.sql.execute.ExecRow;
42 import org.apache.derby.iapi.sql.execute.ExecutionContext;
43 import org.apache.derby.iapi.sql.execute.RowChanger;
44 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
45
46 import org.apache.derby.iapi.types.DataValueDescriptor;
47 import org.apache.derby.iapi.sql.Activation;
48 import org.apache.derby.iapi.sql.ResultDescription;
49 import org.apache.derby.iapi.sql.ResultSet;
50
51 import org.apache.derby.iapi.store.access.ConglomerateController;
52 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
53 import org.apache.derby.iapi.store.access.ScanController;
54 import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
55 import org.apache.derby.iapi.store.access.TransactionController;
56
57 import org.apache.derby.iapi.reference.SQLState;
58
59 import org.apache.derby.iapi.db.TriggerExecutionContext;
60 import org.apache.derby.iapi.services.io.FormatableBitSet;
61 import java.util.Properties JavaDoc;
62 import java.util.Hashtable JavaDoc;
63
64 /**
65  * Update the rows from the specified
66  * base table. This will cause constraints to be checked
67  * and triggers to be executed based on the c's and t's
68  * compiled into the update plan.
69  *
70  * @author ames
71  */

72 class UpdateResultSet extends DMLWriteResultSet
73 {
74     private TransactionController tc;
75     private ExecRow newBaseRow;
76     private ExecRow row;
77     private ExecRow deferredSparseRow;
78     UpdateConstantAction constants;
79     
80     private ResultDescription resultDescription;
81     private NoPutResultSet source;
82     NoPutResultSet savedSource;
83     private RowChanger rowChanger;
84
85     protected ConglomerateController deferredBaseCC;
86
87     protected long[] deferredUniqueCIDs;
88     protected boolean[] deferredUniqueCreated;
89     protected ConglomerateController deferredUniqueCC[];
90     protected ScanController[] deferredUniqueScans;
91
92     private TemporaryRowHolderImpl deletedRowHolder;
93     private TemporaryRowHolderImpl insertedRowHolder;
94
95     // cached
96
private RISetChecker riChecker;
97     private TriggerInfo triggerInfo;
98     private TriggerEventActivator triggerActivator;
99     private boolean updatingReferencedKey;
100     private boolean updatingForeignKey;
101     private int numOpens;
102     private long heapConglom;
103     private FKInfo[] fkInfoArray;
104     private FormatableBitSet baseRowReadList;
105     private GeneratedMethod checkGM;
106     private int resultWidth;
107     private int numberOfBaseColumns;
108     private ExecRow deferredTempRow;
109     private ExecRow deferredBaseRow;
110     private ExecRow oldDeletedRow;
111     private ResultDescription triggerResultDescription;
112
113     int lockMode;
114     boolean deferred;
115     boolean beforeUpdateCopyRequired = false;
116
117     /**
118      * Returns the description of the updated rows.
119      * REVISIT: Do we want this to return NULL instead?
120      */

121     public ResultDescription getResultDescription()
122     {
123         return resultDescription;
124     }
125
126     /*
127      * class interface
128      *
129      */

130     /**
131      * @param source update rows come from source
132      * @param checkGM Generated method for enforcing check constraints
133      * @exception StandardException thrown on error
134      */

135     UpdateResultSet(NoPutResultSet source,
136                            GeneratedMethod checkGM,
137                            Activation activation)
138       throws StandardException
139     {
140         this(source, checkGM , activation, activation.getConstantAction(),null);
141     }
142
143     /*
144      * class interface
145      *
146      */

147     /**
148      * @param source update rows come from source
149      * @param checkGM Generated method for enforcing check constraints
150      * @param activation Activation
151      * @param constantActionItem id of the update constant action saved objec
152      * @param rsdItem id of the Result Description saved object
153      * @exception StandardException thrown on error
154      */

155     UpdateResultSet(NoPutResultSet source,
156                            GeneratedMethod checkGM,
157                            Activation activation,
158                            int constantActionItem,
159                            int rsdItem)
160       throws StandardException
161     {
162         this(source, checkGM , activation,
163               ((ConstantAction)activation.getPreparedStatement().getSavedObject(constantActionItem)),
164              (ResultDescription) activation.getPreparedStatement().getSavedObject(rsdItem));
165     
166         // In case of referential action update, we do a deferred updates
167
deferred = true;
168     }
169
170
171     /*
172      * class interface
173      *
174      */

175     /**
176      * @param source update rows come from source
177      * @param checkGM Generated method for enforcing check constraints
178      * @exception StandardException thrown on error
179      */

180     UpdateResultSet(NoPutResultSet source,
181                            GeneratedMethod checkGM,
182                            Activation activation,
183                            ConstantAction passedInConstantAction,
184                            ResultDescription passedInRsd)
185       throws StandardException
186     {
187         super(activation, passedInConstantAction);
188
189         // Get the current transaction controller
190
tc = activation.getTransactionController();
191         this.source = source;
192         this.checkGM = checkGM;
193
194         constants = (UpdateConstantAction) constantAction;
195         fkInfoArray = constants.getFKInfo( lcc.getExecutionContext() );
196         triggerInfo = constants.getTriggerInfo(lcc.getExecutionContext());
197
198         heapConglom = constants.conglomId;
199
200         baseRowReadList = constants.getBaseRowReadList();
201         if(passedInRsd ==null)
202             resultDescription = source.getResultDescription();
203         else
204             resultDescription = passedInRsd;
205         /*
206         ** We NEED a result description when we are going to
207         ** to have to kick off a trigger. In a replicated environment
208         ** we don't get a result description when we are replaying
209         ** source xacts on the target, which should never be the
210         ** case for an UpdateResultSet.
211         */

212         if (SanityManager.DEBUG)
213         {
214             if (resultDescription == null)
215             {
216                 SanityManager.ASSERT(triggerInfo == null, "triggers need a result description to pass to result sets given to users");
217             }
218         }
219
220         if (fkInfoArray != null)
221         {
222             for (int i = 0; i < fkInfoArray.length; i++)
223             {
224                 if (fkInfoArray[i].type == FKInfo.REFERENCED_KEY)
225                 {
226                     updatingReferencedKey = true;
227                     if (SanityManager.DEBUG)
228                     {
229                         SanityManager.ASSERT(constants.deferred, "updating referenced key but update not deferred, wuzzup?");
230                     }
231                 }
232                 else
233                 {
234                     updatingForeignKey = true;
235                 }
236             }
237         }
238
239         /* Get the # of columns in the ResultSet */
240         resultWidth = resultDescription.getColumnCount();
241         
242         /*
243         ** Calculate the # of columns in the base table. The result set
244         ** contains the before columns, the after columns, and the RowLocation,
245         ** so the number of base columns is half of the number of result set
246         ** columns, after subtracting one for the row location column.
247         */

248         numberOfBaseColumns = (resultWidth - 1) / 2;
249         
250         /* Get the new base row */
251         newBaseRow = RowUtil.getEmptyValueRow(numberOfBaseColumns, lcc);
252
253         /* decode lock mode */
254         lockMode = decodeLockMode(lcc, constants.lockMode);
255         deferred = constants.deferred;
256         
257         //update can be marked for deferred mode because the scan is being done
258
//using index. But it is not necesary to keep the before copy
259
//of the row in the temporary row holder (deletedRowHolder) unless
260
//there are RI constraint or Triggers.(beetle:5301)
261
if(triggerInfo != null || fkInfoArray !=null){
262             beforeUpdateCopyRequired = true;
263         }
264         
265     }
266     /**
267         @exception StandardException Standard Cloudscape error policy
268     */

269     public void open() throws StandardException
270     {
271
272         setup();
273         collectAffectedRows();
274
275         /*
276         ** If this is a deferred update, read the new rows and RowLocations
277         ** from the temporary conglomerate and update the base table using
278         ** the RowChanger.
279         */

280         if (deferred)
281         {
282
283             runChecker(true); //check for only RESTRICT referential action rule violations
284
fireBeforeTriggers();
285             updateDeferredRows();
286             /* Apply deferred inserts to unique indexes */
287             rowChanger.finish();
288             runChecker(false); //check for all violations
289
fireAfterTriggers();
290
291         }
292         else{
293         /* Apply deferred inserts to unique indexes */
294         rowChanger.finish();
295         }
296
297         cleanUp();
298     }
299
300
301     /**
302         @exception StandardException Standard Cloudscape error policy
303     */

304     void setup() throws StandardException
305     {
306         boolean firstOpen = (rowChanger == null);
307
308         rowCount = 0;
309         
310         /* Cache query plan text for source, before it gets blown away */
311         if (lcc.getRunTimeStatisticsMode())
312         {
313             /* savedSource nulled after run time statistics generation */
314             savedSource = source;
315         }
316
317         /* Get or re-use the row changer.
318          * NOTE: We need to set ourself as the top result set
319          * if this is not the 1st execution. (Done in constructor
320          * for 1st execution.)
321          */

322         if (firstOpen)
323         {
324             rowChanger = lcc.getLanguageConnectionFactory().getExecutionFactory()
325                              .getRowChanger( heapConglom,
326                                          constants.heapSCOCI,
327                                          heapDCOCI,
328                                          constants.irgs,
329                                          constants.indexCIDS,
330                                          constants.indexSCOCIs,
331                                          indexDCOCIs,
332                                          constants.numColumns,
333                                          tc,
334                                          constants.changedColumnIds,
335                                          constants.getBaseRowReadList(),
336                                          constants.getBaseRowReadMap(),
337                                          constants.getStreamStorableHeapColIds(),
338                                          activation);
339             rowChanger.setIndexNames(constants.indexNames);
340         }
341         else
342         {
343             lcc.getStatementContext().setTopResultSet(this, subqueryTrackingArray);
344         }
345
346
347         /* Open the RowChanger before the source ResultSet so that
348          * the store will see the RowChanger's lock as a covering lock
349          * if it is a table lock.
350          */

351         rowChanger.open(lockMode);
352
353         if (numOpens++ == 0)
354         {
355             source.openCore();
356         }
357         else
358         {
359             source.reopenCore();
360         }
361
362         /* The source does not know whether or not we are doing a
363          * deferred mode update. If we are, then we must clear the
364          * index scan info from the activation so that the row changer
365          * does not re-use that information (which won't be valid for
366          * a deferred mode update).
367          */

368         if (deferred)
369         {
370             activation.clearIndexScanInfo();
371         }
372
373         if (fkInfoArray != null)
374         {
375             if (riChecker == null)
376             {
377                 riChecker = new RISetChecker(tc, fkInfoArray);
378             }
379             else
380             {
381                 riChecker.reopen();
382             }
383         }
384
385         if (deferred)
386         {
387             /* Allocate the temporary rows and get result description
388              * if this is the 1st time that we are executing.
389              */

390             if (firstOpen)
391             {
392                 deferredTempRow = RowUtil.getEmptyValueRow(numberOfBaseColumns+1, lcc);
393                 oldDeletedRow = RowUtil.getEmptyValueRow(numberOfBaseColumns, lcc);
394                 triggerResultDescription = (resultDescription != null) ?
395                                     resultDescription.truncateColumns(numberOfBaseColumns+1) :
396                                     null;
397             }
398
399             Properties JavaDoc properties = new Properties JavaDoc();
400
401             // Get the properties on the heap
402
rowChanger.getHeapConglomerateController().getInternalTablePropertySet(properties);
403             if(beforeUpdateCopyRequired){
404                 deletedRowHolder =
405                     new TemporaryRowHolderImpl(activation, properties,
406                                                triggerResultDescription);
407             }
408             insertedRowHolder =
409                 new TemporaryRowHolderImpl(activation, properties,
410                                            triggerResultDescription);
411
412             rowChanger.setRowHolder(insertedRowHolder);
413         }
414
415     }
416
417     /* Following 2 methods are for checking and make sure we don't have one un-objectified stream
418      * to be inserted into 2 temp table rows for deferred update. Otherwise it would cause problem
419      * when writing to disk using the stream a second time. In other cases we don't want to
420      * unnecessarily objectify the stream. beetle 4896.
421      */

422     private FormatableBitSet checkStreamCols()
423     {
424         DataValueDescriptor[] cols = row.getRowArray();
425         FormatableBitSet streamCols = null;
426         for (int i = 0; i < numberOfBaseColumns; i++)
427         {
428             if (cols[i+numberOfBaseColumns] instanceof StreamStorable) //check new values
429
{
430                 if (streamCols == null) streamCols = new FormatableBitSet(numberOfBaseColumns);
431                 streamCols.set(i);
432             }
433         }
434         return streamCols;
435     }
436
437     private void objectifyStream(ExecRow tempRow, FormatableBitSet streamCols) throws StandardException
438     {
439         DataValueDescriptor[] cols = tempRow.getRowArray();
440         for (int i = 0; i < numberOfBaseColumns; i++)
441         {
442             if (cols[i] != null && streamCols.get(i))
443                 ((StreamStorable)cols[i]).loadStream();
444         }
445     }
446
447     public boolean collectAffectedRows() throws StandardException
448     {
449
450         boolean rowsFound = false;
451         row = getNextRowCore(source);
452         if (row!=null)
453             rowsFound = true;
454         else
455         {
456             activation.addWarning(
457                         StandardException.newWarning(
458                             SQLState.LANG_NO_ROW_FOUND));
459         }
460
461         //beetle 3865, update cursor use index.
462
TableScanResultSet tableScan = (TableScanResultSet) activation.getForUpdateIndexScan();
463         boolean notifyCursor = ((tableScan != null) && ! tableScan.sourceDrained);
464         boolean checkStream = (deferred && rowsFound && ! constants.singleRowSource);
465         FormatableBitSet streamCols = (checkStream ? checkStreamCols() : null);
466         checkStream = (streamCols != null);
467
468         while ( row != null )
469         {
470
471             /* By convention, the last column in the result set for an
472              * update contains a SQLRef containing the RowLocation of
473              * the row to be updated.
474              */

475
476             /*
477             ** If we're doing deferred update, write the new row and row
478             ** location to the temporary conglomerate. If we're not doing
479             ** deferred update, update the permanent conglomerates now
480             ** using the RowChanger.
481             */

482             if (deferred)
483             {
484                 /*
485                 ** If we have a before trigger, we must evaluate the
486                 ** check constraint after we have executed the trigger.
487                 ** Note that we have compiled checkGM accordingly (to
488                 ** handle the different row shape if we are evaluating
489                 ** against the input result set or a temporary row holder
490                 ** result set).
491                 */

492                 if (triggerInfo == null)
493                 {
494                     evaluateCheckConstraints( checkGM, activation );
495                 }
496
497                 /*
498                 ** We are going to only save off the updated
499                 ** columns and the RID. For a trigger, all columns
500                 ** were marked as needed so we'll copy them all.
501                 */

502                 RowUtil.copyRefColumns(deferredTempRow,
503                                             row,
504                                             numberOfBaseColumns,
505                                             numberOfBaseColumns + 1);
506                 if (checkStream)
507                     objectifyStream(deferredTempRow, streamCols);
508
509                 insertedRowHolder.insert(deferredTempRow);
510
511                 /*
512                 ** Grab a copy of the row to delete. We are
513                 ** going to use this for deferred RI checks.
514                 */

515                 if(beforeUpdateCopyRequired)
516                 {
517                     RowUtil.copyRefColumns(oldDeletedRow,
518                                            row,
519                                            numberOfBaseColumns);
520
521                     deletedRowHolder.insert(oldDeletedRow);
522                 }
523
524                 /*
525                 ** If we haven't already, lets get a template to
526                 ** use as a template for our rescan of the base table.
527                 ** Do this now while we have a real row to use
528                 ** as a copy.
529                 **
530                 ** There is one less column in the base row than
531                 ** there is in source row, because the base row
532                 ** doesn't contain the row location.
533                 */

534                 if (deferredBaseRow == null)
535                 {
536                     deferredBaseRow = RowUtil.getEmptyValueRow(numberOfBaseColumns, lcc);
537             
538                     RowUtil.copyCloneColumns(deferredBaseRow, row,
539                                             numberOfBaseColumns);
540
541                     /*
542                     ** While we're here, let's also create a sparse row for
543                     ** fetching from the store.
544                     */

545                     deferredSparseRow = makeDeferredSparseRow(deferredBaseRow,
546                                                                 baseRowReadList,
547                                                                 lcc);
548                 }
549             }
550             else
551             {
552                 evaluateCheckConstraints( checkGM, activation );
553
554                 /* Get the RowLocation to update
555                 * NOTE - Column #s in the Row are 1 based.
556                 */

557                 RowLocation baseRowLocation = (RowLocation)
558                     (row.getColumn(resultWidth)).getObject();
559
560                 RowUtil.copyRefColumns(newBaseRow,
561                                         row,
562                                         numberOfBaseColumns,
563                                         numberOfBaseColumns);
564
565                 if (riChecker != null)
566                 {
567                     /*
568                     ** Make sure all foreign keys in the new row
569                     ** are maintained. Note that we don't bother
570                     ** checking primary/unique keys that are referenced
571                     ** here. The reason is that if we are updating
572                     ** a referenced key, we'll be updating in deferred
573                     ** mode, so we wont get here.
574                     */

575                     riChecker.doFKCheck(newBaseRow);
576                 }
577
578                 source.updateRow(newBaseRow);
579                 rowChanger.updateRow(row,newBaseRow,baseRowLocation);
580
581                 //beetle 3865, update cursor use index.
582
if (notifyCursor)
583                     notifyForUpdateCursor(row.getRowArray(),newBaseRow.getRowArray(),baseRowLocation,
584                                             tableScan);
585             }
586
587             rowCount++;
588
589             // No need to do a next on a single row source
590
if (constants.singleRowSource)
591             {
592                 row = null;
593             }
594             else
595             {
596                 row = getNextRowCore(source);
597             }
598         }
599
600         return rowsFound;
601     }
602
603     /* beetle 3865, updateable cursor use index. If the row we are updating has new value that
604      * falls into the direction of the index scan of the cursor, we save this rid into a hash table
605      * (for fast search), so that when the cursor hits it again, it knows to skip it. When we get
606      * to a point that the hash table is full, we scan forward the cursor until one of two things
607      * happen: (1) we hit a record whose rid is in the hash table (we went through it already, so
608      * skip it), we remove it from hash table, so that we can continue to use hash table. OR, (2) the scan
609      * forward hit the end. If (2) happens, we can de-reference the hash table to make it available
610      * for garbage collection. We save the future row id's in a virtual mem heap. In any case,
611      * next read will use a row id that we saved.
612      */

613     private void notifyForUpdateCursor(DataValueDescriptor[] row, DataValueDescriptor[] newBaseRow,
614                                         RowLocation rl, TableScanResultSet tableScan)
615         throws StandardException
616     {
617         int[] indexCols = tableScan.indexCols;
618         int[] changedCols = constants.changedColumnIds;
619         boolean placedForward = false, ascending, decided = false, overlap = false;
620         int basePos, k;
621         /* first of all, we see if there's overlap between changed column ids and index key
622          * columns. If so, we see if the new update value falls into the future range of the
623          * index scan, if so, we need to save it in hash table.
624          */

625         for (int i = 0; i < indexCols.length; i++)
626         {
627             basePos = indexCols[i];
628             if (basePos > 0)
629                 ascending = true;
630             else
631             {
632                 ascending = false;
633                 basePos = -basePos;
634             }
635             for (int j = 0; j < changedCols.length; j++)
636             {
637                 if (basePos == changedCols[j])
638                 {
639                     decided = true; //we pretty much decided if new row falls in front
640
//of the cursor or behind
641
/* the row and newBaseRow we get are compact base row that only have
642                      * referenced columns. Our "basePos" is index in sparse heap row, so
643                      * we need the BaseRowReadMap to map into the compact row.
644                      */

645                     int[] map = constants.getBaseRowReadMap();
646                     if (map == null)
647                         k = basePos - 1;
648                     else
649                         k = map[basePos - 1];
650
651                     DataValueDescriptor key;
652                     /* We need to compare with saved most-forward cursor scan key if we
653                      * are reading records from the saved RowLocation temp table (instead
654                      * of the old column value) because we only care if new update value
655                      * jumps forward the most-forward scan key.
656                      */

657                     if (tableScan.compareToLastKey)
658                         key = tableScan.lastCursorKey.getColumn(i + 1);
659                     else
660                         key = row[k];
661
662                     /* Starting from the first index key column forward, we see if the direction
663                      * of the update change is consistent with the direction of index scan.
664                      * If so, we save it in hash table.
665                      */

666                     if ((ascending && key.greaterThan(newBaseRow[k], key).equals(true)) ||
667                         (!ascending && key.lessThan(newBaseRow[k], key).equals(true)))
668                         placedForward = true;
669                     else if (key.equals(newBaseRow[k], key).equals(true))
670                     {
671                         decided = false;
672                         overlap = true;
673                     }
674                     break;
675                 }
676             }
677             if (decided) // already decided if new row falls in front or behind
678
break;
679         }
680         /* If index row gets updated but key value didn't actually change, we still
681          * put it in hash table because it can either fall in front or behind. This
682          * can happen if the update explicitly sets a value, but same as old.
683          */

684         if (overlap && !decided)
685             placedForward = true;
686
687         if (placedForward) // add it to hash table
688
{
689             /* determining initial capacity of hash table from a few factors:
690              * (1) user specified MAX_MEMORY_PER_TABLE property, (2) min value 100
691              * (3) optimizer estimated row count. We want to avoid re-hashing if
692              * possible, for performance reason, yet don't waste space. If initial
693              * capacity is greater than max size divided by load factor, no rehash
694              * is ever needed.
695              */

696             int maxCapacity = lcc.getOptimizerFactory().getMaxMemoryPerTable() / 16;
697             if (maxCapacity < 100)
698                 maxCapacity = 100;
699
700             if (tableScan.past2FutureTbl == null)
701             {
702                 double rowCount = tableScan.getEstimatedRowCount();
703                 int initCapacity = 32 * 1024;
704                 if (rowCount > 0.0)
705                 {
706                     rowCount = rowCount / 0.75 + 1.0; // load factor
707
if (rowCount < initCapacity)
708                         initCapacity = (int) rowCount;
709                 }
710                 if (maxCapacity < initCapacity)
711                     initCapacity = maxCapacity;
712
713                 tableScan.past2FutureTbl = new Hashtable JavaDoc(initCapacity);
714             }
715
716             Hashtable JavaDoc past2FutureTbl = tableScan.past2FutureTbl;
717             /* If hash table is not full, we add it in. The key of the hash entry
718              * is the string value of the RowLocation. If the hash table is full,
719              * as the comments above this function say, we scan forward.
720              *
721              * Need to save a clone because when we get cached currentRow, "rl" shares the
722              * same reference, so is changed at the same time.
723              */

724             RowLocation updatedRL = (RowLocation) rl.getClone();
725
726             if (past2FutureTbl.size() < maxCapacity)
727                 past2FutureTbl.put(updatedRL, updatedRL);
728             else
729             {
730                 tableScan.skipFutureRowHolder = true;
731                 ExecRow rlRow = new ValueRow(1);
732
733                 for (;;)
734                 {
735                     ExecRow aRow = tableScan.getNextRowCore();
736                     if (aRow == null)
737                     {
738                         tableScan.sourceDrained = true;
739                         tableScan.past2FutureTbl = null; // de-reference for garbage coll.
740
break;
741                     }
742                     RowLocation rowLoc = (RowLocation) aRow.getColumn(aRow.nColumns());
743
744                     if (updatedRL.equals(rowLoc)) //this row we are updating jumped forward
745
{
746                         saveLastCusorKey(tableScan, aRow);
747                         break; // don't need to worry about adding this row to hash any more
748
}
749
750                     if (tableScan.futureForUpdateRows == null)
751                     {
752                         // virtual memory heap. In-memory part size 100. With the co-operation
753
// of hash table and in-memory part of heap (hash table shrinks while
754
// in-memory heap grows), hopefully we never spill temp table to disk.
755

756                         tableScan.futureForUpdateRows = new TemporaryRowHolderImpl
757                             (activation, null, null, 100, false, true);
758                     }
759
760                     rlRow.setColumn(1, rowLoc);
761                     tableScan.futureForUpdateRows.insert(rlRow);
762                     if (past2FutureTbl.size() < maxCapacity) //we got space in the hash table now, stop!
763
{
764                         past2FutureTbl.put(updatedRL, updatedRL);
765                         saveLastCusorKey(tableScan, aRow);
766                         break;
767                     }
768                 }
769                 tableScan.skipFutureRowHolder = false;
770             }
771         }
772     }
773
774     private void saveLastCusorKey(TableScanResultSet tableScan, ExecRow aRow) throws StandardException
775     {
776         /* We save the most-forward cursor scan key where we are stopping, so
777          * that next time when we decide if we need to put an updated row id into
778          * hash table, we can compare with this key. This is an optimization on
779          * memory usage of the hash table, otherwise it may be "leaking".
780          */

781         if (tableScan.lastCursorKey == null)
782             tableScan.lastCursorKey = new ValueRow(aRow.nColumns() - 1);
783         for (int i = 1; i <= tableScan.lastCursorKey.nColumns(); i++)
784         {
785             DataValueDescriptor aCol = aRow.getColumn(i);
786             if (aCol != null)
787                 tableScan.lastCursorKey.setColumn(i, aCol.getClone());
788         }
789     }
790
791     void fireBeforeTriggers() throws StandardException
792     {
793         if (deferred)
794         {
795             if (triggerInfo != null)
796             {
797                 if (triggerActivator == null)
798                 {
799                 triggerActivator = new TriggerEventActivator(lcc,
800                                             tc,
801                                             constants.targetUUID,
802                                             triggerInfo,
803                                             TriggerExecutionContext.UPDATE_EVENT,
804                                             activation, null);
805                 }
806                 else
807                 {
808                     triggerActivator.reopen();
809                 }
810
811                 // fire BEFORE trigger, do this before checking constraints
812
triggerActivator.notifyEvent(TriggerEvents.BEFORE_UPDATE,
813                                                 deletedRowHolder.getResultSet(),
814                                                 insertedRowHolder.getResultSet());
815
816             }
817         }
818     }
819
820     void fireAfterTriggers() throws StandardException
821     {
822         if (deferred)
823         {
824             if (triggerActivator != null)
825             {
826                 triggerActivator.notifyEvent(TriggerEvents.AFTER_UPDATE,
827                                         deletedRowHolder.getResultSet(),
828                                         insertedRowHolder.getResultSet());
829             }
830         }
831     }
832
833
834
835     void updateDeferredRows() throws StandardException
836     {
837         if (deferred)
838         {
839             // we already have everything locked
840
deferredBaseCC =
841                 tc.openCompiledConglomerate(
842                     false,
843                     tc.OPENMODE_FORUPDATE|tc.OPENMODE_SECONDARY_LOCKED,
844                     lockMode,
845                     TransactionController.ISOLATION_SERIALIZABLE,
846                     constants.heapSCOCI,
847                     heapDCOCI);
848             
849             CursorResultSet rs = insertedRowHolder.getResultSet();
850             try
851             {
852                 /*
853                 ** We need to do a fetch doing a partial row
854                 ** read. We need to shift our 1-based bit
855                 ** set to a zero based bit set like the store
856                 ** expects.
857                 */

858                 FormatableBitSet readBitSet = RowUtil.shift(baseRowReadList, 1);
859                 ExecRow deferredTempRow2;
860
861                 rs.open();
862                 while ((deferredTempRow2 = rs.getNextRow()) != null)
863                 {
864                     /*
865                     ** Check the constraint now if we have triggers.
866                     ** Otherwise we evaluated them as we read the
867                     ** rows in from the source.
868                     */

869                     if (triggerInfo != null)
870                     {
871                         source.setCurrentRow(deferredTempRow);
872                         evaluateCheckConstraints(checkGM, activation);
873                     }
874
875                     /*
876                     ** The last column is a Ref, which contains a
877                     ** RowLocation.
878                     */

879                     DataValueDescriptor rlColumn = deferredTempRow2.getColumn(numberOfBaseColumns + 1);
880                     RowLocation baseRowLocation =
881                             (RowLocation) (rlColumn).getObject();
882     
883                     /* Get the base row at the given RowLocation */
884                     boolean row_exists =
885                         deferredBaseCC.fetch(
886                             baseRowLocation, deferredSparseRow.getRowArray(),
887                             readBitSet);
888
889                     if (SanityManager.DEBUG)
890                     {
891                         SanityManager.ASSERT(row_exists, "did not find base row in deferred update");
892                     }
893     
894                     /*
895                     ** Copy the columns from the temp row to the base row.
896                     ** The base row has fewer columns than the temp row,
897                     ** because it doesn't contain the row location.
898                     */

899                     RowUtil.copyRefColumns(newBaseRow,
900                                             deferredTempRow2,
901                                             numberOfBaseColumns);
902
903                     rowChanger.updateRow(deferredBaseRow,
904                                         newBaseRow,
905                                         baseRowLocation);
906                     source.updateRow(newBaseRow);
907                 }
908             } finally
909             {
910                 source.clearCurrentRow();
911                 rs.close();
912             }
913         }
914     }
915
916
917     
918     void runChecker(boolean restrictCheckOnly) throws StandardException
919     {
920
921         /*
922         ** For a deferred update, make sure that there
923         ** aren't any primary keys that were removed which
924         ** are referenced.
925         */

926         if (deferred && updatingReferencedKey)
927         {
928             ExecRow deletedRow;
929             CursorResultSet deletedRows;
930
931             /*
932             ** For each referenced key that was modified
933             */

934             for (int i = 0; i < fkInfoArray.length; i++)
935             {
936                 if (fkInfoArray[i].type == FKInfo.FOREIGN_KEY)
937                 {
938                     continue;
939                 }
940
941                 deletedRows = deletedRowHolder.getResultSet();
942                 try
943                 {
944                     /*
945                     ** For each delete row
946                     */

947                     deletedRows.open();
948                     while ((deletedRow = deletedRows.getNextRow()) != null)
949                     {
950                         if (!foundRow(deletedRow,
951                                         fkInfoArray[i].colArray,
952                                         insertedRowHolder))
953                         {
954                             riChecker.doRICheck(i, deletedRow, restrictCheckOnly);
955                         }
956                     }
957                 }
958                 finally
959                 {
960                     deletedRows.close();
961                 }
962             }
963         }
964
965         /*
966         ** For a deferred update, make sure that there
967         ** aren't any foreign keys that were added that
968         ** aren't referenced.
969         */

970         if (deferred && updatingForeignKey)
971         {
972             ExecRow insertedRow;
973             CursorResultSet insertedRows;
974
975             /*
976             ** For each foreign key that was modified
977             */

978             for (int i = 0; i < fkInfoArray.length; i++)
979             {
980                 if (fkInfoArray[i].type == FKInfo.REFERENCED_KEY)
981                 {
982                     continue;
983                 }
984
985                 insertedRows = insertedRowHolder.getResultSet();
986                 try
987                 {
988                     /*
989                     ** For each inserted row
990                     */

991                     insertedRows.open();
992                     while ((insertedRow = insertedRows.getNextRow()) != null)
993                     {
994                         if (!foundRow(insertedRow,
995                                         fkInfoArray[i].colArray,
996                                         deletedRowHolder))
997                         {
998                             riChecker.doRICheck(i, insertedRow, restrictCheckOnly);
999                         }
1000                    }
1001                }
1002                finally
1003                {
1004                    insertedRows.close();
1005                }
1006            }
1007        }
1008
1009    }
1010
1011    public static boolean foundRow
1012    (
1013        ExecRow checkRow,
1014        int[] colsToCheck,
1015        TemporaryRowHolderImpl rowHolder
1016    )
1017        throws StandardException
1018    {
1019        ExecRow scanRow;
1020        boolean foundMatch = false;
1021        Object JavaDoc[] checkRowArray = checkRow.getRowArray();
1022        DataValueDescriptor checkCol;
1023        DataValueDescriptor scanCol;
1024
1025        CursorResultSet rs = rowHolder.getResultSet();
1026        try
1027        {
1028            /*
1029            ** For each inserted row
1030            */

1031            rs.open();
1032            while ((scanRow = rs.getNextRow()) != null)
1033            {
1034                Object JavaDoc[] scanRowArray = scanRow.getRowArray();
1035                int i;
1036                for (i = 0; i < colsToCheck.length; i++)
1037                {
1038                    checkCol = (DataValueDescriptor)checkRowArray[colsToCheck[i]-1];
1039                    scanCol = (DataValueDescriptor)scanRowArray[colsToCheck[i]-1];
1040
1041                    BooleanDataValue result = checkCol.equals(
1042                                            scanCol,
1043                                            checkCol); // result
1044
if (!result.getBoolean())
1045                    {
1046                        break;
1047                    }
1048                }
1049                if (i == colsToCheck.length)
1050                {
1051                    foundMatch = true;
1052                    break;
1053                }
1054            }
1055        }
1056        finally
1057        {
1058            rs.close();
1059        }
1060        return foundMatch;
1061    }
1062
1063
1064    /**
1065     * @see ResultSet#cleanUp
1066     *
1067     * @exception StandardException Thrown on error
1068     */

1069    public void cleanUp() throws StandardException
1070    {
1071        numOpens = 0;
1072
1073        /* Close down the source ResultSet tree */
1074        if (source != null)
1075        {
1076            source.close();
1077            // cache source across open()s
1078
}
1079
1080        if (triggerActivator != null)
1081        {
1082            triggerActivator.cleanup();
1083            // cache triggerActivator across open()s
1084
}
1085
1086        if (rowChanger != null)
1087            rowChanger.close();
1088
1089        if (deferredBaseCC != null)
1090            deferredBaseCC.close();
1091        deferredBaseCC = null;
1092
1093        if (insertedRowHolder != null)
1094        {
1095            insertedRowHolder.close();
1096        }
1097    
1098        if (deletedRowHolder != null)
1099        {
1100            deletedRowHolder.close();
1101        }
1102
1103        if (riChecker != null)
1104        {
1105            riChecker.close();
1106            // cache riChecker across open()s
1107
}
1108
1109        super.close();
1110
1111        endTime = getCurrentTimeMillis();
1112    }
1113
1114    /**
1115     * Decode the update lock mode.
1116     * <p>
1117     * The value for update lock mode is in the 2nd 2 bytes for
1118     * ExecutionContext.SERIALIZABLE_ISOLATION_LEVEL isolation level. Otherwise
1119     * (REPEATABLE READ, READ COMMITTED, and READ UNCOMMITTED) the lock mode is
1120     * located in the first 2 bytes.
1121     * <p>
1122     * This is done to override the optimizer choice to provide maximum
1123     * concurrency of record level locking except in SERIALIZABLE where table
1124     * level locking is required in heap scans for correctness.
1125     * <p>
1126     * See Compilation!QueryTree!FromBaseTable for encoding of the lockmode.
1127     * <p>
1128     *
1129     * @return The lock mode (record or table) to use to open the result set.
1130     *
1131     * @param lcc The context to look for current isolation level.
1132     * @param lockMode The compiled encoded lock mode for this query.
1133     *
1134     * @exception StandardException Standard exception policy.
1135     **/

1136    protected static int decodeLockMode(
1137    LanguageConnectionContext lcc,
1138    int lockMode)
1139    {
1140        if ((lockMode >>> 16) != 0)
1141        {
1142            // Note that isolation level encoding from
1143
// getCurrentIsolationLevel() returns
1144
// ExecutionContext.*ISOLATION_LEVEL constants, not
1145
// TransactionController.ISOLATION* constants.
1146

1147            int isolationLevel = lcc.getCurrentIsolationLevel();
1148
1149            if (isolationLevel != ExecutionContext.SERIALIZABLE_ISOLATION_LEVEL)
1150            {
1151                lockMode = lockMode & 0xff;
1152            }
1153            else
1154            {
1155                lockMode = lockMode >>> 16;
1156            }
1157        }
1158        return lockMode;
1159    }
1160
1161    
1162    void rowChangerFinish() throws StandardException
1163    {
1164        rowChanger.finish();
1165    }
1166
1167}
1168
Popular Tags