KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > store > access > heap > HeapController


1 /*
2
3    Derby - Class org.apache.derby.impl.store.access.heap.HeapController
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.store.access.heap;
23
24 import org.apache.derby.iapi.reference.SQLState;
25
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27
28 import org.apache.derby.iapi.error.StandardException;
29
30 import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
31
32 import org.apache.derby.iapi.store.access.AccessFactoryGlobals;
33 import org.apache.derby.iapi.store.access.ConglomerateController;
34 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
35 import org.apache.derby.iapi.store.access.RowLocationRetRowSource;
36 import org.apache.derby.iapi.store.access.RowUtil;
37 import org.apache.derby.iapi.store.access.TransactionController;
38
39 import org.apache.derby.iapi.store.raw.ContainerHandle;
40 import org.apache.derby.iapi.store.raw.LockingPolicy;
41 import org.apache.derby.iapi.store.raw.Page;
42 import org.apache.derby.iapi.store.raw.RecordHandle;
43
44 import org.apache.derby.iapi.types.DataValueDescriptor;
45
46 import org.apache.derby.iapi.types.RowLocation;
47
48 import org.apache.derby.impl.store.access.conglomerate.OpenConglomerate;
49 import org.apache.derby.impl.store.access.conglomerate.GenericConglomerateController;
50 import org.apache.derby.impl.store.access.conglomerate.RowPosition;
51
52 import org.apache.derby.iapi.services.io.FormatableBitSet;
53
54 /**
55
56 **/

57
58 public class HeapController
59     extends GenericConglomerateController
60     implements ConglomerateController
61 {
62     /**************************************************************************
63      * Fields of the class
64      **************************************************************************
65      */

66
67     /**************************************************************************
68      * Constructors for This class:
69      **************************************************************************
70      */

71
72     /**************************************************************************
73      * Protected concrete impl of abstract methods of
74      * GenericConglomerateController class:
75      **************************************************************************
76      */

77     protected final void getRowPositionFromRowLocation(
78     RowLocation row_loc,
79     RowPosition pos)
80         throws StandardException
81     {
82         if (SanityManager.DEBUG)
83         {
84             SanityManager.ASSERT(row_loc instanceof HeapRowLocation);
85         }
86         pos.current_rh =
87             ((HeapRowLocation) row_loc).getRecordHandle(
88                 open_conglom.getContainer());
89         pos.current_rh_qualified = true;
90     }
91
92     protected void queueDeletePostCommitWork(
93     RowPosition pos)
94         throws StandardException
95     {
96         TransactionManager xact_mgr = open_conglom.getXactMgr();
97
98         xact_mgr.addPostCommitWork(
99             new HeapPostCommit(
100                 xact_mgr.getAccessManager(),
101                 (Heap) open_conglom.getConglomerate(),
102                 pos.current_page.getPageNumber()));
103     }
104
105     /**************************************************************************
106      * Private/Protected methods of This class:
107      **************************************************************************
108      */

109
110     /**
111      * Check and purge committed deleted rows on a page.
112      * <p>
113      *
114      * @return true, if no purging has been done on page, and thus latch
115      * can be released before end transaction. Otherwise the latch
116      * on the page can not be released before commit.
117      *
118      * @param page A non-null, latched page must be passed in. If all
119      * rows on page are purged, then page will be removed and
120      * latch released.
121      *
122      * @exception StandardException Standard exception policy.
123      **/

124     protected final boolean purgeCommittedDeletes(
125     Page page)
126         throws StandardException
127     {
128         boolean purgingDone = false;
129
130         // The number records that can be reclaimed is:
131
// total recs - recs_not_deleted
132
int num_possible_commit_delete =
133             page.recordCount() - page.nonDeletedRecordCount();
134
135         if (num_possible_commit_delete > 0)
136         {
137             // loop backward so that purges which affect the slot table
138
// don't affect the loop (ie. they only move records we
139
// have already looked at).
140
for (int slot_no = page.recordCount() - 1;
141                  slot_no >= 0;
142                  slot_no--)
143             {
144                 boolean row_is_committed_delete =
145                     page.isDeletedAtSlot(slot_no);
146
147                 if (row_is_committed_delete)
148                 {
149                     // At this point we only know that the row is
150
// deleted, not whether it is committed.
151

152                     // see if we can purge the row, by getting an
153
// exclusive lock on the row. If it is marked
154
// deleted and we can get this lock, then it
155
// must be a committed delete and we can purge
156
// it.
157

158                     RecordHandle rh =
159                         page.fetchFromSlot(
160                             (RecordHandle) null,
161                             slot_no,
162                             RowUtil.EMPTY_ROW,
163                             RowUtil.EMPTY_ROW_FETCH_DESCRIPTOR,
164                             true);
165
166                     row_is_committed_delete =
167                         this.lockRowAtSlotNoWaitExclusive(rh);
168
169                     if (row_is_committed_delete)
170                     {
171                         purgingDone = true;
172
173                         page.purgeAtSlot(slot_no, 1, false);
174                     }
175                 }
176             }
177         }
178         if (page.recordCount() == 0)
179         {
180
181             // Deallocate the current page with 0 rows on it.
182
this.removePage(page);
183
184             // removePage guarantees to unlatch the page even if an
185
// exception is thrown. The page is protected against reuse
186
// because removePage locks it with a dealloc lock, so it
187
// is OK to release the latch even after a purgeAtSlot is
188
// called.
189
// @see ContainerHandle#removePage
190

191             purgingDone = true;
192         }
193
194         return(purgingDone);
195     }
196
197     /**
198      * Insert a new row into the heap.
199      * <p>
200      * Overflow policy:
201      * The current heap access method implements an algorithm that optimizes
202      * for fetch efficiency vs. space efficiency. A row will not be over
203      * flowed unless it is bigger than a page. If it is bigger than a page
204      * then it's initial part will be placed on a page and then subsequent
205      * parts will be overflowed to other pages.
206      * <p>
207      *
208      * @return The record handle of the inserted row.
209      *
210      * @param row The row to insert.
211      *
212      * @exception StandardException Standard exception policy.
213      **/

214     private RecordHandle doInsert(DataValueDescriptor[] row)
215         throws StandardException
216     {
217         Page page = null;
218         byte insert_mode;
219         
220         RecordHandle rh;
221
222         if (SanityManager.DEBUG)
223         {
224             Heap heap = (Heap) open_conglom.getConglomerate();
225             // Make sure valid columns are in the list. The RowUtil
226
// call is too expensive to make in a released system for
227
// every insert.
228

229             int invalidColumn =
230                 RowUtil.columnOutOfRange(
231                     row, null, heap.format_ids.length);
232
233             if (invalidColumn >= 0)
234             {
235                 throw(StandardException.newException(
236                         SQLState.HEAP_TEMPLATE_MISMATCH,
237                         new Long JavaDoc(invalidColumn),
238                         new Long JavaDoc(heap.format_ids.length)));
239             }
240         }
241
242         // Get the last page that was returned for insert or the last page
243
// that was allocated.
244
page = open_conglom.getContainer().getPageForInsert(0);
245
246         if (page != null) {
247
248             // if there are 0 rows on the page allow the insert to overflow.
249
insert_mode =
250                 (page.recordCount() == 0) ?
251                     Page.INSERT_OVERFLOW : Page.INSERT_DEFAULT;
252
253             // Check to see if there is enough space on the page
254
// for the row.
255
rh = page.insert(row, null, insert_mode,
256                 AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD);
257             page.unlatch();
258             page = null;
259
260             // If we have found a page with enough space for the row,
261
// insert it and release exclusive access to the page.
262
if (rh != null)
263             {
264                 return rh;
265
266             }
267         }
268
269         // If the last inserted page is now full, or RawStore have
270
// forgotten what it was, or the row cannot fit on the last
271
// inserted page, try to have rawStore get a relatively unfilled
272
// page.
273

274         page =
275             open_conglom.getContainer().getPageForInsert(
276                 ContainerHandle.GET_PAGE_UNFILLED);
277
278         if (page != null)
279         {
280             // Do the insert all over again hoping that it will fit into
281
// this page, and if not, allocate a new page.
282

283             // if there are 0 rows on the page allow the insert to overflow.
284
insert_mode =
285                 (page.recordCount() == 0) ?
286                     Page.INSERT_OVERFLOW : Page.INSERT_DEFAULT;
287             
288             rh = page.insert(row, null, insert_mode,
289                 AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD);
290
291             page.unlatch();
292             page = null;
293
294             // If we have found a page with enough space for the row,
295
// insert it and release exclusive access to the page.
296
if (rh != null)
297             {
298                 return rh;
299             }
300         }
301
302         page = open_conglom.getContainer().addPage();
303
304         // At this point with long rows the raw store will guarantee
305
// that any size row will fit on an empty page.
306

307         rh = page.insert(row, null, Page.INSERT_OVERFLOW,
308             AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD);
309         page.unlatch();
310         page = null;
311
312         if (SanityManager.DEBUG)
313         {
314             // a null will only be returned if this page is not empty
315
SanityManager.ASSERT(rh != null);
316         }
317
318         return rh;
319     }
320
321     protected long load(
322     TransactionManager xact_manager,
323     Heap heap,
324     boolean createConglom,
325     RowLocationRetRowSource rowSource)
326          throws StandardException
327     {
328         long num_rows_loaded = 0;
329
330         if (SanityManager.DEBUG)
331         {
332             SanityManager.ASSERT(open_conglom == null,
333                 "load expects container handle to be closed on entry.");
334         }
335
336         // The individual rows that are inserted are not logged. To use a
337
// logged interface, use insert. RESOLVE: do we want to allow client
338
// to use the load interface even for logged insert?
339
int mode =
340             (ContainerHandle.MODE_FORUPDATE | ContainerHandle.MODE_UNLOGGED);
341
342         // If the container is being created in the same operation, don't log
343
// page allocation.
344
if (createConglom)
345             mode |= ContainerHandle.MODE_CREATE_UNLOGGED;
346
347         OpenConglomerate open_conglom = new OpenHeap();
348
349         if (open_conglom.init(
350                 (ContainerHandle) null,
351                 heap,
352                 heap.format_ids,
353                 xact_manager,
354                 xact_manager.getRawStoreXact(),
355                 false,
356                 mode,
357                 TransactionController.MODE_TABLE,
358                 xact_manager.getRawStoreXact().newLockingPolicy(
359                     LockingPolicy.MODE_CONTAINER,
360                     TransactionController.ISOLATION_SERIALIZABLE, true),
361                 (DynamicCompiledOpenConglomInfo) null) == null)
362         {
363             throw StandardException.newException(
364                     SQLState.HEAP_CONTAINER_NOT_FOUND,
365                     new Long JavaDoc(heap.id.getContainerId()));
366         }
367
368         this.init(open_conglom);
369
370         // For bulk loading, we always use only brand new page because the row
371
// insertion itself is not logged. We cannot pollute pages with
372
// pre-existing data with unlogged rows because nobody is going to wipe
373
// out these rows if the transaction rolls back. We are counting on
374
// the allocation page rollback to obliterate these rows if the
375
// transaction fails, or, in the CREAT_UNLOGGED case, the whole
376
// container to be removed.
377

378         Page page = open_conglom.getContainer().addPage();
379
380         boolean callbackWithRowLocation = rowSource.needsRowLocation();
381         RecordHandle rh;
382         HeapRowLocation rowlocation;
383
384         if (callbackWithRowLocation)
385             rowlocation = new HeapRowLocation();
386         else
387             rowlocation = null;
388
389         FormatableBitSet validColumns = rowSource.getValidColumns();
390
391         try
392         {
393             // get the next row and its valid columns from the rowSource
394
DataValueDescriptor[] row;
395             while ((row = rowSource.getNextRowFromRowSource()) != null)
396             {
397                 num_rows_loaded++;
398
399                 if (SanityManager.DEBUG)
400                 {
401                     // Make sure valid columns are in the list. The RowUtil
402
// call is too expensive to make in a released system for
403
// every insert.
404
int invalidColumn =
405                         RowUtil.columnOutOfRange(
406                             row, validColumns, heap.format_ids.length);
407
408                     if (invalidColumn >= 0)
409                     {
410                         throw(StandardException.newException(
411                                 SQLState.HEAP_TEMPLATE_MISMATCH,
412                                 new Long JavaDoc(invalidColumn),
413                                 new Long JavaDoc(heap.format_ids.length)));
414                     }
415                 }
416
417
418                 // Insert it onto this page as long as it can fit more rows.
419
if ((rh = page.insert(
420                         row, validColumns, Page.INSERT_DEFAULT,
421                         AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD))
422                                 == null)
423                 {
424                     // Insert faied, row did not fit. Get a new page.
425

426                     page.unlatch();
427                     page = null;
428
429                     page = open_conglom.getContainer().addPage();
430
431                     // RESOLVE (mikem) - no long rows yet so the following code
432
// will get an exception from the raw store for a row that
433
// does not fit on a page.
434
//
435
// Multi-thread considerations aside, the raw store will
436
// guarantee that any size row will fit on an empty page.
437
rh = page.insert(
438                             row, validColumns, Page.INSERT_OVERFLOW,
439                             AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD);
440
441                 }
442
443                 // Else, the row fit. If we are expected to call back with the
444
// row location, do so. All the while keep the page latched
445
// and go for the next row.
446
if (callbackWithRowLocation)
447                 {
448                     rowlocation.setFrom(rh);
449                     rowSource.rowLocation(rowlocation);
450                 }
451             }
452             page.unlatch();
453             page = null;
454
455             // Done with the container, now we need to flush it to disk since
456
// it is unlogged.
457
if (!heap.isTemporary())
458                 open_conglom.getContainer().flushContainer();
459         }
460         finally
461         {
462             // If an error happened here, don't bother flushing the
463
// container since the changes should be rolled back anyhow.
464
close();
465         }
466         return(num_rows_loaded);
467     }
468
469     protected boolean lockRow(
470     RecordHandle rh,
471     int lock_oper,
472     boolean wait,
473     int lock_duration)
474         throws StandardException
475     {
476         boolean ret_val;
477         boolean forUpdate =
478             ((ConglomerateController.LOCK_UPD & lock_oper) != 0);
479         boolean forUpdateLock =
480             ((ConglomerateController.LOCK_UPDATE_LOCKS & lock_oper) != 0);
481
482         if (forUpdate && !forUpdateLock)
483         {
484             boolean forInsert =
485                 ((ConglomerateController.LOCK_INS & lock_oper) != 0);
486             boolean forInsertPrevKey =
487                 ((ConglomerateController.LOCK_INS_PREVKEY & lock_oper) != 0);
488
489             if (SanityManager.DEBUG)
490             {
491                 SanityManager.ASSERT(!(forInsertPrevKey && forInsert));
492             }
493
494             if (lock_duration == TransactionManager.LOCK_INSTANT_DURATION)
495             {
496                 ret_val =
497                     open_conglom.getContainer().getLockingPolicy().
498                         zeroDurationLockRecordForWrite(
499                             open_conglom.getRawTran(), rh, forInsertPrevKey, wait);
500             }
501             else
502             {
503                 ret_val =
504                     open_conglom.getContainer().getLockingPolicy().
505                         lockRecordForWrite(
506                             open_conglom.getRawTran(), rh, forInsert, wait);
507             }
508         }
509         else
510         {
511             if (SanityManager.DEBUG)
512             {
513                 SanityManager.ASSERT(
514                     (ConglomerateController.LOCK_INS & lock_oper) == 0);
515                 SanityManager.ASSERT(
516                     (ConglomerateController.LOCK_INS_PREVKEY & lock_oper) == 0);
517             }
518
519             ret_val =
520                 open_conglom.getContainer().getLockingPolicy().lockRecordForRead(
521                     open_conglom.getRawTran(),
522                     open_conglom.getContainer(), rh, wait, forUpdate);
523         }
524
525         return(ret_val);
526     }
527
528     protected Page getUserPageNoWait(long pageno)
529         throws StandardException
530     {
531         return(open_conglom.getContainer().getUserPageNoWait(pageno));
532     }
533     protected Page getUserPageWait(long pageno)
534         throws StandardException
535     {
536         return(open_conglom.getContainer().getUserPageWait(pageno));
537     }
538     protected boolean lockRowAtSlotNoWaitExclusive(RecordHandle rh)
539         throws StandardException
540     {
541         return(
542             open_conglom.getContainer().getLockingPolicy().
543                 lockRecordForWrite(
544                     open_conglom.getRawTran(), rh, false, false));
545     }
546     protected void removePage(Page page)
547         throws StandardException
548     {
549         open_conglom.getContainer().removePage(page);
550     }
551
552     /**************************************************************************
553      * Public Methods of This class:
554      **************************************************************************
555      */

556
557     public int insert(DataValueDescriptor[] row)
558         throws StandardException
559     {
560         if (open_conglom.isClosed())
561         {
562             if (open_conglom.getHold())
563             {
564                 open_conglom.reopen();
565             }
566             else
567             {
568                 throw(StandardException.newException(
569                         SQLState.HEAP_IS_CLOSED,
570                         open_conglom.getConglomerate().getId()));
571             }
572         }
573
574         doInsert(row);
575
576         return(0);
577     }
578
579     public void insertAndFetchLocation(
580     DataValueDescriptor[] row,
581     RowLocation templateRowLocation)
582         throws StandardException
583     {
584         if (open_conglom.isClosed())
585         {
586             if (open_conglom.getHold())
587             {
588                 open_conglom.reopen();
589             }
590             else
591             {
592                 throw(StandardException.newException(
593                         SQLState.HEAP_IS_CLOSED,
594                         open_conglom.getConglomerate().getId()));
595             }
596         }
597
598         RecordHandle rh = doInsert(row);
599         if (SanityManager.DEBUG)
600         {
601             SanityManager.ASSERT(
602                 templateRowLocation instanceof HeapRowLocation);
603         }
604         HeapRowLocation hrl = (HeapRowLocation) templateRowLocation;
605         hrl.setFrom(rh);
606     }
607
608     /**
609      * Lock the given row location.
610      * <p>
611      * Should only be called by access.
612      * <p>
613      * This call can be made on a ConglomerateController that was opened
614      * for locking only.
615      * <p>
616      * RESOLVE (mikem) - move this call to ConglomerateManager so it is
617      * obvious that non-access clients should not call this.
618      *
619      * @return true if lock was granted, only can be false if wait was false.
620      *
621      * @param loc The "RowLocation" which describes the exact row to lock.
622      * @param wait Should the lock call wait to be granted?
623      *
624      * @exception StandardException Standard exception policy.
625      **/

626     public boolean lockRow(
627     RowLocation loc,
628     int lock_operation,
629     boolean wait,
630     int lock_duration)
631         throws StandardException
632     {
633         RecordHandle rh =
634             ((HeapRowLocation) loc).getRecordHandle(
635                 open_conglom.getContainer());
636
637         return(lockRow(rh, lock_operation, wait, lock_duration));
638     }
639
640     /**
641      * UnLock the given row location.
642      * <p>
643      * Should only be called by access.
644      * <p>
645      * This call can be made on a ConglomerateController that was opened
646      * for locking only.
647      * <p>
648      * RESOLVE (mikem) - move this call to ConglomerateManager so it is
649      * obvious that non-access clients should not call this.
650      *
651      * @param loc The "RowLocation" which describes the row to unlock.
652      * @param forUpdate Row was previously Locked the record for read or update.
653      *
654      * @exception StandardException Standard exception policy.
655      **/

656     public void unlockRowAfterRead(
657     RowLocation loc,
658     boolean forUpdate,
659     boolean row_qualified)
660         throws StandardException
661     {
662
663         RecordHandle rh =
664             ((HeapRowLocation) loc).getRecordHandle(
665                 open_conglom.getContainer());
666
667         open_conglom.getContainer().getLockingPolicy().
668             unlockRecordAfterRead(
669                 open_conglom.getRawTran(),
670                 open_conglom.getContainer(),
671                 rh,
672                 open_conglom.isForUpdate(),
673                 row_qualified);
674     }
675
676
677     /**
678      * Lock the given record id/page num pair.
679      * <p>
680      * Should only be called by access, to lock "special" locks formed from
681      * the Recordhandle.* reserved constants for page specific locks.
682      * <p>
683      * This call can be made on a ConglomerateController that was opened
684      * for locking only.
685      * <p>
686      * RESOLVE (mikem) - move this call to ConglomerateManager so it is
687      * obvious that non-access clients should not call this.
688      *
689      * @return true if lock was granted, only can be false if wait was false.
690      *
691      * @param page_num Page number of row to lock.
692      * @param record_id Record id of row on page_num to lock.
693      * @param lock_operation Desc of what to lock for, ie. update, insert ...
694      * @param wait Should the lock call wait to be granted?
695      *
696      * @exception StandardException Standard exception policy.
697      **/

698     public boolean lockRow(
699     long page_num,
700     int record_id,
701     int lock_operation,
702     boolean wait,
703     int lock_duration)
704         throws StandardException
705     {
706         boolean ret_val;
707
708         RecordHandle rh =
709             open_conglom.getContainer().makeRecordHandle(page_num, record_id);
710
711         return(lockRow(rh, lock_operation, wait, lock_duration));
712     }
713
714     public RowLocation newRowLocationTemplate()
715         throws StandardException
716     {
717         if (open_conglom.isClosed())
718         {
719             if (open_conglom.getHold())
720             {
721                 open_conglom.reopen();
722             }
723             else
724             {
725                 throw(StandardException.newException(
726                         SQLState.HEAP_IS_CLOSED,
727                         open_conglom.getConglomerate().getId()));
728             }
729         }
730
731         return new HeapRowLocation();
732     }
733
734
735     /**************************************************************************
736      * Public Methods of XXXX class:
737      **************************************************************************
738      */

739 }
740
Popular Tags