001: /*
002:
003: Derby - Class org.apache.derby.impl.store.access.btree.index.B2IRowLocking3
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.store.access.btree.index;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025:
026: import org.apache.derby.iapi.error.StandardException;
027:
028: import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
029:
030: import org.apache.derby.iapi.store.access.ConglomerateController;
031:
032: import org.apache.derby.iapi.store.access.RowUtil;
033: import org.apache.derby.iapi.store.access.TransactionController;
034:
035: import org.apache.derby.iapi.store.raw.FetchDescriptor;
036: import org.apache.derby.iapi.store.raw.LockingPolicy;
037: import org.apache.derby.iapi.store.raw.Page;
038: import org.apache.derby.iapi.store.raw.RecordHandle;
039: import org.apache.derby.iapi.store.raw.Transaction;
040:
041: import org.apache.derby.iapi.types.DataValueDescriptor;
042:
043: import org.apache.derby.iapi.types.RowLocation;
044:
045: import org.apache.derby.impl.store.access.btree.BTree;
046: import org.apache.derby.impl.store.access.btree.BTreeLockingPolicy;
047: import org.apache.derby.impl.store.access.btree.ControlRow;
048: import org.apache.derby.impl.store.access.btree.LeafControlRow;
049: import org.apache.derby.impl.store.access.btree.OpenBTree;
050: import org.apache.derby.impl.store.access.btree.BTreeRowPosition;
051: import org.apache.derby.impl.store.access.btree.WaitError;
052:
053: /**
054:
055: Implements the jdbc serializable isolation level using row locks.
056: <p>
057: Holds read and write locks until end of transaction.
058: Obtains previous key locks to protect from phantom reads.
059:
060: **/
061: class B2IRowLocking3 implements BTreeLockingPolicy {
062:
063: /**************************************************************************
064: * Private/Protected fields of This class:
065: **************************************************************************
066: */
067:
068: /**
069: * The container id of the base container for this index. Used to build
070: * record handles to make lock calls on.
071: **/
072: protected ConglomerateController base_cc;
073:
074: /**
075: * The OpenBtree to use if we have to lock anything in the btree vs.
076: * base row locking.
077: **/
078: protected OpenBTree open_btree;
079:
080: /**
081: * The locking policy to use to get and release the scan locks. We could
082: * cache this somewhere better.
083: **/
084: private LockingPolicy scan_locking_policy;
085:
086: /**
087: * The transaction to associate lock requests with.
088: **/
089: private Transaction rawtran;
090:
091: /**************************************************************************
092: * Constructors for This class:
093: **************************************************************************
094: */
095: B2IRowLocking3(Transaction rawtran, int lock_level,
096: LockingPolicy locking_policy,
097: ConglomerateController base_cc, OpenBTree open_btree) {
098: this .rawtran = rawtran;
099: this .base_cc = base_cc;
100: this .open_btree = open_btree;
101: this .scan_locking_policy = rawtran.newLockingPolicy(
102: LockingPolicy.MODE_RECORD,
103: TransactionController.ISOLATION_READ_COMMITTED, true);
104: }
105:
106: /**************************************************************************
107: * Private methods of This class:
108: **************************************************************************
109: */
110:
111: private boolean _lockScan(RecordHandle rh, boolean forUpdate,
112: boolean wait) throws StandardException {
113: boolean ret_val = true;
114:
115: // only get the scan lock if we are record locking.
116:
117: if (!forUpdate) {
118: ret_val = scan_locking_policy.lockRecordForRead(rawtran,
119: open_btree.getContainerHandle(), rh, wait, false);
120: } else {
121: ret_val = scan_locking_policy.lockRecordForWrite(rawtran,
122: rh, false, wait);
123: }
124:
125: return (ret_val);
126: }
127:
128: /**
129: * Lock key previous to first key in btree.
130: * <p>
131: * In the previous key locking protocol repeatable read and phantom
132: * protection is guaranteed by locking a range of keys in the btree.
133: * The range is defined by the key previous to the first key you look
134: * at and all subsequent keys you look at. The first key in the index
135: * is a special case, as there are no keys previous to it. In that
136: * case a special key is declared the "previous key" to the first key
137: * in the btree and is locked instead.
138: * <p>
139: * In this implementation that first key is defined to be in the base
140: * container, page ContainerHandle.FIRST_PAGE_NUMBER, record id
141: * PREVIOUS_KEY_HANDLE.
142: * <p>
143: * Note that the previous key is the same for all indexes on a given
144: * conglomerate. It seemed better for all locks on a base table to have
145: * the same containerid, rather than having some locks generated from
146: * a btree have a containerid from base table and some having a containerid
147: * from the btree. If this turns out to be a problem we could either
148: * have 2 different containerid's, be more creative with the record id, or
149: * even add more to the lock key.
150: *
151: * @param aux_leaf If non-null, this leaf is unlatched if the
152: * routine has to wait on the lock.
153: * @param lock_operation Whether to lock exclusive or share.
154: * @param lock_duration For what duration should the lock be held,
155: * if INSTANT_DURATION, then the routine will
156: * guarantee that lock was acquired while holding
157: * the latch, but then immediately release the
158: * lock. If COMMIT_DURATION or MANUAL_DURATION
159: * then the lock be held when routine returns
160: * successfully.
161: *
162: * @exception StandardException Standard exception policy.
163: **/
164: private boolean lockPreviousToFirstKey(LeafControlRow current_leaf,
165: LeafControlRow aux_leaf, int lock_operation,
166: int lock_duration) throws StandardException {
167: // This is first row in table, lock the special key that
168: // represents the key previous to the first key of the table.
169:
170: // First try to get the lock NOWAIT, while latch is held.
171: boolean ret_status = base_cc.lockRow(BTree.ROOTPAGEID,
172: RecordHandle.PREVIOUS_KEY_HANDLE, lock_operation,
173: false /* NOWAIT */, lock_duration);
174:
175: if (!ret_status) {
176: current_leaf.release();
177: current_leaf = null;
178:
179: if (aux_leaf != null) {
180: aux_leaf.release();
181: aux_leaf = null;
182: }
183:
184: // Couldn't get the lock NOWAIT, release latch and wait for lock.
185: base_cc.lockRow(BTree.ROOTPAGEID,
186: RecordHandle.PREVIOUS_KEY_HANDLE, lock_operation,
187: true /* WAIT */, lock_duration);
188: }
189:
190: return (ret_status);
191: }
192:
193: /**
194: * Lock a btree row (row is at given slot in page).
195: * <p>
196: * Lock the row at the given slot in the page. Meant to be used if caller
197: * only has the slot on the page to be locked, and has not read the row
198: * yet. This routine fetches the row location field from the page, and then
199: * locks that rowlocation in the base container.
200: * <p>
201: * Lock a btree row, enforcing the standard lock/latch protocol.
202: * On return the row is locked. Return status indicates if the lock
203: * was waited for, which will mean a latch was dropped while waiting.
204: * In general a false status means that the caller will either have
205: * to research the tree unless some protocol has been implemented that
206: * insures that the row will not have moved while the latch was dropped.
207: * <p>
208: * This routine request a row lock NOWAIT on the in-memory row
209: * "current_row.". If the lock is granted the routine will return true.
210: * If the lock cannot be granted NOWAIT, then the routine will release
211: * the latch on "current_leaf" and "aux_leaf" (if aux_leaf is non-null),
212: * and then it will request a WAIT lock on the row.
213: * <p>
214: *
215: * @param btree The conglomerate we are locking.
216: * @param current_leaf Latched current leaf where "current" key is.
217: * @param aux_leaf If non-null, this leaf is unlatched if the
218: * routine has to wait on the lock.
219: * @param current_slot Slot of row to lock.
220: * @param lock_fetch_desc Descriptor for fetching just the RowLocation,
221: * used for locking.
222: * @param check_changed_rowloc
223: * whether to check for the changed rowloc or not.
224: * @param lock_operation Whether lock is for key prev to insert or not.
225: * @param lock_duration For what duration should the lock be held,
226: * if INSTANT_DURATION, then the routine will
227: * guarantee that lock was acquired while holding
228: * the latch, but then immediately release the
229: * lock. If COMMIT_DURATION or MANUAL_DURATION
230: * then the lock be held when routine returns
231: * successfully.
232: *
233: * @exception StandardException Standard exception policy.
234: **/
235: private boolean lockRowOnPage(BTree btree,
236: LeafControlRow current_leaf, LeafControlRow aux_leaf,
237: int current_slot, boolean check_changed_rowloc,
238: FetchDescriptor lock_fetch_desc,
239: DataValueDescriptor[] lock_template,
240: RowLocation lock_row_loc, int lock_operation,
241: int lock_duration) throws StandardException {
242: if (SanityManager.DEBUG) {
243: SanityManager.ASSERT(current_leaf != null);
244:
245: if (current_slot <= 0
246: || current_slot >= current_leaf.getPage()
247: .recordCount()) {
248: SanityManager.THROWASSERT("current_slot = "
249: + current_slot
250: + "; current_leaf.getPage().recordCount() = "
251: + current_leaf.getPage().recordCount());
252: }
253:
254: if (!(btree instanceof B2I)) {
255: SanityManager
256: .THROWASSERT("btree not instance of B2I, it is "
257: + btree.getClass().getName());
258: }
259:
260: SanityManager.ASSERT(lock_template != null,
261: "template is null");
262:
263: // For now the RowLocation is expected to be the object located in
264: // the last column of the lock_template, this may change if we
265: // ever support rows with RowLocations somewhere else.
266: SanityManager
267: .ASSERT(
268: lock_row_loc == lock_template[lock_template.length - 1],
269: "row_loc is not the object in last column of lock_template.");
270: }
271:
272: // Fetch the row location to lock.
273: RecordHandle rec_handle = current_leaf.getPage().fetchFromSlot(
274: (RecordHandle) null, current_slot, lock_template,
275: lock_fetch_desc, true);
276:
277: // First try to get the lock NOWAIT, while latch is held.
278: boolean ret_status = base_cc.lockRow(lock_row_loc,
279: lock_operation, false /* NOWAIT */, lock_duration);
280:
281: if (!ret_status) {
282: // Could not get the lock NOWAIT, release latch and wait for lock.
283:
284: if (current_leaf != null) {
285: current_leaf.release();
286: current_leaf = null;
287: }
288: if (aux_leaf != null) {
289: aux_leaf.release();
290: aux_leaf = null;
291: }
292:
293: base_cc.lockRow(lock_row_loc, lock_operation,
294: true /* WAIT */, lock_duration);
295: }
296:
297: return (ret_status);
298: }
299:
300: private boolean searchLeftAndLockPreviousKey(B2I b2i,
301: LeafControlRow current_leaf, int current_slot,
302: FetchDescriptor lock_fetch_desc,
303: DataValueDescriptor[] lock_template,
304: RowLocation lock_row_loc, OpenBTree open_btree,
305: int lock_operation, int lock_duration)
306: throws StandardException {
307: boolean latches_released = false;
308: LeafControlRow prev_leaf;
309: LeafControlRow prev_prev_leaf;
310:
311: try {
312: // Move left in tree, page latch will be requested nowait,
313: // and WaitError will be thrown if latch not granted.
314:
315: prev_leaf = (LeafControlRow) current_leaf
316: .getLeftSibling(open_btree);
317:
318: } catch (WaitError e) {
319: long previous_pageno = current_leaf
320: .getleftSiblingPageNumber();
321:
322: // error going from mainpage to first left page. Release
323: // current page latch and continue the search.
324: current_leaf.release();
325: current_leaf = null;
326:
327: // wait on the left page, which we could not get before.
328: prev_leaf = (LeafControlRow) ControlRow.Get(open_btree,
329: previous_pageno);
330:
331: latches_released = true;
332: }
333:
334: while (true) {
335: try {
336: // loop searching left in the btree until you either find
337: // a record to lock, or you reach the leftmost empty leaf.
338:
339: if (prev_leaf.getPage().recordCount() > 1) {
340: // lock the last row on the page, which is the previous
341: // record to the first row on the next page.
342:
343: boolean ret_status = lockRowOnPage(b2i, prev_leaf,
344: current_leaf, prev_leaf.getPage()
345: .recordCount() - 1, false,
346: lock_fetch_desc, lock_template,
347: lock_row_loc, lock_operation, lock_duration);
348:
349: if (!ret_status) {
350: prev_leaf = null;
351: current_leaf = null;
352: latches_released = true;
353: }
354:
355: break;
356: } else if (prev_leaf.isLeftmostLeaf()) {
357: // Table's first row, lock the key that represents the
358: // key previous to first key of the table.
359: boolean ret_status = lockPreviousToFirstKey(
360: prev_leaf, current_leaf, lock_operation,
361: lock_duration);
362:
363: if (!ret_status) {
364: prev_leaf = null;
365: current_leaf = null;
366: latches_released = true;
367: }
368:
369: break;
370: }
371:
372: // Move left in tree, page latch will be requested nowait,
373: // and WaitError will be thrown if latch not granted.
374: // Release latches on pages between "current_leaf" and
375: // where the search leads, so that at most 3 latched pages
376: // (current_leaf, prev_leaf, prev_prev_leaf) are held during
377: // the search. Do left ladder locking as you walk left,
378: // but be ready to release l
379:
380: prev_prev_leaf = (LeafControlRow) prev_leaf
381: .getLeftSibling(open_btree);
382: prev_leaf.release();
383: prev_leaf = prev_prev_leaf;
384: prev_prev_leaf = null;
385: } catch (WaitError e) {
386: long previous_pageno = prev_leaf
387: .getleftSiblingPageNumber();
388:
389: // error going left. Release current page latch and
390: // original page latch continue the search.
391: current_leaf.release();
392: current_leaf = null;
393: prev_leaf.release();
394: prev_leaf = null;
395:
396: // wait on the left page, which we could not get before.
397: prev_leaf = (LeafControlRow) ControlRow.Get(open_btree,
398: previous_pageno);
399:
400: latches_released = true;
401: }
402: }
403: if (prev_leaf != null)
404: prev_leaf.release();
405:
406: return (!latches_released);
407:
408: }
409:
410: /**************************************************************************
411: * Protected methods of This class:
412: **************************************************************************
413: */
414:
415: /**
416: * Lock a row as part of doing the scan.
417: * <p>
418: * Lock the row at the given slot (or the previous row if slot is 0).
419: * Get the scan lock on the page if "request_scan_lock" is true.
420: * <p>
421: * If this routine returns true all locks were acquired while maintaining
422: * the latch on leaf. If this routine returns false, locks may or may
423: * not have been acquired, and the routine should be called again after
424: * the client has researched the tree to reget the latch on the
425: * appropriate page.
426: * (p>
427: * As a sided effect stores the value of the record handle of the current
428: * scan lock.
429: *
430: * @return Whether locks were acquired without releasing latch on leaf.
431: *
432: * @param open_btree The open_btree to associate latches with -
433: * used if routine has to scan backward.
434: * @param btree the conglomerate info.
435: * @param pos The position of the row to lock.
436: * @param request_row_lock Whether to request the row lock, should
437: * only be requested once per page in the scan.
438: * @param request_scan_lock Whether to request the page scan lock, should
439: * only be requested once per page in the scan.
440: * @param lock_fetch_desc The fetch descriptor to use to fetch the
441: * row location for the lock request.
442: * @param lock_template A scratch area to use to read in rows.
443: * @param previous_key_lock Is this a previous key lock call?
444: * @param forUpdate Is the scan for update or for read only.
445: *
446: * @exception StandardException Standard exception policy.
447: **/
448: protected boolean _lockScanRow(OpenBTree open_btree, BTree btree,
449: BTreeRowPosition pos, boolean request_row_lock,
450: boolean request_scan_lock, FetchDescriptor lock_fetch_desc,
451: DataValueDescriptor[] lock_template,
452: RowLocation lock_row_loc, boolean previous_key_lock,
453: boolean forUpdate, int lock_operation)
454: throws StandardException {
455: boolean latch_released = false;
456: B2I b2i = (B2I) btree;
457:
458: if (request_row_lock) {
459: // In order to implement a serialized scan based on previous
460: // key locking, this method acquires a row lock on
461: // the base table's row from the index row at [startpage/startslot].
462: // This will be the 'previous key'.
463:
464: if (pos.current_slot == 0) {
465: // this call will take care of searching left in the btree
466: // to find the previous row to lock, 0 is the control row and
467: // not a valid thing to lock as a previous key.
468:
469: // it is ok to call the non-scan as this is just a special
470: // case of a previous key lock call. The only scan code that
471: // will call this routine with slot == 0 will retry if this
472: // routine returns that a latch was released.
473:
474: latch_released = !lockNonScanPreviousRow(btree,
475: pos.current_leaf,
476: 1 /* lock row previous to row at slot 1 */,
477: lock_fetch_desc, lock_template, lock_row_loc,
478: open_btree, lock_operation,
479: TransactionManager.LOCK_COMMIT_DURATION);
480:
481: // special test to see if latch release code works
482: if (SanityManager.DEBUG) {
483: latch_released = OpenBTree.test_errors(open_btree,
484: "B2iRowLocking3_1_lockScanRow", false,
485: this , pos.current_leaf, latch_released);
486: }
487: } else {
488: // Just lock the row at "slot"
489:
490: latch_released = !lockRowOnPage(
491: btree,
492: pos.current_leaf,
493: (LeafControlRow) null /* no other latch currently */,
494: pos.current_slot, true, lock_fetch_desc,
495: lock_template, lock_row_loc, lock_operation,
496: TransactionManager.LOCK_COMMIT_DURATION);
497:
498: // special test to see if latch release code works
499: if (SanityManager.DEBUG) {
500: latch_released = OpenBTree.test_errors(open_btree,
501: "B2iRowLocking3_2_lockScanRow", false,
502: this , pos.current_leaf, latch_released);
503: }
504: }
505: }
506:
507: if (request_scan_lock && !latch_released) {
508: // Get the scan lock on the start page.
509:
510: // Get shared RECORD_ID_PROTECTION_HANDLE lock to make sure that
511: // we wait for scans in other transactions to move off of this page
512: // before we split.
513:
514: latch_released = !lockScan(pos.current_leaf,
515: (LeafControlRow) null, // no other latch currently
516: false, ConglomerateController.LOCK_READ);// read scan lock position
517:
518: // special test to see if latch release code works
519: if (SanityManager.DEBUG) {
520: /* RESOLVE - need to get a container here */
521: latch_released = OpenBTree.test_errors(open_btree,
522: "B2iRowLocking3_3_lockScanRow", true, this ,
523: pos.current_leaf, latch_released);
524: }
525: }
526:
527: return (!latch_released);
528: }
529:
530: /**************************************************************************
531: * Public Methods of This class:
532: **************************************************************************
533: */
534:
535: /**************************************************************************
536: * Abstract Protected lockScan*() locking methods of BTree:
537: * lockScan - lock the scan page
538: * lockScanForReclaimSpace - lock page for reclaiming deleted rows.
539: * lockScanRow - lock row and possibly the scan page
540: * unlockScan - unlock the scan page
541: * unlockScanRecordAfterRead- unlock the scan record
542: **************************************************************************
543: */
544:
545: /**
546: * Lock a control row page for scan.
547: * <p>
548: * Scanners get shared lock on the page while positioned on a row within
549: * the page, splitter/purgers/mergers get exclusive lock on the page.
550: *
551: * See BTree.lockScan() for more info.
552: *
553: * @exception StandardException Standard exception policy.
554: **/
555: public boolean lockScan(LeafControlRow current_leaf,
556: ControlRow aux_control_row, boolean forUpdate,
557: int lock_operation) throws StandardException {
558: // The scan page lock is implemented as a row lock on the reserved
559: // row id on the page (RecordHandle.RECORD_ID_PROTECTION_HANDLE).
560: RecordHandle scan_lock_rh = current_leaf.getPage()
561: .makeRecordHandle(
562: RecordHandle.RECORD_ID_PROTECTION_HANDLE);
563:
564: // First try to get the lock NOWAIT, while latch is held.
565: boolean ret_status = _lockScan(scan_lock_rh, forUpdate, false /* NOWAIT */);
566:
567: if (!ret_status) {
568: current_leaf.release();
569: current_leaf = null;
570:
571: if (aux_control_row != null) {
572: aux_control_row.release();
573: aux_control_row = null;
574: }
575:
576: // Could not get the lock NOWAIT, release latch and wait
577: // for the lock.
578: _lockScan(scan_lock_rh, forUpdate, true /* WAIT */);
579:
580: // once we get the lock, give it up as we need to get the lock
581: // while we have the latch. When the lock manager gives us the
582: // ability to do instantaneous locks do that. We just wait on the
583: // lock to give the split a chance to finish before we interfere.
584:
585: if (!forUpdate) {
586: scan_locking_policy.unlockRecordAfterRead(rawtran,
587: open_btree.getContainerHandle(), scan_lock_rh,
588: false, true);
589: } else {
590: // RESOLVE - need instantaneous locks as there is no way
591: // currently to release a write lock. This lock will only
592: // be requested by split, and will be released by internal
593: // transaction.
594: }
595: }
596:
597: return (ret_status);
598: }
599:
600: /**
601: * Lock a control row page for reclaiming deleted rows.
602: * <p>
603: * When reclaiming deleted rows during split need to get an exclusive
604: * scan lock on the page, which will mean there are no other scans
605: * positioned on the page. If there are other scans positioned, just
606: * give up on reclaiming space now.
607: *
608: * @return true if lock was granted nowait, else false and not lock was
609: * granted.
610: *
611: * @exception StandardException Standard exception policy.
612: **/
613: public boolean lockScanForReclaimSpace(LeafControlRow current_leaf)
614: throws StandardException {
615: // The scan page lock is implemented as a row lock on the reserved
616: // row id on the page (RecordHandle.RECORD_ID_PROTECTION_HANDLE).
617: RecordHandle scan_lock_rh = current_leaf.getPage()
618: .makeRecordHandle(
619: RecordHandle.RECORD_ID_PROTECTION_HANDLE);
620:
621: // First try to get the lock NOWAIT, while latch is held.
622: return (_lockScan(scan_lock_rh, true /* update */, false /* NOWAIT */));
623: }
624:
625: /**
626: * Lock a btree row to determine if it is a committed deleted row.
627: * <p>
628: * @see BTreeLockingPolicy#lockScanCommittedDeletedRow
629: *
630: * @exception StandardException Standard exception policy.
631: **/
632: public boolean lockScanCommittedDeletedRow(OpenBTree open_btree,
633: LeafControlRow leaf, DataValueDescriptor[] template,
634: FetchDescriptor lock_fetch_desc, int slot_no)
635: throws StandardException {
636: if (SanityManager.DEBUG) {
637: SanityManager.ASSERT(leaf != null);
638:
639: if (slot_no <= 0 || slot_no >= leaf.getPage().recordCount()) {
640: SanityManager.THROWASSERT("slot_no = " + slot_no
641: + "; leaf.getPage().recordCount() = "
642: + leaf.getPage().recordCount());
643: }
644:
645: SanityManager.ASSERT(template != null, "template is null");
646: }
647:
648: RowLocation row_loc = (RowLocation) template[((B2I) open_btree
649: .getConglomerate()).rowLocationColumn];
650:
651: // Fetch the row location to lock.
652: leaf.getPage().fetchFromSlot((RecordHandle) null, slot_no,
653: template, lock_fetch_desc, true);
654:
655: // Request the lock NOWAIT, return status
656: return (base_cc.lockRow(row_loc,
657: ConglomerateController.LOCK_UPD, false /* NOWAIT */,
658: TransactionManager.LOCK_COMMIT_DURATION));
659: }
660:
661: /**
662: * Lock a row as part of doing the scan.
663: * <p>
664: * Lock the row at the given slot (or the previous row if slot is 0).
665: * Get the scan lock on the page if "request_scan_lock" is true.
666: * <p>
667: * If this routine returns true all locks were acquired while maintaining
668: * the latch on leaf. If this routine returns false, locks may or may
669: * not have been acquired, and the routine should be called again after
670: * the client has researched the tree to reget the latch on the
671: * appropriate page.
672: * (p>
673: * As a sided effect stores the value of the record handle of the current
674: * scan lock.
675: *
676: * @return Whether locks were acquired without releasing latch on leaf.
677: *
678: * @param open_btree The open_btree to associate latches with -
679: * used if routine has to scan backward.
680: * @param btree the conglomerate info.
681: * @param pos The position of the row to lock.
682: * @param request_scan_lock Whether to request the page scan lock, should
683: * only be requested once per page in the scan.
684: * @param lock_template A scratch area to use to read in rows.
685: * @param previous_key_lock Is this a previous key lock call?
686: * @param forUpdate Is the scan for update or for read only.
687: *
688: * @exception StandardException Standard exception policy.
689: **/
690: public boolean lockScanRow(OpenBTree open_btree, BTree btree,
691: BTreeRowPosition pos, boolean request_scan_lock,
692: FetchDescriptor lock_fetch_desc,
693: DataValueDescriptor[] lock_template,
694: RowLocation lock_row_loc, boolean previous_key_lock,
695: boolean forUpdate, int lock_operation)
696: throws StandardException {
697: return (_lockScanRow(open_btree, btree,
698: pos,
699: true, // request the row lock (always true for iso 3 )
700: request_scan_lock, lock_fetch_desc, lock_template,
701: lock_row_loc, previous_key_lock, forUpdate,
702: lock_operation));
703: }
704:
705: /**
706: * Release read lock on a row.
707: *
708: * For serializable, there is no work to do.
709: *
710: *
711: **/
712: public void unlockScanRecordAfterRead(BTreeRowPosition pos,
713: boolean forUpdate) throws StandardException {
714: return;
715: }
716:
717: /**
718: * Release the lock gotten by calling lockScan. This call can only be
719: * made to release read scan locks, write scan locks must be held until
720: * end of transaction.
721: * <p>
722: * See BTree.unlockScan() for more info.
723: *
724: **/
725: public void unlockScan(long page_number) {
726: // This is first row in table, lock the special key that
727: // represents the key previous to the first key of the table.
728: try {
729: RecordHandle scan_lock_rh = open_btree.makeRecordHandle(
730: page_number,
731: RecordHandle.RECORD_ID_PROTECTION_HANDLE);
732:
733: scan_locking_policy.unlockRecordAfterRead(rawtran,
734: open_btree.getContainerHandle(), scan_lock_rh,
735: false, true);
736: } catch (StandardException se) {
737: if (SanityManager.DEBUG)
738: SanityManager
739: .THROWASSERT("error from make RecordHandle.");
740: }
741:
742: }
743:
744: /**************************************************************************
745: * Abstract Protected lockNonScan*() locking methods of BTree:
746: *
747: * lockNonScanPreviousRow - lock the row previous to the current
748: * lockNonScanRow - lock the input row
749: **************************************************************************
750: */
751:
752: /**
753: * Lock the row previous to the input row.
754: * <p>
755: * See BTree.lockPreviousRow() for more info.
756: *
757: * @exception StandardException Standard exception policy.
758: **/
759: public boolean lockNonScanPreviousRow(BTree btree,
760: LeafControlRow current_leaf, int current_slot,
761: FetchDescriptor lock_fetch_desc,
762: DataValueDescriptor[] lock_template,
763: RowLocation lock_row_loc, OpenBTree open_btree,
764: int lock_operation, int lock_duration)
765: throws StandardException {
766: boolean ret_status;
767:
768: if (SanityManager.DEBUG) {
769: SanityManager.ASSERT(btree instanceof B2I);
770: }
771:
772: if (current_slot > 1) {
773: // Easy case, just lock the key previous to the current one.
774:
775: // Lock (current_slot - 1)
776:
777: ret_status = lockRowOnPage(btree, current_leaf,
778: (LeafControlRow) null, current_slot - 1, false,
779: lock_fetch_desc, lock_template, lock_row_loc,
780: lock_operation, lock_duration);
781: } else {
782: // Should only be called while pointing at a valid location, 0
783: // is not a valid key slot - it is the control row.
784: if (SanityManager.DEBUG)
785: SanityManager.ASSERT(current_slot == 1);
786:
787: if (current_leaf.isLeftmostLeaf()) {
788: // This is first row in table, lock the special key that
789: // represents the key previous to the first key of the table.
790: ret_status = lockPreviousToFirstKey(current_leaf,
791: (LeafControlRow) null, lock_operation,
792: lock_duration);
793: } else {
794: // The previous key is on a previous page, search left
795: // through the pages to find the key to latch.
796:
797: // RESOLVE RLL (mikem) - do I need to do the
798: // RECORD_ID_PROTECTION_HANDLE lock.
799: // First guarantee that record id's will not move off this
800: // current page while searching for previous key, by getting
801: // the RECORD_ID_PROTECTION_HANDLE lock on the current page.
802: // Since we have a latch on the cur
803:
804: // RESOLVE RLL (mikem) - NO RECORD_ID PROTECTION IN EFFECT.
805: // caller must research, get new locks if this routine
806: // releases latches.
807: ret_status = this .searchLeftAndLockPreviousKey(
808: (B2I) btree, current_leaf, current_slot,
809: lock_fetch_desc, lock_template, lock_row_loc,
810: open_btree, lock_operation, lock_duration);
811: }
812: }
813:
814: return (ret_status);
815: }
816:
817: /**
818: * Lock the in memory row.
819: * <p>
820: * See BTree.lockRow() for more info.
821: *
822: * @exception StandardException Standard exception policy.
823: **/
824: public boolean lockNonScanRow(BTree btree,
825: LeafControlRow current_leaf, LeafControlRow aux_leaf,
826: DataValueDescriptor[] current_row, int lock_operation)
827: throws StandardException {
828: if (SanityManager.DEBUG) {
829: SanityManager.ASSERT(btree instanceof B2I);
830: }
831: B2I b2i = (B2I) btree;
832:
833: // First try to get the lock NOWAIT, while latch is held.
834: boolean ret_status = base_cc.lockRow(
835: (RowLocation) current_row[b2i.rowLocationColumn],
836: lock_operation, false /* NOWAIT */,
837: TransactionManager.LOCK_COMMIT_DURATION);
838:
839: if (!ret_status) {
840: // Could not get the lock NOWAIT, release latch and wait for lock.
841:
842: if (current_leaf != null) {
843: current_leaf.release();
844: current_leaf = null;
845: }
846: if (aux_leaf != null) {
847: aux_leaf.release();
848: aux_leaf = null;
849: }
850:
851: base_cc.lockRow(
852: (RowLocation) current_row[b2i.rowLocationColumn],
853: lock_operation, true /* WAIT */,
854: TransactionManager.LOCK_COMMIT_DURATION);
855: }
856:
857: return (ret_status);
858: }
859:
860: public boolean lockNonScanRowOnPage(BTree btree,
861: LeafControlRow current_leaf, int current_slot,
862: FetchDescriptor lock_fetch_desc,
863: DataValueDescriptor[] lock_template,
864: RowLocation lock_row_loc, int lock_operation)
865: throws StandardException {
866: return (lockRowOnPage(btree, current_leaf, null, current_slot,
867: false, lock_fetch_desc, lock_template, lock_row_loc,
868: lock_operation, TransactionManager.LOCK_COMMIT_DURATION));
869: }
870: }
|