0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.store.access.btree.BTreeScan
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.store.access.btree;
0023:
0024: import org.apache.derby.iapi.reference.SQLState;
0025:
0026: import org.apache.derby.iapi.services.sanity.SanityManager;
0027: import org.apache.derby.iapi.services.io.Storable;
0028:
0029: import org.apache.derby.iapi.error.StandardException;
0030:
0031: import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;
0032: import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
0033: import org.apache.derby.iapi.store.access.conglomerate.ScanManager;
0034: import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
0035:
0036: import org.apache.derby.iapi.store.access.ConglomerateController;
0037: import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
0038: import org.apache.derby.iapi.store.access.GenericScanController;
0039: import org.apache.derby.iapi.store.access.Qualifier;
0040: import org.apache.derby.iapi.store.access.RowUtil;
0041: import org.apache.derby.iapi.store.access.ScanController;
0042: import org.apache.derby.iapi.store.access.ScanInfo;
0043: import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
0044: import org.apache.derby.iapi.store.access.TransactionController;
0045:
0046: import org.apache.derby.iapi.store.raw.ContainerHandle;
0047: import org.apache.derby.iapi.store.raw.FetchDescriptor;
0048: import org.apache.derby.iapi.store.raw.LockingPolicy;
0049: import org.apache.derby.iapi.store.raw.Page;
0050: import org.apache.derby.iapi.store.raw.RecordHandle;
0051: import org.apache.derby.iapi.store.raw.Transaction;
0052:
0053: import org.apache.derby.iapi.types.DataValueDescriptor;
0054:
0055: import org.apache.derby.iapi.types.RowLocation;
0056:
0057: import org.apache.derby.impl.store.access.conglomerate.ConglomerateUtil;
0058: import org.apache.derby.impl.store.access.conglomerate.TemplateRow;
0059:
0060: import org.apache.derby.iapi.services.io.FormatableBitSet;
0061: import org.apache.derby.iapi.store.access.BackingStoreHashtable;
0062:
0063: /**
0064:
0065: A b-tree scan controller corresponds to an instance of an open b-tree scan.
0066: <P>
0067: <B>Concurrency Notes<\B>
0068: <P>
0069: The concurrency rules are derived from OpenBTree.
0070: <P>
0071: @see OpenBTree
0072:
0073: **/
0074:
0075: public abstract class BTreeScan extends OpenBTree implements
0076: ScanManager {
0077:
0078: /*
0079: ** Fields of BTreeScan
0080: */
0081:
0082: /**
0083: * init_startKeyValue, init_qualifier, and init_stopKeyValue all are used
0084: * to store * references to the values passed in when ScanController.init()
0085: * is called. It is assumed that these are not altered by the client
0086: * while the scan is active.
0087: */
0088: protected Transaction init_rawtran = null;
0089: protected boolean init_forUpdate;
0090: protected FormatableBitSet init_scanColumnList;
0091: protected DataValueDescriptor[] init_template;
0092: protected DataValueDescriptor[] init_startKeyValue;
0093: protected int init_startSearchOperator = 0;
0094: protected Qualifier init_qualifier[][] = null;
0095: protected DataValueDescriptor[] init_stopKeyValue;
0096: protected int init_stopSearchOperator = 0;
0097: protected boolean init_hold;
0098:
0099: /**
0100: * The fetch descriptor which describes the row to be returned by the scan.
0101: **/
0102: protected FetchDescriptor init_fetchDesc;
0103:
0104: /**
0105: * A constant FetchDescriptor which describes the position of the
0106: * RowLocation field within the btree, currently always the last column).
0107: * Used by lock/unlock to fetch the RowLocation.
0108: * Only needs to be allocated once per scan.
0109: **/
0110: protected FetchDescriptor init_lock_fetch_desc;
0111:
0112: BTreeRowPosition scan_position;
0113:
0114: /**
0115: * Whether the scan should requests UPDATE locks which then will be
0116: * converted to X locks when the actual operation is performed.
0117: **/
0118: protected boolean init_useUpdateLocks = false;
0119:
0120: /*
0121: * There are 5 states a scan can be in.
0122: * SCAN_INIT - A scan has started but no positioning has been done.
0123: * The scan will be positioned when the first next() call
0124: * has been made. None of the positioning state variables
0125: * are valid in this state.
0126: * SCAN_INPROGRESS -
0127: * A scan is in this state after the first next() call.
0128: * On exit from any BTreeScan method, while in this state,
0129: * the scan "points" at a row which qualifies for the
0130: * scan. While not maintaining latches on a page the
0131: * current position of the scan is either kept by record
0132: * handle or key. To tell which use the following:
0133: * if (record key == null)
0134: * record handle has current position
0135: * else
0136: * record key has current position
0137: *
0138: * SCAN_DONE - Once the end of the table or the stop condition is met
0139: * then the scan is placed in this state. Only valid
0140: * ScanController method at this point is close().
0141: *
0142: * SCAN_HOLD_INIT -
0143: * The scan has been opened and held open across a commit,
0144: * at the last commit the state was SCAN_INIT.
0145: * The scan has never progressed from the SCAN_INIT state
0146: * during a transaction. When a next is done the state
0147: * will either progress to SCAN_INPROGRESS or SCAN_DONE.
0148: *
0149: * SCAN_HOLD_INPROGRESS -
0150: * The scan has been opened and held open across a commit,
0151: * at the last commit the state was in SCAN_INPROGRESS.
0152: * The transaction which opened the scan has committed,
0153: * but the scan was opened with the "hold" option true.
0154: * At commit the locks were released and the "current"
0155: * position is remembered. In this state only two calls
0156: * are valid, either next() or close(). When next() is
0157: * called the scan is reopened, the underlying container
0158: * is opened thus associating all new locks with the current
0159: * transaction, and the scan continues at the "next" row.
0160: */
0161: protected static final int SCAN_INIT = 1;
0162: protected static final int SCAN_INPROGRESS = 2;
0163: protected static final int SCAN_DONE = 3;
0164: protected static final int SCAN_HOLD_INIT = 4;
0165: protected static final int SCAN_HOLD_INPROGRESS = 5;
0166:
0167: /**
0168: * Delay positioning the table at the start position until the first
0169: * next() call. The initial position is done in positionAtStartPosition().
0170: */
0171: protected int scan_state = SCAN_INIT;
0172:
0173: /**
0174: * Performance counters ...
0175: */
0176: protected int stat_numpages_visited = 0;
0177: protected int stat_numrows_visited = 0;
0178: protected int stat_numrows_qualified = 0;
0179: protected int stat_numdeleted_rows_visited = 0;
0180:
0181: /**
0182: * What kind of row locks to get during the scan.
0183: **/
0184: protected int lock_operation;
0185:
0186: /**
0187: * A 1 element array to turn fetchNext and fetch calls into
0188: * fetchNextGroup calls.
0189: **/
0190: protected DataValueDescriptor[][] fetchNext_one_slot_array = new DataValueDescriptor[1][];
0191:
0192: /* Constructors for This class: */
0193:
0194: public BTreeScan() {
0195: }
0196:
0197: /*
0198: ** Private/Protected methods of This class, sorted alphabetically
0199: */
0200:
0201: /**
0202: * Fetch the next N rows from the table.
0203: * <p>
0204: * Utility routine used by both fetchSet() and fetchNextGroup().
0205: *
0206: * @exception StandardException Standard exception policy.
0207: **/
0208: abstract protected int fetchRows(BTreeRowPosition pos,
0209: DataValueDescriptor[][] row_array,
0210: RowLocation[] rowloc_array,
0211: BackingStoreHashtable hash_table, long max_rowcnt,
0212: int[] key_column_numbers) throws StandardException;
0213:
0214: /**
0215: * Shared initialization code between init() and reopenScan().
0216: * <p>
0217: * Basically save away input parameters describing qualifications for
0218: * the scan, and do some error checking.
0219: *
0220: * @exception StandardException Standard exception policy.
0221: **/
0222: private void initScanParams(DataValueDescriptor[] startKeyValue,
0223: int startSearchOperator, Qualifier qualifier[][],
0224: DataValueDescriptor[] stopKeyValue, int stopSearchOperator)
0225: throws StandardException {
0226: // startKeyValue init.
0227: this .init_startKeyValue = startKeyValue;
0228: if (RowUtil.isRowEmpty(this .init_startKeyValue))
0229: this .init_startKeyValue = null;
0230:
0231: // startSearchOperator init.
0232: this .init_startSearchOperator = startSearchOperator;
0233:
0234: // qualifier init.
0235: if ((qualifier != null) && (qualifier.length == 0))
0236: qualifier = null;
0237: this .init_qualifier = qualifier;
0238:
0239: // stopKeyValue init.
0240: this .init_stopKeyValue = stopKeyValue;
0241: if (RowUtil.isRowEmpty(this .init_stopKeyValue))
0242: this .init_stopKeyValue = null;
0243:
0244: // stopSearchOperator init.
0245: this .init_stopSearchOperator = stopSearchOperator;
0246:
0247: // reset the "current" position to starting condition.
0248: // RESOLVE (mmm) - "compile" this.
0249: scan_position = new BTreeRowPosition();
0250:
0251: scan_position.init();
0252:
0253: scan_position.current_lock_template = new DataValueDescriptor[this .init_template.length];
0254:
0255: scan_position.current_lock_template[this .init_template.length - 1] = scan_position.current_lock_row_loc = (RowLocation) ((RowLocation) init_template[init_template.length - 1])
0256: .cloneObject();
0257:
0258: // Verify that all columns in start key value, stop key value, and
0259: // qualifiers are present in the list of columns described by the
0260: // scanColumnList.
0261: if (SanityManager.DEBUG) {
0262: if (init_scanColumnList != null) {
0263: // verify that all columns specified in qualifiers, start
0264: // and stop positions are specified in the scanColumnList.
0265:
0266: FormatableBitSet required_cols;
0267:
0268: if (qualifier != null)
0269: required_cols = RowUtil
0270: .getQualifierBitSet(qualifier);
0271: else
0272: required_cols = new FormatableBitSet(0);
0273:
0274: // add in start columns
0275: if (this .init_startKeyValue != null) {
0276: required_cols.grow(this .init_startKeyValue.length);
0277: for (int i = 0; i < this .init_startKeyValue.length; i++)
0278: required_cols.set(i);
0279: }
0280:
0281: if (this .init_stopKeyValue != null) {
0282: required_cols.grow(this .init_stopKeyValue.length);
0283: for (int i = 0; i < this .init_stopKeyValue.length; i++)
0284: required_cols.set(i);
0285: }
0286:
0287: FormatableBitSet required_cols_and_scan_list = (FormatableBitSet) required_cols
0288: .clone();
0289:
0290: required_cols_and_scan_list.and(init_scanColumnList);
0291:
0292: // FormatableBitSet equals requires the two FormatableBitSets to be of same
0293: // length.
0294: required_cols.grow(init_scanColumnList.size());
0295:
0296: if (!required_cols_and_scan_list.equals(required_cols)) {
0297: SanityManager
0298: .THROWASSERT("Some column specified in a Btree "
0299: + " qualifier/start/stop list is "
0300: + "not represented in the scanColumnList."
0301: + "\n:required_cols_and_scan_list = "
0302: + required_cols_and_scan_list
0303: + "\n;required_cols = "
0304: + required_cols
0305: + "\n;init_scanColumnList = "
0306: + init_scanColumnList);
0307: }
0308: }
0309: }
0310: }
0311:
0312: /**
0313: * Position scan at "start" position for a forward scan.
0314: * <p>
0315: * Positions the scan to the slot just before the first record to be
0316: * returned from the scan. Returns the start page latched, and
0317: * sets "current_slot" to the slot number.
0318: * <p>
0319: *
0320: * @exception StandardException Standard exception policy.
0321: **/
0322: protected void positionAtStartForForwardScan(BTreeRowPosition pos)
0323: throws StandardException {
0324: boolean exact;
0325:
0326: // This routine should only be called from first next() call //
0327: if (SanityManager.DEBUG) {
0328: SanityManager.ASSERT((scan_state == SCAN_INIT)
0329: || (scan_state == SCAN_HOLD_INIT));
0330: SanityManager.ASSERT(pos.current_rh == null);
0331: SanityManager.ASSERT(pos.current_positionKey == null);
0332: SanityManager.ASSERT(pos.current_scan_pageno == 0);
0333: }
0334:
0335: // Loop until you can lock the row previous to the first row to be
0336: // returned by the scan, while holding the page latched, without
0337: // waiting. If you have to wait, drop the latch, wait for the lock -
0338: // which makes it likely if you wait for the lock you will loop just
0339: // once, find the same lock satisfies the search and since you already
0340: // have the lock it will be granted.
0341: while (true) {
0342: // Find the starting page and row slot, must start at root and
0343: // search either for leftmost leaf, or search for specific key.
0344: ControlRow root = ControlRow.Get(this , BTree.ROOTPAGEID);
0345:
0346: // include search of tree in page visited stats.
0347: stat_numpages_visited += root.getLevel() + 1;
0348:
0349: boolean need_previous_lock = true;
0350:
0351: if (init_startKeyValue == null) {
0352: // No start given, so position at 0 slot of leftmost leaf page
0353: pos.current_leaf = (LeafControlRow) root
0354: .searchLeft(this );
0355:
0356: pos.current_slot = ControlRow.CR_SLOT;
0357: exact = false;
0358: } else {
0359: // Search for the starting row.
0360:
0361: if (SanityManager.DEBUG)
0362: SanityManager
0363: .ASSERT((init_startSearchOperator == ScanController.GE)
0364: || (init_startSearchOperator == ScanController.GT));
0365:
0366: SearchParameters sp = new SearchParameters(
0367: init_startKeyValue,
0368: ((init_startSearchOperator == ScanController.GE) ? SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH
0369: : SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH),
0370: init_template, this , false);
0371:
0372: pos.current_leaf = (LeafControlRow) root.search(sp);
0373:
0374: pos.current_slot = sp.resultSlot;
0375: exact = sp.resultExact;
0376:
0377: // The way that scans are used, the caller calls next()
0378: // to position on the first row. If the result of the
0379: // search that found the starting page and slot was not
0380: // exact, then the page/slot will refer to the row before
0381: // the first qualifying row. The first call to next()
0382: // will therefore move to the first (potentially) qualifying
0383: // row. However, if the search was exact, then we don't
0384: // want to move the position on the first call to next.
0385: // In that case, by decrementing the slot, the first call
0386: // to next will put us back on the starting row.
0387:
0388: if (exact
0389: && init_startSearchOperator == ScanController.GE) {
0390: pos.current_slot--;
0391:
0392: // A scan on a unique index, with a start position of
0393: // GE, need not get a previous key lock to protect the
0394: // range. Since it is unique no other key can go before
0395: // the first row returned from the scan.
0396: //
0397: // RESOLVE - currently btree's only support allowDuplicates
0398: // of "false", so no need to do the extra check, current
0399: // btree implementation depends on RowLocation field
0400: // making every key unique (duplicate indexes are supported
0401: // by the nUniqueColumns and nKeyFields).
0402: if (getConglomerate().nUniqueColumns < getConglomerate().nKeyFields) {
0403: // this implies unique index, thus no prev key.
0404: need_previous_lock = false;
0405: }
0406: }
0407: }
0408:
0409: boolean latch_released = false;
0410: if (need_previous_lock) {
0411: latch_released = !this .getLockingPolicy().lockScanRow(
0412: this , this .getConglomerate(), pos, true,
0413: init_lock_fetch_desc,
0414: pos.current_lock_template,
0415: pos.current_lock_row_loc, true, init_forUpdate,
0416: lock_operation);
0417: } else {
0418: // Don't need to lock the "previous key" but still need to get
0419: // the scan lock to protect the position in the btree.
0420:
0421: latch_released = !this .getLockingPolicy().lockScan(
0422: pos.current_leaf, // the page we are positioned on.
0423: (ControlRow) null, // no other page to unlatch
0424: false, // lock for read.
0425: lock_operation); // not used.
0426: }
0427:
0428: // special test to see if latch release code works
0429: if (SanityManager.DEBUG) {
0430: latch_released = test_errors(this ,
0431: "BTreeScan_positionAtStartPosition", true, this
0432: .getLockingPolicy(), pos.current_leaf,
0433: latch_released);
0434: }
0435:
0436: if (latch_released) {
0437: // lost latch on pos.current_leaf, search the tree again.
0438: pos.current_leaf = null;
0439: continue;
0440: } else {
0441: // success! got all the locks, while holding the latch.
0442: break;
0443: }
0444: }
0445:
0446: this .scan_state = SCAN_INPROGRESS;
0447: pos.current_scan_pageno = pos.current_leaf.page.getPageNumber();
0448: pos.current_slot = pos.current_slot;
0449:
0450: if (SanityManager.DEBUG)
0451: SanityManager.ASSERT(pos.current_leaf != null);
0452: }
0453:
0454: /**
0455: * Position scan at "start" position for a backward scan.
0456: * <p>
0457: * Positions the scan to the slot just after the first record to be
0458: * returned from the backward scan. Returns the start page latched, and
0459: * sets "current_slot" to the slot number just right of the first slot
0460: * to return.
0461: * <p>
0462: *
0463: * @exception StandardException Standard exception policy.
0464: **/
0465: protected void positionAtStartForBackwardScan(BTreeRowPosition pos)
0466: throws StandardException {
0467: boolean exact;
0468:
0469: // This routine should only be called from first next() call //
0470: if (SanityManager.DEBUG) {
0471: SanityManager.ASSERT((this .scan_state == SCAN_INIT)
0472: || (this .scan_state == SCAN_HOLD_INIT));
0473:
0474: SanityManager.ASSERT(pos.current_rh == null);
0475: SanityManager.ASSERT(pos.current_positionKey == null);
0476: SanityManager.ASSERT(pos.current_scan_pageno == 0);
0477: }
0478:
0479: // Loop until you can lock the row previous to the first row to be
0480: // returned by the scan, while holding the page latched, without
0481: // waiting. If you have to wait, drop the latch, wait for the lock -
0482: // which makes it likely if you wait for the lock you will loop just
0483: // once, find the same lock satisfies the search and since you already
0484: // have the lock it will be granted.
0485: while (true) {
0486: // Find the starting page and row slot, must start at root and
0487: // search either for leftmost leaf, or search for specific key.
0488: ControlRow root = ControlRow.Get(this , BTree.ROOTPAGEID);
0489:
0490: // include search of tree in page visited stats.
0491: stat_numpages_visited += root.getLevel() + 1;
0492:
0493: if (init_startKeyValue == null) {
0494: // No start given, position at last slot + 1 of rightmost leaf
0495: pos.current_leaf = (LeafControlRow) root
0496: .searchRight(this );
0497:
0498: pos.current_slot = pos.current_leaf.page.recordCount();
0499: exact = false;
0500: } else {
0501: /*
0502: if (SanityManager.DEBUG)
0503: SanityManager.THROWASSERT(
0504: "Code not ready yet for positioned backward scans.");
0505: */
0506:
0507: if (SanityManager.DEBUG)
0508: SanityManager
0509: .ASSERT((init_startSearchOperator == ScanController.GE)
0510: || (init_startSearchOperator == ScanController.GT));
0511:
0512: // Search for the starting row.
0513:
0514: SearchParameters sp = new SearchParameters(
0515: init_startKeyValue,
0516: ((init_startSearchOperator == ScanController.GE) ? SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH
0517: : SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH),
0518: init_template, this , false);
0519:
0520: pos.current_leaf = (LeafControlRow) root.search(sp);
0521:
0522: pos.current_slot = sp.resultSlot;
0523: exact = sp.resultExact;
0524:
0525: // The way that backward scans are used, the caller calls next()
0526: // to position on the first row. If the result of the
0527: // search that found the starting page and slot was not
0528: // exact, then the page/slot will refer to the row before
0529: // the first qualifying row. The first call to next()
0530: // will therefore move to the first (potentially) qualifying
0531: // row. However, if the search was exact, then we don't
0532: // want to move the position on the first call to next.
0533: // In that case, by decrementing the slot, the first call
0534: // to next will put us back on the starting row.
0535:
0536: if (exact) {
0537: // the search has found exactly the start position key
0538: if (init_startSearchOperator == ScanController.GE) {
0539: // insure backward scan returns this row by moving
0540: // slot to one after this row.
0541: pos.current_slot++;
0542: } else {
0543: // no work necessary leave startslot positioned on the
0544: // row, we will skip this record
0545: if (SanityManager.DEBUG)
0546: SanityManager
0547: .ASSERT(init_startSearchOperator == ScanController.GT);
0548: }
0549: } else {
0550: // the search positioned one before the start position key,
0551: // move it to one "after"
0552: pos.current_slot++;
0553: }
0554: }
0555:
0556: boolean latch_released = !this .getLockingPolicy()
0557: .lockScanRow(this , this .getConglomerate(), pos,
0558: true, init_lock_fetch_desc,
0559: pos.current_lock_template,
0560: pos.current_lock_row_loc, true,
0561: init_forUpdate, lock_operation);
0562:
0563: // special test to see if latch release code works
0564: if (SanityManager.DEBUG) {
0565: latch_released = test_errors(this ,
0566: "BTreeScan_positionAtStartPosition", true, this
0567: .getLockingPolicy(), pos.current_leaf,
0568: latch_released);
0569: }
0570:
0571: if (latch_released) {
0572: // lost latch on pos.current_leaf, search the tree again.
0573: pos.current_leaf = null;
0574: continue;
0575: } else {
0576: // success! got all the locks, while holding the latch.
0577: break;
0578: }
0579: }
0580:
0581: this .scan_state = SCAN_INPROGRESS;
0582: pos.current_scan_pageno = pos.current_leaf.page.getPageNumber();
0583:
0584: if (SanityManager.DEBUG)
0585: SanityManager.ASSERT(pos.current_leaf != null);
0586:
0587: // System.out.println("backward scan end start position: " +
0588: // " current_slot = " + this.current_slot );
0589: }
0590:
0591: /**
0592: * Position scan to 0 slot on next page.
0593: * <p>
0594: * Position to next page, keeping latch on previous page until we have
0595: * latch on next page. This routine releases the latch on current_page
0596: * once it has successfully gotten both the latch on the next page and
0597: * the scan lock on the next page.
0598: *
0599: * @param pos current row position of the scan.
0600: *
0601: * @exception StandardException Standard exception policy.
0602: **/
0603: protected void positionAtNextPage(BTreeRowPosition pos)
0604: throws StandardException {
0605: // RESOLVE (mikem) - not sure but someday in the future this
0606: // assert may not be true, but for now we always have the scan
0607: // lock when we call this routine.
0608: if (SanityManager.DEBUG)
0609: SanityManager.ASSERT(pos.current_scan_pageno != 0);
0610:
0611: while (true) {
0612: if ((pos.next_leaf = (LeafControlRow) pos.current_leaf
0613: .getRightSibling(this )) == null) {
0614: break;
0615: }
0616:
0617: boolean latch_released = !this .getLockingPolicy().lockScan(
0618: pos.next_leaf,
0619: (LeafControlRow) null, // no other latch currently
0620: false /* not for update */,
0621: ConglomerateController.LOCK_READ); // get read scan lock.
0622:
0623: // TESTING CODE:
0624: if (SanityManager.DEBUG) {
0625: latch_released = test_errors(this ,
0626: "BTreeScan_positionAtNextPage", true, this
0627: .getLockingPolicy(), pos.next_leaf,
0628: latch_released);
0629: }
0630:
0631: if (!latch_released) {
0632: break;
0633: }
0634: }
0635:
0636: // Now that we either have both latch and scan lock on next leaf, or
0637: // there is no next leaf we can release scan and latch on current page.
0638: if (SanityManager.DEBUG) {
0639: if (pos.current_scan_pageno != pos.current_leaf.page
0640: .getPageNumber())
0641: SanityManager.THROWASSERT("pos.current_scan_pageno = "
0642: + pos.current_scan_pageno
0643: + "pos.current_leaf = " + pos.current_leaf);
0644: }
0645:
0646: // unlock the previous row if doing read.
0647: if (pos.current_rh != null) {
0648: this .getLockingPolicy().unlockScanRecordAfterRead(pos,
0649: init_forUpdate);
0650: }
0651:
0652: this .getLockingPolicy().unlockScan(
0653: pos.current_leaf.page.getPageNumber());
0654: pos.current_leaf.release();
0655: pos.current_leaf = pos.next_leaf;
0656:
0657: pos.current_scan_pageno = (pos.next_leaf == null) ? 0
0658: : pos.next_leaf.page.getPageNumber();
0659:
0660: // set up for scan to continue at beginning of next page.
0661: pos.current_slot = Page.FIRST_SLOT_NUMBER;
0662: pos.current_rh = null;
0663: }
0664:
0665: /**
0666: Position scan at "start" position.
0667: <p>
0668: Positions the scan to the slot just before the first record to be returned
0669: from the scan. Returns the start page latched, and sets "current_slot" to
0670: the slot number.
0671:
0672: @exception StandardException Standard exception policy.
0673: **/
0674: abstract void positionAtStartPosition(BTreeRowPosition pos)
0675: throws StandardException;
0676:
0677: /**
0678: * Do any necessary work to complete the scan.
0679: *
0680: * @param pos current row position of the scan.
0681: *
0682: * @exception StandardException Standard exception policy.
0683: **/
0684: protected void positionAtDoneScanFromClose(BTreeRowPosition pos)
0685: throws StandardException {
0686: // call unlockScanRecordAfterRead() before closing, currently
0687: // this is only important for releasing RR locks on non-qualified
0688: // rows.
0689: //
0690: // Otherwise the correct behavior happens as part of the close, ie.:
0691: //
0692: // for READ_UNCOMMITTED there is no lock to release,
0693: // for READ_COMMITTED all read locks will be released,
0694: // for REPEATABLE_READ or SERIALIZABLE no locks are released.
0695:
0696: if ((pos.current_rh != null) && !pos.current_rh_qualified) {
0697: if (pos.current_leaf == null
0698: || pos.current_leaf.page == null) {
0699: // If we are being called from a "normal" close then there
0700: // will be no latch on current_leaf, get it and do the the
0701: // unlock. We may be called sometimes, after an error where
0702: // we may have the latch, in this case the transaction is about
0703: // to be backed out anyway so don't worry about doing this
0704: // unlock (thus why we only do the following code if we
0705: // "don't" have lock, ie. pos.current_leaf== null).
0706:
0707: if (!reposition(pos, false)) {
0708: if (SanityManager.DEBUG) {
0709: SanityManager
0710: .THROWASSERT("can not fail while holding update row lock.");
0711: }
0712: }
0713:
0714: this .getLockingPolicy().unlockScanRecordAfterRead(pos,
0715: init_forUpdate);
0716:
0717: pos.current_rh = null;
0718: pos.current_leaf.release();
0719: pos.current_leaf = null;
0720: }
0721: }
0722:
0723: // Need to do this unlock in any case, until lock manager provides
0724: // a way to release locks associated with a compatibility space. This
0725: // scan lock is special, as it is a lock on the btree container rather
0726: // than the heap container. The open container on the btree actually
0727: // has a null locking policy so the close of that container does not
0728: // release this lock, need to explicitly unlock it here or when the
0729: // scan is closed as part of the abort the lock will not be released.
0730: if (pos.current_scan_pageno != 0) {
0731: this .getLockingPolicy().unlockScan(pos.current_scan_pageno);
0732: pos.current_scan_pageno = 0;
0733: }
0734:
0735: pos.current_slot = Page.INVALID_SLOT_NUMBER;
0736: pos.current_rh = null;
0737: pos.current_positionKey = null;
0738: this .scan_state = SCAN_DONE;
0739:
0740: return;
0741: }
0742:
0743: /**
0744: * Do work necessary to close a scan.
0745: * <p>
0746: * This routine can only be called "inline" from other btree routines,
0747: * as it counts on the state of the pos to be correct.
0748: * <p>
0749: * Closing a scan from close() must handle long jumps from exceptions
0750: * where the state of pos may not be correct. The easiest case is
0751: * a lock timeout which has caused us not to have a latch on a page,
0752: * but pos still thinks there is a latch. This is the easiest but
0753: * other exceptions can also caused the same state at close() time.
0754: **/
0755: protected void positionAtDoneScan(BTreeRowPosition pos)
0756: throws StandardException {
0757:
0758: // Need to do this unlock in any case, until lock manager provides
0759: // a way to release locks associated with a compatibility space. This
0760: // scan lock is special, as it is a lock on the btree container rather
0761: // than the heap container. The open container on the btree actually
0762: // has a null locking policy so the close of that container does not
0763: // release this lock, need to explicitly unlock it here or when the
0764: // scan is closed as part of the abort the lock will not be released.
0765: if (pos.current_scan_pageno != 0) {
0766: this .getLockingPolicy().unlockScan(pos.current_scan_pageno);
0767: pos.current_scan_pageno = 0;
0768: }
0769:
0770: pos.current_slot = Page.INVALID_SLOT_NUMBER;
0771: pos.current_rh = null;
0772: pos.current_positionKey = null;
0773: this .scan_state = SCAN_DONE;
0774:
0775: return;
0776: }
0777:
0778: /**
0779: * process_qualifier - Determine if a row meets all qualifier conditions.
0780: * <p>
0781: * Check all qualifiers in the qualifier array against row. Return true
0782: * if all compares specified by the qualifier array return true, else
0783: * return false.
0784: * <p>
0785: * It is up to caller to make sure qualifier list is non-null.
0786: *
0787: * @param row The row with the same partial column list as the
0788: * row returned by the current scan.
0789: *
0790: * @exception StandardException Standard exception policy.
0791: */
0792: protected boolean process_qualifier(DataValueDescriptor[] row)
0793: throws StandardException {
0794: boolean row_qualifies = true;
0795: Qualifier q;
0796:
0797: // Process the 2-d qualifier which is structured as follows:
0798: //
0799: // A two dimensional array is to be used to pass around a AND's and OR's
0800: // in conjunctive normal form (CNF). The top slot of the 2 dimensional
0801: // array is optimized for the more frequent where no OR's are present.
0802: // The first array slot is always a list of AND's to be treated as
0803: // described above for single dimensional AND qualifier arrays. The
0804: // subsequent slots are to be treated as AND'd arrays or OR's. Thus
0805: // the 2 dimensional array qual[][] argument is to be treated as the
0806: // following, note if qual.length = 1 then only the first array is
0807: // valid and // it is and an array of and clauses:
0808: //
0809: // (qual[0][0] and qual[0][0] ... and qual[0][qual[0].length - 1])
0810: // and
0811: // (qual[1][0] or qual[1][1] ... or qual[1][qual[1].length - 1])
0812: // and
0813: // (qual[2][0] or qual[2][1] ... or qual[2][qual[2].length - 1])
0814: // ...
0815: // and
0816: // (qual[qual.length - 1][0] or qual[1][1] ... or qual[1][2])
0817:
0818: // First do the qual[0] which is an array of qualifer terms.
0819:
0820: if (SanityManager.DEBUG) {
0821: // routine should not be called if there is no qualifier
0822: SanityManager.ASSERT(this .init_qualifier != null);
0823: SanityManager.ASSERT(this .init_qualifier.length > 0);
0824: }
0825:
0826: for (int i = 0; i < this .init_qualifier[0].length; i++) {
0827: // process each AND clause
0828:
0829: row_qualifies = false;
0830:
0831: // process each OR clause.
0832:
0833: q = this .init_qualifier[0][i];
0834:
0835: // Get the column from the possibly partial row, of the
0836: // q.getColumnId()'th column in the full row.
0837: DataValueDescriptor columnValue = row[q.getColumnId()];
0838:
0839: row_qualifies = columnValue.compare(q.getOperator(), q
0840: .getOrderable(), q.getOrderedNulls(), q
0841: .getUnknownRV());
0842:
0843: if (q.negateCompareResult())
0844: row_qualifies = !row_qualifies;
0845:
0846: // Once an AND fails the whole Qualification fails - do a return!
0847: if (!row_qualifies)
0848: return (false);
0849: }
0850:
0851: // all the qual[0] and terms passed, now process the OR clauses
0852:
0853: for (int and_idx = 1; and_idx < this .init_qualifier.length; and_idx++) {
0854: // process each AND clause
0855:
0856: row_qualifies = false;
0857:
0858: if (SanityManager.DEBUG) {
0859: // Each OR clause must be non-empty.
0860: SanityManager
0861: .ASSERT(this .init_qualifier[and_idx].length > 0);
0862: }
0863:
0864: for (int or_idx = 0; or_idx < this .init_qualifier[and_idx].length; or_idx++) {
0865: // process each OR clause.
0866:
0867: q = this .init_qualifier[and_idx][or_idx];
0868:
0869: // Get the column from the possibly partial row, of the
0870: // q.getColumnId()'th column in the full row.
0871: DataValueDescriptor columnValue = row[q.getColumnId()];
0872:
0873: row_qualifies = columnValue.compare(q.getOperator(), q
0874: .getOrderable(), q.getOrderedNulls(), q
0875: .getUnknownRV());
0876:
0877: if (q.negateCompareResult())
0878: row_qualifies = !row_qualifies;
0879:
0880: // once one OR qualifies the entire clause is TRUE
0881: if (row_qualifies)
0882: break;
0883: }
0884:
0885: if (!row_qualifies)
0886: break;
0887: }
0888:
0889: return (row_qualifies);
0890: }
0891:
0892: /**
0893: * Reposition the scan leaving and reentering the access layer.
0894: * <p>
0895: * When a scan leaves access it saves the RecordHandle of the record
0896: * on the page. There are 2 cases to consider when trying to reposition
0897: * the scan when re-entering access:
0898: * o ROW has not moved off the page.
0899: * If the row has not moved then the RecordHandle we have saved
0900: * away is valid, and we just call RawStore to reposition on that
0901: * RecordHandle (RawStore takes care of the row moving within
0902: * the page).
0903: * o ROW has moved off the page.
0904: * This can only happen in the case of a btree split. In that
0905: * case the splitter will have caused all scans positioned on
0906: * this page within the same transaction to save a copy of the
0907: * row that the scan was positioned on. Then to reposition the
0908: * scan it is necessary to research the tree from the top using
0909: * the copy of the row.
0910: *
0911: * If the scan has saved it's position by key (and thus has given up the
0912: * scan lock on the page), there are a few cases where it is possible that
0913: * the key no longer exists in the table. In the case of a scan held
0914: * open across commit it is easy to imagine that the row the scan was
0915: * positioned on could be deleted and subsequently purged from the table
0916: * all before the scan resumes. Also in the case of read uncommitted
0917: * the scan holds no lock on the current row, so it could be purged -
0918: * in the following scenario for instance: read uncommitted transaction 1
0919: * opens scan and positions on row (1,2), transaction 2 deletes (1,2) and
0920: * commits, transaction 1 inserts (1,3) which goes to same page as (1,2)
0921: * and is going to cause a split, transaction 1 saves scan position as
0922: * key, gives up scan lock and then purges row (1, 2), when transaction
0923: * 1 resumes scan (1, 2) no longer exists. missing_row_for_key_ok
0924: * parameter is added as a sanity check to make sure it ok that
0925: * repositioning does not go to same row that we were repositioned on.
0926: *
0927: *
0928: *
0929: * @param pos position to set the scan to.
0930: *
0931: * @param missing_row_for_key_ok if true and exact key is not found then
0932: * scan is just set to key just left of
0933: * the key (thus a next will move to the
0934: * key just after "pos")
0935: *
0936: * @return returns true if scan has been repositioned successfully, else
0937: * returns false if the position key could not be found and
0938: * missing_row_for_key_ok was false indicating that scan could
0939: * only be positioned on the exact key match.
0940: *
0941: * @exception StandardException Standard exception policy.
0942: **/
0943: protected boolean reposition(BTreeRowPosition pos,
0944: boolean missing_row_for_key_ok) throws StandardException {
0945: // RESOLVE (mikem) - performance - we need to do a buffer manager
0946: // get for every row returned from the scan. It may be better to
0947: // allow a reference to the page with no latch (ie. a fixed bit).
0948:
0949: if (this .scan_state != SCAN_INPROGRESS) {
0950: throw StandardException.newException(
0951: SQLState.BTREE_SCAN_NOT_POSITIONED, new Integer(
0952: this .scan_state));
0953: }
0954:
0955: // Either current_rh or positionKey is valid - the other is null.
0956: if (SanityManager.DEBUG) {
0957: if ((pos.current_rh == null) != (pos.current_positionKey != null))
0958: SanityManager.THROWASSERT("pos.current_rh = ("
0959: + pos.current_rh + "), "
0960: + "pos.current_positionKey = ("
0961: + pos.current_positionKey + ").");
0962: }
0963:
0964: if (!((pos.current_rh == null) == (pos.current_positionKey != null))) {
0965: throw StandardException.newException(
0966: SQLState.BTREE_SCAN_INTERNAL_ERROR, new Boolean(
0967: pos.current_rh == null), new Boolean(
0968: pos.current_positionKey == null));
0969: }
0970:
0971: if (pos.current_positionKey == null) {
0972: // Reposition to remembered spot on page.
0973: if (SanityManager.DEBUG)
0974: SanityManager.ASSERT(pos.current_scan_pageno != 0);
0975:
0976: pos.current_leaf = (LeafControlRow) ControlRow.Get(this ,
0977: pos.current_rh.getPageNumber());
0978: pos.current_slot = pos.current_leaf.page
0979: .getSlotNumber(pos.current_rh);
0980: } else {
0981: // RESOLVE (mikem) - not sure but someday in the future this
0982: // assert may not be true, but for now we always release the
0983: // scan lock when we save the row away as the current position.
0984: if (SanityManager.DEBUG)
0985: SanityManager.ASSERT(pos.current_scan_pageno == 0);
0986:
0987: SearchParameters sp = new SearchParameters(
0988: pos.current_positionKey,
0989: // this is a full key search, so this arg is not used.
0990: SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH,
0991: init_template, this , false);
0992:
0993: // latch/lock loop, continue until you can get scan lock on page
0994: // while holding page latched without waiting.
0995:
0996: boolean latch_released;
0997: do {
0998: pos.current_leaf = (LeafControlRow) ControlRow.Get(
0999: this , BTree.ROOTPAGEID).search(sp);
1000:
1001: if (sp.resultExact || missing_row_for_key_ok) {
1002: // RESOLVE (mikem) - we could have a scan which always
1003: // maintained it's position by key value, or we could
1004: // optimize and delay this lock until we were about to
1005: // give up the latch. But it is VERY likely we will get
1006: // the lock since we have the latch on the page.
1007: //
1008: // In order to be successfully positioned we must get the
1009: // scan lock again.
1010: latch_released = !this .getLockingPolicy().lockScan(
1011: pos.current_leaf,
1012: (LeafControlRow) null, // no other latch currently
1013: false /* not for update */,
1014: ConglomerateController.LOCK_READ); // read lock on scan position
1015:
1016: // TESTING CODE:
1017: if (SanityManager.DEBUG) {
1018: latch_released = test_errors(this ,
1019: "BTreeScan_reposition", true, this
1020: .getLockingPolicy(),
1021: pos.current_leaf, latch_released);
1022: }
1023: } else {
1024: // Did not find key to exactly position on.
1025:
1026: pos.current_leaf.release();
1027: pos.current_leaf = null;
1028: return (false);
1029: }
1030:
1031: } while (latch_released);
1032:
1033: pos.current_scan_pageno = pos.current_leaf.page
1034: .getPageNumber();
1035: pos.current_slot = sp.resultSlot;
1036: pos.current_positionKey = null;
1037: }
1038:
1039: return (true);
1040: }
1041:
1042: /*
1043: ** Public Methods of BTreeScan
1044: */
1045:
1046: /**
1047: Initialize the scan for use.
1048: <p>
1049: Any changes to this method may have to be reflected in close as well.
1050: <p>
1051: The btree init opens the container (super.init), and stores away the
1052: state of the qualifiers. The actual searching for the first position
1053: is delayed until the first next() call.
1054:
1055: @exception StandardException Standard exception policy.
1056: **/
1057: public void init(TransactionManager xact_manager,
1058: Transaction rawtran, boolean hold, int open_mode,
1059: int lock_level, BTreeLockingPolicy btree_locking_policy,
1060: FormatableBitSet scanColumnList,
1061: DataValueDescriptor[] startKeyValue,
1062: int startSearchOperator, Qualifier qualifier[][],
1063: DataValueDescriptor[] stopKeyValue, int stopSearchOperator,
1064: BTree conglomerate, LogicalUndo undo,
1065: StaticCompiledOpenConglomInfo static_info,
1066: DynamicCompiledOpenConglomInfo dynamic_info)
1067: throws StandardException {
1068: super .init(xact_manager, xact_manager, (ContainerHandle) null,
1069: rawtran, hold, open_mode, lock_level,
1070: btree_locking_policy, conglomerate, undo, dynamic_info);
1071:
1072: this .init_rawtran = rawtran;
1073: this .init_forUpdate = ((open_mode & ContainerHandle.MODE_FORUPDATE) == ContainerHandle.MODE_FORUPDATE);
1074:
1075: // Keep track of whether this scan should use update locks.
1076: this .init_useUpdateLocks = ((open_mode & ContainerHandle.MODE_USE_UPDATE_LOCKS) != 0);
1077:
1078: this .init_hold = hold;
1079:
1080: this .init_template = runtime_mem.get_template();
1081:
1082: this .init_scanColumnList = scanColumnList;
1083:
1084: this .init_lock_fetch_desc = RowUtil
1085: .getFetchDescriptorConstant(init_template.length - 1);
1086:
1087: if (SanityManager.DEBUG) {
1088: SanityManager
1089: .ASSERT(init_lock_fetch_desc.getMaxFetchColumnId() == (init_template.length - 1));
1090: SanityManager
1091: .ASSERT((init_lock_fetch_desc
1092: .getValidColumnsArray())[init_template.length - 1] == 1);
1093: }
1094:
1095: // note that we don't process qualifiers in btree fetch's
1096: this .init_fetchDesc = new FetchDescriptor(init_template.length,
1097: init_scanColumnList, (Qualifier[][]) null);
1098:
1099: initScanParams(startKeyValue, startSearchOperator, qualifier,
1100: stopKeyValue, stopSearchOperator);
1101:
1102: if (SanityManager.DEBUG) {
1103: // RESOLVE - (mikem) we should we require a template, need to
1104: // clean up some of the old tests which did not provide one?
1105: if (init_template != null) {
1106: SanityManager.ASSERT(TemplateRow.checkColumnTypes(this
1107: .getConglomerate().format_ids, init_template));
1108: }
1109: }
1110:
1111: // System.out.println("initializing scan:" + this);
1112:
1113: // initialize default locking operation for the scan.
1114: this .lock_operation = (init_forUpdate ? ConglomerateController.LOCK_UPD
1115: : ConglomerateController.LOCK_READ);
1116:
1117: if (init_useUpdateLocks)
1118: this .lock_operation |= ConglomerateController.LOCK_UPDATE_LOCKS;
1119:
1120: // System.out.println("Btree scan: " + this);
1121: }
1122:
1123: /*
1124: ** Methods of ScanController
1125: */
1126:
1127: /**
1128: Close the scan.
1129: **/
1130: public void close() throws StandardException {
1131: // Scan is closed, make sure no access to any state variables
1132: positionAtDoneScanFromClose(scan_position);
1133:
1134: super .close();
1135:
1136: // null out so that these object's can get GC'd earlier.
1137: this .init_rawtran = null;
1138: this .init_template = null;
1139: this .init_startKeyValue = null;
1140: this .init_qualifier = null;
1141: this .init_stopKeyValue = null;
1142:
1143: this .getXactMgr().closeMe(this );
1144: }
1145:
1146: /**
1147: Delete the row at the current position of the scan.
1148: @see ScanController#delete
1149:
1150: @exception StandardException Standard exception policy.
1151: **/
1152: public boolean delete() throws StandardException {
1153: boolean ret_val = false;
1154:
1155: if (scan_state != SCAN_INPROGRESS)
1156: throw StandardException
1157: .newException(SQLState.AM_SCAN_NOT_POSITIONED);
1158:
1159: if (SanityManager.DEBUG) {
1160: SanityManager.ASSERT(this .container != null,
1161: "BTreeScan.delete() called on a closed scan.");
1162: SanityManager.ASSERT(init_forUpdate);
1163: }
1164:
1165: try {
1166: // Get current page of scan, with latch.
1167: if (!reposition(scan_position, false)) {
1168: throw StandardException.newException(
1169: SQLState.AM_RECORD_NOT_FOUND, new Long(
1170: err_containerid), new Long(
1171: scan_position.current_rh.getId()));
1172: }
1173:
1174: if (init_useUpdateLocks) {
1175: // RESOLVE (mikem) - I don't think lockScanRow() is the right
1176: // thing to call.
1177:
1178: // if we are doing update locking, then we got an U lock on
1179: // this row when the scan positioned on it, but now that we
1180: // are doing a delete on the current position we need to upgrade
1181: // the lock to X.
1182: boolean latch_released = !this .getLockingPolicy()
1183: .lockScanRow(this , this .getConglomerate(),
1184: scan_position, false,
1185: init_lock_fetch_desc,
1186: scan_position.current_lock_template,
1187: scan_position.current_lock_row_loc,
1188: false, init_forUpdate, lock_operation);
1189:
1190: if (latch_released) {
1191: // lost latch on page in order to wait for row lock.
1192: // Because we have scan lock on page, we need only
1193: // call reposition() which will use the saved record
1194: // handle to reposition to the same spot on the page.
1195: // We don't have to search the
1196: // tree again, as we have the a scan lock on the page
1197: // which means the current_rh is valid to reposition on.
1198: if (reposition(scan_position, false)) {
1199: throw StandardException.newException(
1200: SQLState.AM_RECORD_NOT_FOUND, new Long(
1201: err_containerid), new Long(
1202: scan_position.current_rh
1203: .getId()));
1204: }
1205: }
1206: }
1207:
1208: // Do a fetch just to get the RecordHandle for the delete call,
1209: // don't fetch any columns.
1210: RecordHandle delete_rh = scan_position.current_leaf.page
1211: .fetchFromSlot((RecordHandle) null,
1212: scan_position.current_slot,
1213: RowUtil.EMPTY_ROW, (FetchDescriptor) null,
1214: true);
1215:
1216: ret_val = scan_position.current_leaf.page.delete(delete_rh,
1217: this .btree_undo);
1218:
1219: // See if we just deleted the last row on the page, in a btree a
1220: // page with all rows still has 1 left - the control row.
1221: // Beetle 5750: we do not reclaim the root page of the btree if
1222: // there are no children since we were
1223: // doing too many post commit actions in a benchmark which does an
1224: // insert/commit/delete/commit operations in a single user system. now ,
1225: // with this change the work will move to the user
1226: // thread which does the insert
1227:
1228: if (scan_position.current_leaf.page.nonDeletedRecordCount() == 1
1229: && !(scan_position.current_leaf.getIsRoot() && scan_position.current_leaf
1230: .getLevel() == 0)) {
1231: this .getXactMgr().addPostCommitWork(
1232: new BTreePostCommit(this .getXactMgr()
1233: .getAccessManager(), this
1234: .getConglomerate(),
1235: scan_position.current_leaf.page
1236: .getPageNumber()));
1237: }
1238: } finally {
1239: if (scan_position.current_leaf != null) {
1240: // release latch on page
1241: scan_position.current_leaf.release();
1242: scan_position.current_leaf = null;
1243: }
1244: }
1245:
1246: return (ret_val);
1247: }
1248:
1249: /**
1250: * A call to allow client to indicate that current row does not qualify.
1251: * <p>
1252: * Indicates to the ScanController that the current row does not
1253: * qualify for the scan. If the isolation level of the scan allows,
1254: * this may result in the scan releasing the lock on this row.
1255: * <p>
1256: * Note that some scan implimentations may not support releasing locks on
1257: * non-qualifying rows, or may delay releasing the lock until sometime
1258: * later in the scan (ie. it may be necessary to keep the lock until
1259: * either the scan is repositioned on the next row or page).
1260: * <p>
1261: * This call should only be made while the scan is positioned on a current
1262: * valid row.
1263: *
1264: * @exception StandardException Standard exception policy.
1265: **/
1266: public void didNotQualify() throws StandardException {
1267: }
1268:
1269: /**
1270: * Returns true if the current position of the scan still qualifies
1271: * under the set of qualifiers passed to the openScan(). When called
1272: * this routine will reapply all qualifiers against the row currently
1273: * positioned and return true if the row still qualifies. If the row
1274: * has been deleted or no longer passes the qualifiers then this routine
1275: * will return false.
1276: * <p>
1277: * This case can come about if the current scan
1278: * or another scan on the same table in the same transaction
1279: * deleted the row or changed columns referenced by the qualifier after
1280: * the next() call which positioned the scan at this row.
1281: * <p>
1282: * Note that for comglomerates which don't support update, like btree's,
1283: * there is no need to recheck the qualifiers.
1284: * <p>
1285: * The results of a fetch() performed on a scan positioned on
1286: * a deleted row are undefined.
1287: * <p>
1288: * @exception StandardException Standard exception policy.
1289: **/
1290: public boolean doesCurrentPositionQualify()
1291: throws StandardException {
1292: if (scan_state != SCAN_INPROGRESS)
1293: throw StandardException
1294: .newException(SQLState.AM_SCAN_NOT_POSITIONED);
1295:
1296: if (SanityManager.DEBUG) {
1297: SanityManager
1298: .ASSERT(this .container != null,
1299: "BTreeScan.doesCurrentPositionQualify() called on a closed scan.");
1300: }
1301:
1302: try {
1303: // Get current page of scan, with latch
1304: if (!reposition(scan_position, false)) {
1305: // TODO - write unit test to get here, language always calls
1306: // isCurrentPositionDeleted() right before calling this, so
1307: // hard to write .sql test to exercise this.
1308:
1309: // if reposition fails it means the position of the scan
1310: // has been purged from the table - for example if this is
1311: // a uncommitted read scan and somehow the row was purged
1312: // since the last positioning.
1313:
1314: return (false);
1315: }
1316:
1317: if (SanityManager.DEBUG) {
1318: SanityManager
1319: .ASSERT(scan_position.current_leaf.page
1320: .fetchNumFieldsAtSlot(scan_position.current_slot) > 1);
1321: }
1322:
1323: // Since btree row don't get updated, the only way a current
1324: // position may not qualify is if it got deleted.
1325: return (!scan_position.current_leaf.page
1326: .isDeletedAtSlot(scan_position.current_slot));
1327: } finally {
1328:
1329: if (scan_position.current_leaf != null) {
1330: // release latch on page.
1331: scan_position.current_leaf.release();
1332: scan_position.current_leaf = null;
1333: }
1334: }
1335: }
1336:
1337: /**
1338: * Fetch the row at the current position of the Scan.
1339: *
1340: * @param row The row into which the value of the current
1341: * position in the scan is to be stored.
1342: * @param qualify indicates whether the qualifiers should be applied.
1343: *
1344: * @exception StandardException Standard exception policy.
1345: */
1346: private void fetch(DataValueDescriptor[] row, boolean qualify)
1347: throws StandardException {
1348: if (scan_state != SCAN_INPROGRESS)
1349: throw StandardException
1350: .newException(SQLState.AM_SCAN_NOT_POSITIONED);
1351: if (SanityManager.DEBUG) {
1352: SanityManager.ASSERT(this .container != null,
1353: "BTreeScan.fetch() called on a closed scan.");
1354:
1355: TemplateRow.checkPartialColumnTypes(
1356: this .getConglomerate().format_ids,
1357: init_scanColumnList, (int[]) null, row);
1358: }
1359:
1360: try {
1361: // Get current page of scan, with latch
1362: if (!reposition(scan_position, false)) {
1363: // TODO - write unit test to get here, language always calls
1364: // isCurrentPositionDeleted() right before calling this, so
1365: // hard to write .sql test to exercise this.
1366:
1367: throw StandardException.newException(
1368: SQLState.AM_RECORD_NOT_FOUND, new Long(
1369: err_containerid), new Long(
1370: scan_position.current_rh.getId()));
1371: }
1372:
1373: if (SanityManager.DEBUG) {
1374: SanityManager
1375: .ASSERT(scan_position.current_leaf.page
1376: .fetchNumFieldsAtSlot(scan_position.current_slot) > 1);
1377: }
1378:
1379: scan_position.current_rh = scan_position.current_leaf.page
1380: .fetchFromSlot((RecordHandle) null,
1381: scan_position.current_slot, row,
1382: qualify ? init_fetchDesc : null, true);
1383:
1384: // The possibility is that the row at the current position
1385: // has been marked as deleted (it cannot have been purged
1386: // since the scan maintains a lock on the row, and purges
1387: // are always done from system transactions). I'm not sure
1388: // what the desired behavior is in this case. For now,
1389: // just return null.
1390:
1391: // RESOLVE (mikem) - what should be done here?
1392: if (scan_position.current_leaf.page
1393: .isDeletedAtSlot(scan_position.current_slot)) {
1394: if (SanityManager.DEBUG)
1395: SanityManager.ASSERT(false,
1396: "positioned on deleted row");
1397: }
1398: } finally {
1399: if (scan_position.current_leaf != null) {
1400: // release latch on page.
1401: scan_position.current_leaf.release();
1402: scan_position.current_leaf = null;
1403: }
1404: }
1405:
1406: return;
1407: }
1408:
1409: /**
1410: Fetch the row at the current position of the Scan.
1411: @see ScanController#fetch
1412:
1413: @exception StandardException Standard exception policy.
1414: **/
1415: public void fetch(DataValueDescriptor[] row)
1416: throws StandardException {
1417: fetch(row, true);
1418: }
1419:
1420: /**
1421: * Fetch the row at the current position of the Scan without applying the
1422: * qualifiers.
1423: * @see ScanController#fetchWithoutQualify
1424: *
1425: * @exception StandardException Standard exception policy.
1426: */
1427: public void fetchWithoutQualify(DataValueDescriptor[] row)
1428: throws StandardException {
1429: fetch(row, false);
1430: }
1431:
1432: /**
1433: * Return ScanInfo object which describes performance of scan.
1434: * <p>
1435: * Return ScanInfo object which contains information about the current
1436: * scan.
1437: * <p>
1438: *
1439: * @see ScanInfo
1440: *
1441: * @return The ScanInfo object which contains info about current scan.
1442: *
1443: * @exception StandardException Standard exception policy.
1444: **/
1445: public ScanInfo getScanInfo() throws StandardException {
1446: return (new BTreeScanInfo(this ));
1447: }
1448:
1449: /**
1450: Returns true if the current position of the scan is at a
1451: deleted row. This case can come about if the current scan
1452: or another scan on the same table in the same transaction
1453: deleted the row after the next() call which positioned the
1454: scan at this row.
1455:
1456: The results of a fetch() performed on a scan positioned on
1457: a deleted row are undefined.
1458:
1459: @exception StandardException Standard exception policy.
1460: **/
1461: public boolean isCurrentPositionDeleted() throws StandardException {
1462: boolean ret_val;
1463:
1464: if (scan_state != SCAN_INPROGRESS)
1465: throw StandardException
1466: .newException(SQLState.AM_SCAN_NOT_POSITIONED);
1467:
1468: if (SanityManager.DEBUG) {
1469: SanityManager
1470: .ASSERT(this .container != null,
1471: "BTreeScan.isCurrentPositionDeleted() called on closed scan.");
1472: }
1473: try {
1474: // Get current page of scan, with latch
1475:
1476: if (reposition(scan_position, false)) {
1477:
1478: if (SanityManager.DEBUG) {
1479: SanityManager
1480: .ASSERT(scan_position.current_leaf.page
1481: .fetchNumFieldsAtSlot(scan_position.current_slot) > 1);
1482: }
1483:
1484: ret_val = scan_position.current_leaf.page
1485: .isDeletedAtSlot(scan_position.current_slot);
1486: } else {
1487: ret_val = false;
1488: }
1489: } finally {
1490: if (scan_position.current_leaf != null) {
1491: // release latch on page.
1492: scan_position.current_leaf.release();
1493: scan_position.current_leaf = null;
1494: }
1495: }
1496:
1497: return (ret_val);
1498: }
1499:
1500: /**
1501: * Return whether this is a keyed conglomerate.
1502: * <p>
1503: *
1504: * @return whether this is a keyed conglomerate.
1505: **/
1506: public boolean isKeyed() {
1507: return (true);
1508: }
1509:
1510: /**
1511: * @see ScanController#positionAtRowLocation
1512: *
1513: * Not implemented for this class
1514: */
1515: public boolean positionAtRowLocation(RowLocation rLoc)
1516: throws StandardException {
1517: throw StandardException
1518: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1519: }
1520:
1521: /**
1522: Move to the next position in the scan.
1523: @see ScanController#next
1524:
1525: @exception StandardException Standard exception policy.
1526: **/
1527: public boolean next() throws StandardException {
1528: // Turn this call into a group fetch of a 1 element group.
1529: fetchNext_one_slot_array[0] = runtime_mem.get_scratch_row();
1530: boolean ret_val = fetchRows(scan_position,
1531: fetchNext_one_slot_array, (RowLocation[]) null,
1532: (BackingStoreHashtable) null, 1, (int[]) null) == 1;
1533:
1534: return (ret_val);
1535: }
1536:
1537: /**
1538: Fetch the row at the next position of the Scan.
1539:
1540: If there is a valid next position in the scan then
1541: the value in the template storable row is replaced
1542: with the value of the row at the current scan
1543: position. The columns of the template row must
1544: be of the same type as the actual columns in the
1545: underlying conglomerate.
1546:
1547: The resulting contents of templateRow after a fetchNext()
1548: which returns false is undefined.
1549:
1550: The result of calling fetchNext(row) is exactly logically
1551: equivalent to making a next() call followed by a fetch(row)
1552: call. This interface allows implementations to optimize
1553: the 2 calls if possible.
1554:
1555: @param row The template row into which the value
1556: of the next position in the scan is to be stored.
1557:
1558: @return True if there is a next position in the scan,
1559: false if there isn't.
1560:
1561: @exception StandardException Standard exception policy.
1562: **/
1563: public boolean fetchNext(DataValueDescriptor[] row)
1564: throws StandardException {
1565: boolean ret_val;
1566:
1567: if (SanityManager.DEBUG) {
1568: TemplateRow.checkPartialColumnTypes(
1569: this .getConglomerate().format_ids,
1570: init_scanColumnList, (int[]) null, row);
1571: }
1572:
1573: // Turn this call into a group fetch of a 1 element group.
1574: fetchNext_one_slot_array[0] = row;
1575: ret_val = fetchRows(scan_position, fetchNext_one_slot_array,
1576: (RowLocation[]) null, (BackingStoreHashtable) null, 1,
1577: (int[]) null) == 1;
1578:
1579: return (ret_val);
1580: }
1581:
1582: /**
1583: * Fetch the next N rows from the table.
1584: * <p>
1585: * The client allocates an array of N rows and passes it into the
1586: * fetchNextSet() call. This routine does the equivalent of N
1587: * fetchNext() calls, filling in each of the rows in the array.
1588: * Locking is performed exactly as if the N fetchNext() calls had
1589: * been made.
1590: * <p>
1591: * It is up to Access how many rows to return. fetchNextSet() will
1592: * return how many rows were filled in. If fetchNextSet() returns 0
1593: * then the scan is complete, (ie. the scan is in the same state as if
1594: * fetchNext() had returned false). If the scan is not complete then
1595: * fetchNext() will return (1 <= row_count <= N).
1596: * <p>
1597: * The current position of the scan is undefined if fetchNextSet()
1598: * is used (ie. mixing fetch()/fetchNext() and fetchNextSet() calls
1599: * in a single scan does not work). This is because a fetchNextSet()
1600: * request for 5 rows from a heap where the first 2 rows qualify, but
1601: * no other rows qualify will result in the scan being positioned at
1602: * the end of the table, while if 5 rows did qualify the scan will be
1603: * positioned on the 5th row.
1604: * <p>
1605: * Qualifiers, start and stop positioning of the openscan are applied
1606: * just as in a normal scan.
1607: * <p>
1608: * The columns of the row will be the standard columns returned as
1609: * part of a scan, as described by the validColumns - see openScan for
1610: * description.
1611: * <p>
1612: * Expected usage:
1613: *
1614: * // allocate an array of 5 empty row templates
1615: * DataValueDescriptor[][] row_array = allocate_row_array(5);
1616: * int row_cnt = 0;
1617: *
1618: * scan = openScan();
1619: *
1620: * while ((row_cnt = scan.fetchNextSet(row_array) != 0)
1621: * {
1622: * // I got "row_cnt" rows from the scan. These rows will be
1623: * // found in row_array[0] through row_array[row_cnt - 1]
1624: * }
1625: *
1626: * <p>
1627: *
1628: * RESOLVE - This interface is being provided so that we can prototype
1629: * the performance results it can achieve. If it looks like
1630: * this interface is useful, it is very likely we will look
1631: * into a better way to tie together the now 4 different
1632: * fetch interfaces: fetch, fetchNext(), fetchNextGroup(),
1633: * and fetchSet().
1634: *
1635: * @return The number of qualifying rows found and copied into the
1636: * provided array of rows. If 0 then the scan is complete,
1637: * otherwise the return value will be:
1638: * 1 <= row_count <= row_array.length
1639: *
1640: * @param row_array The array of rows to copy rows into.
1641: * row_array[].length must >= 1. This routine
1642: * assumes that all entries in the array
1643: * contain complete template rows.
1644: *
1645: * @exception StandardException Standard exception policy.
1646: **/
1647: public int fetchNextGroup(DataValueDescriptor[][] row_array,
1648: RowLocation[] rowloc_array) throws StandardException {
1649: return (fetchRows(scan_position, row_array, rowloc_array,
1650: (BackingStoreHashtable) null, row_array.length,
1651: (int[]) null));
1652: }
1653:
1654: public int fetchNextGroup(DataValueDescriptor[][] row_array,
1655: RowLocation[] old_rowloc_array,
1656: RowLocation[] new_rowloc_array) throws StandardException {
1657: // This interface is currently only used to move rows around in
1658: // a heap table, unused in btree's -- so not implemented.
1659:
1660: throw StandardException
1661: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1662: }
1663:
1664: /**
1665: * Insert all rows that qualify for the current scan into the input
1666: * Hash table.
1667: * <p>
1668: * This routine scans executes the entire scan as described in the
1669: * openScan call. For every qualifying unique row value an entry is
1670: * placed into the HashTable. For unique row values the entry in the
1671: * BackingStoreHashtable has a key value of the object stored in
1672: * row[key_column_number], and the value of the data is row. For row
1673: * values with duplicates, the key value is also row[key_column_number],
1674: * but the value of the data is a Vector of
1675: * rows. The caller will have to call "instanceof" on the data value
1676: * object if duplicates are expected, to determine if the data value
1677: * of the Hashtable entry is a row or is a Vector of rows.
1678: * <p>
1679: * Note, that for this routine to work efficiently the caller must
1680: * ensure that the object in row[key_column_number] implements
1681: * the hashCode and equals method as appropriate for it's datatype.
1682: * <p>
1683: * It is expected that this call will be the first and only call made in
1684: * an openscan. Qualifiers and stop position of the openscan are applied
1685: * just as in a normal scan. This call is logically equivalent to the
1686: * caller performing the following:
1687: *
1688: * import java.util.Hashtable;
1689: *
1690: * hash_table = new Hashtable();
1691: *
1692: * while (next())
1693: * {
1694: * row = create_new_row();
1695: * fetch(row);
1696: * if ((duplicate_value =
1697: * hash_table.put(row[key_column_number], row)) != null)
1698: * {
1699: * Vector row_vec;
1700: *
1701: * // inserted a duplicate
1702: * if ((duplicate_value instanceof vector))
1703: * {
1704: * row_vec = (Vector) duplicate_value;
1705: * }
1706: * else
1707: * {
1708: * // allocate vector to hold duplicates
1709: * row_vec = new Vector(2);
1710: *
1711: * // insert original row into vector
1712: * row_vec.addElement(duplicate_value);
1713: *
1714: * // put the vector as the data rather than the row
1715: * hash_table.put(row[key_column_number], row_vec);
1716: * }
1717: *
1718: * // insert new row into vector
1719: * row_vec.addElement(row);
1720: * }
1721: * }
1722: * <p>
1723: * The columns of the row will be the standard columns returned as
1724: * part of a scan, as described by the validColumns - see openScan for
1725: * description.
1726: * RESOLVE - is this ok? or should I hard code somehow the row to
1727: * be the first column and the row location?
1728: * <p>
1729: * Currently it is only possible to hash on the first column in the
1730: * conglomerate, in the future we may change the interface to allow
1731: * hashing either on a different column or maybe on a combination of
1732: * columns.
1733: * <p>
1734: * No overflow to external storage is provided, so calling this routine
1735: * on a 1 gigabyte conglomerate will incur at least 1 gigabyte of memory
1736: * (probably failing with a java out of memory condition). If this
1737: * routine gets an out of memory condition, or if "max_rowcnt" is
1738: * exceeded then then the routine will give up, empty the Hashtable,
1739: * and return "false."
1740: * <p>
1741: * On exit from this routine, whether the fetchSet() succeeded or not
1742: * the scan is complete, it is positioned just the same as if the scan
1743: * had been drained by calling "next()" until it returns false (ie.
1744: * fetchNext() and next() calls will return false).
1745: * reopenScan() can be called to restart the scan.
1746: * <p>
1747: *
1748: * RESOLVE - until we get row counts what should we do for sizing the
1749: * the size, capasity, and load factor of the hash table.
1750: * For now it is up to the caller to create the Hashtable,
1751: * Access does not reset any parameters.
1752: * <p>
1753: * RESOLVE - I am not sure if access should be in charge of allocating
1754: * the new row objects. I know that I can do this in the
1755: * case of btree's, but I don't think I can do this in heaps.
1756: * Maybe this is solved by work to be done on the sort
1757: * interface.
1758: *
1759: *
1760: * @param max_rowcnt The maximum number of rows to insert into the
1761: * Hash table. Pass in -1 if there is no maximum.
1762: * @param key_column_numbers The column numbers of the columns in the
1763: * scan result row to be the key to the Hashtable.
1764: * "0" is the first column in the scan result
1765: * row (which may be different than the first
1766: * column in the row in the table of the scan).
1767: * @param hash_table The java HashTable to load into.
1768: *
1769: * @exception StandardException Standard exception policy.
1770: **/
1771: public void fetchSet(long max_rowcnt, int[] key_column_numbers,
1772: BackingStoreHashtable hash_table) throws StandardException {
1773: // System.out.println("fetchSet");
1774:
1775: fetchRows(scan_position, (DataValueDescriptor[][]) null,
1776: (RowLocation[]) null,
1777: (BackingStoreHashtable) hash_table, max_rowcnt,
1778: key_column_numbers);
1779:
1780: return;
1781: }
1782:
1783: /**
1784: Reposition the current scan. This call is semantically the same as if
1785: the current scan had been closed and a openScan() had been called instead.
1786: The scan is reopened with against the same conglomerate, and the scan
1787: is reopened with the same "hold" and "forUpdate" parameters passed in
1788: the original openScan. The previous template row continues to be used.
1789:
1790: @param startKeyValue An indexable row which holds a
1791: (partial) key value which, in combination with the
1792: startSearchOperator, defines the starting position of
1793: the scan. If null, the starting position of the scan
1794: is the first row of the conglomerate.
1795:
1796: @param startSearchOperator an operator which defines
1797: how the startKeyValue is to be searched for. If
1798: startSearchOperation is ScanController.GE, the scan starts on
1799: the first row which is greater than or equal to the
1800: startKeyValue. If startSearchOperation is ScanController.GT,
1801: the scan starts on the first row whose key is greater than
1802: startKeyValue. The startSearchOperation parameter is
1803: ignored if the startKeyValue parameter is null.
1804:
1805: @param qualifier An array of qualifiers which, applied
1806: to each key, restrict the rows returned by the scan. Rows
1807: for which any one of the qualifiers returns false are not
1808: returned by the scan. If null, all rows are returned.
1809:
1810: @param stopKeyValue An indexable row which holds a
1811: (partial) key value which, in combination with the
1812: stopSearchOperator, defines the ending position of
1813: the scan. If null, the ending position of the scan
1814: is the last row of the conglomerate.
1815:
1816: @param stopSearchOperator an operator which defines
1817: how the stopKeyValue is used to determine the scan stopping
1818: position. If stopSearchOperation is ScanController.GE, the scan
1819: stops just before the first row which is greater than or
1820: equal to the stopKeyValue. If stopSearchOperation is
1821: ScanController.GT, the scan stops just before the first row whose
1822: key is greater than startKeyValue. The stopSearchOperation
1823: parameter is ignored if the stopKeyValue parameter is null.
1824:
1825: @exception StandardException Standard exception policy.
1826: **/
1827: public final void reopenScan(DataValueDescriptor[] startKeyValue,
1828: int startSearchOperator, Qualifier qualifier[][],
1829: DataValueDescriptor[] stopKeyValue, int stopSearchOperator)
1830: throws StandardException {
1831: if (SanityManager.DEBUG) {
1832: if (!init_hold)
1833: SanityManager
1834: .ASSERT(this .container != null,
1835: "BTreeScan.reopenScan() called on non-held closed scan.");
1836:
1837: // should only be called by clients outside of store, so should
1838: // not be possible for a latch to held.
1839: SanityManager.ASSERT(scan_position.current_leaf == null);
1840: }
1841:
1842: // call unlockScanRecordAfterRead() before setting the scan back
1843: // to init state, so that we release the last lock if necessary (ie.
1844: // for read committed).
1845: //
1846:
1847: if (scan_position.current_rh != null) {
1848: // reposition to get record handle if we don't have it.
1849:
1850: if (!reposition(scan_position, false)) {
1851: if (SanityManager.DEBUG) {
1852: SanityManager
1853: .THROWASSERT("can not fail while holding update row lock.");
1854: }
1855: }
1856:
1857: this .getLockingPolicy().unlockScanRecordAfterRead(
1858: scan_position, init_forUpdate);
1859:
1860: scan_position.current_rh = null;
1861: scan_position.current_leaf.release();
1862: scan_position.current_leaf = null;
1863: }
1864:
1865: // Need to do this unlock in any case, until lock manager provides
1866: // a way to release locks associated with a compatibility space. This
1867: // scan lock is special, as it is a lock on the btree container rather
1868: // than the heap container. The open container on the btree actually
1869: // has a null locking policy so the close of that container does not
1870: // release this lock, need to explicitly unlock it here or when the
1871: // scan is closed as part of the abort the lock will not be released.
1872: if (scan_position.current_scan_pageno != 0) {
1873: this .getLockingPolicy().unlockScan(
1874: scan_position.current_scan_pageno);
1875: scan_position.current_scan_pageno = 0;
1876: }
1877:
1878: scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
1879: scan_position.current_rh = null;
1880: scan_position.current_positionKey = null;
1881:
1882: initScanParams(startKeyValue, startSearchOperator, qualifier,
1883: stopKeyValue, stopSearchOperator);
1884:
1885: if (!init_hold)
1886: this .scan_state = SCAN_INIT;
1887: else
1888: this .scan_state = (this .container != null ? SCAN_INIT
1889: : SCAN_HOLD_INIT);
1890: }
1891:
1892: /**
1893: Reposition the current scan. This call is semantically the same as if
1894: the current scan had been closed and a openScan() had been called instead.
1895: The scan is reopened against the same conglomerate, and the scan
1896: is reopened with the same "scan column list", "hold" and "forUpdate"
1897: parameters passed in the original openScan.
1898: <p>
1899: The statistics gathered by the scan are not reset to 0 by a reopenScan(),
1900: rather they continue to accumulate.
1901: <p>
1902: Note that this operation is currently only supported on Heap conglomerates.
1903: Also note that order of rows within are heap are not guaranteed, so for
1904: instance positioning at a RowLocation in the "middle" of a heap, then
1905: inserting more data, then continuing the scan is not guaranteed to see
1906: the new rows - they may be put in the "beginning" of the heap.
1907:
1908: @param startRowLocation An existing RowLocation within the conglomerate,
1909: at which to position the start of the scan. The scan will begin at this
1910: location and continue forward until the end of the conglomerate.
1911: Positioning at a non-existent RowLocation (ie. an invalid one or one that
1912: had been deleted), will result in an exception being thrown when the
1913: first next operation is attempted.
1914:
1915: @param qualifier An array of qualifiers which, applied
1916: to each key, restrict the rows returned by the scan. Rows
1917: for which any one of the qualifiers returns false are not
1918: returned by the scan. If null, all rows are returned.
1919:
1920: @exception StandardException Standard exception policy.
1921: **/
1922: public void reopenScanByRowLocation(RowLocation startRowLocation,
1923: Qualifier qualifier[][]) throws StandardException {
1924: throw StandardException
1925: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1926: }
1927:
1928: /*
1929: ** Methods of ScanController, which are not supported by btree.
1930: */
1931:
1932: /**
1933: Fetch the location of the current position in the scan.
1934: @see ScanController#fetchLocation
1935:
1936: @exception StandardException Standard exception policy.
1937: **/
1938: public void fetchLocation(RowLocation templateLocation)
1939: throws StandardException {
1940: throw StandardException
1941: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1942: }
1943:
1944: /**
1945: Return a row location object of the correct type to be
1946: used in calls to fetchLocation.
1947: @see GenericScanController#newRowLocationTemplate
1948:
1949: @exception StandardException Standard exception policy.
1950: **/
1951: public RowLocation newRowLocationTemplate()
1952: throws StandardException {
1953: throw StandardException
1954: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1955: }
1956:
1957: /**
1958: Replace the entire row at the current position of the scan.
1959:
1960: Unimplemented interface by btree, will throw an exception.
1961:
1962: @see ScanController#replace
1963: @exception StandardException Standard exception policy.
1964: **/
1965: public boolean replace(DataValueDescriptor[] row,
1966: FormatableBitSet validColumns) throws StandardException {
1967: throw StandardException
1968: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1969: }
1970:
1971: /*
1972: ** Methods of ScanManager
1973: */
1974:
1975: /**
1976: Close the scan, a commit or abort is about to happen.
1977: **/
1978: public boolean closeForEndTransaction(boolean closeHeldScan)
1979: throws StandardException {
1980: if (!init_hold || closeHeldScan) {
1981: // Scan is closed, make sure no access to any state variables
1982: positionAtDoneScan(scan_position);
1983:
1984: super .close();
1985:
1986: // null out so that these object's can get GC'd earlier.
1987: this .init_rawtran = null;
1988: this .init_template = null;
1989: this .init_startKeyValue = null;
1990: this .init_qualifier = null;
1991: this .init_stopKeyValue = null;
1992:
1993: this .getXactMgr().closeMe(this );
1994:
1995: return (true);
1996: } else {
1997:
1998: if (this .scan_state == SCAN_INPROGRESS) {
1999: if (SanityManager.DEBUG) {
2000: SanityManager.ASSERT(scan_position != null);
2001: }
2002:
2003: if (scan_position.current_positionKey == null) {
2004: // save position of scan by key rather than location so
2005: // that we can recover if the page with the position
2006: // disappears while we don't have a scan lock.
2007:
2008: savePosition();
2009: }
2010: this .scan_state = SCAN_HOLD_INPROGRESS;
2011: } else if (this .scan_state == SCAN_INIT) {
2012: this .scan_state = SCAN_HOLD_INIT;
2013: }
2014:
2015: super .close();
2016:
2017: return (false);
2018: }
2019: }
2020:
2021: /**
2022: * Do work necessary to maintain the current position in the scan.
2023: * <p>
2024: * Save the current position of the scan as a key.
2025: * Do whatever is necessary to maintain the current position of the scan.
2026: * For some conglomerates this may be a no-op.
2027: *
2028: * <p>
2029: * @exception StandardException Standard exception policy.
2030: **/
2031: private void savePosition() throws StandardException {
2032: if (this .scan_state == SCAN_INPROGRESS) {
2033: // Either current_rh or positionKey is valid - the other is null.
2034: if (SanityManager.DEBUG) {
2035: SanityManager
2036: .ASSERT((scan_position.current_rh == null) == (scan_position.current_positionKey != null));
2037: }
2038:
2039: try {
2040: if (scan_position.current_rh != null) {
2041: // if scan position is not saved by key, then make it so.
2042:
2043: // must reposition to get the page latched.
2044:
2045: if (reposition(scan_position, false)) {
2046: scan_position.current_positionKey = runtime_mem
2047: .get_row_for_export();
2048:
2049: Page page = scan_position.current_leaf
2050: .getPage();
2051:
2052: RecordHandle rh = page
2053: .fetchFromSlot(
2054: (RecordHandle) null,
2055: page
2056: .getSlotNumber(scan_position.current_rh),
2057: scan_position.current_positionKey,
2058: (FetchDescriptor) null, true);
2059:
2060: if (SanityManager.DEBUG) {
2061: SanityManager.ASSERT(rh != null);
2062: }
2063:
2064: scan_position.current_rh = null;
2065: scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
2066:
2067: // release scan lock now that the row is saved away.
2068:
2069: if (scan_position.current_scan_pageno != 0) {
2070: this .getLockingPolicy().unlockScan(
2071: scan_position.current_scan_pageno);
2072: scan_position.current_scan_pageno = 0;
2073: }
2074:
2075: } else {
2076: // this should never happen as we hold the scan lock
2077: // on the page while maintaining the position by
2078: // recordhandle - reposition should always work in this
2079: // case.
2080:
2081: if (SanityManager.DEBUG)
2082: SanityManager
2083: .THROWASSERT("Must always be able to reposition.");
2084: }
2085: }
2086:
2087: } finally {
2088:
2089: if (scan_position.current_leaf != null) {
2090: // release latch on page
2091: scan_position.current_leaf.release();
2092: scan_position.current_leaf = null;
2093: }
2094: }
2095: }
2096:
2097: }
2098:
2099: /**
2100: * Do work necessary to maintain the current position in the scan.
2101: * <p>
2102: * The latched page in the conglomerate "congomid" is changing, do
2103: * whatever is necessary to maintain the current position of the scan.
2104: * For some conglomerates this may be a no-op.
2105: * <p>
2106: *
2107: * @param conglom Conglomerate object of the conglomerate being changed.
2108: * @param page Page in the conglomerate being changed.
2109: *
2110: * @exception StandardException Standard exception policy.
2111: **/
2112: public void savePosition(Conglomerate conglom, Page page)
2113: throws StandardException {
2114: // page should be latched by split. This scan is assuming that latch
2115: // and reading off it's key from the page under the split's latch.
2116: // A lock should have already been gotten on this row.
2117:
2118: if (SanityManager.DEBUG) {
2119: SanityManager.ASSERT(page.isLatched());
2120: }
2121:
2122: /*
2123: System.out.println(
2124: "Saving position in btree at top: " +
2125: " this.conglomerate = " + this.conglomerate +
2126: " this.scan_state = " + this.scan_state);
2127: SanityManager.DEBUG_PRINT("savePosition()",
2128: "Saving position in btree at top: " +
2129: " this.conglomerate = " + this.conglomerate +
2130: " this.scan_state = " + this.scan_state);
2131: */
2132:
2133: if ((this .getConglomerate() == conglom)
2134: && (this .scan_state == SCAN_INPROGRESS)) {
2135: // Either current_rh or positionKey is valid - the other is null.
2136: if (SanityManager.DEBUG) {
2137: SanityManager
2138: .ASSERT((scan_position.current_rh == null) == (scan_position.current_positionKey != null));
2139: }
2140:
2141: /*
2142: SanityManager.DEBUG_PRINT("savePosition()",
2143: "Saving position in btree: " +
2144: ";current_scan_pageno = " + this.current_scan_pageno +
2145: "this.current_rh = " + this.current_rh +
2146: ";page.getPageNumber() = " + page.getPageNumber() +
2147: ((this.current_rh != null) ?
2148: (";this.current_rh.getPageNumber() = " +
2149: this.current_rh.getPageNumber()) : ""));
2150: */
2151:
2152: if (scan_position.current_rh != null
2153: && page.getPageNumber() == scan_position.current_rh
2154: .getPageNumber()) {
2155: scan_position.current_positionKey = runtime_mem
2156: .get_row_for_export();
2157:
2158: RecordHandle rh = page.fetchFromSlot(
2159: (RecordHandle) null,
2160: page.getSlotNumber(scan_position.current_rh),
2161: scan_position.current_positionKey,
2162: (FetchDescriptor) null, true);
2163:
2164: if (SanityManager.DEBUG) {
2165: SanityManager.ASSERT(rh != null);
2166: }
2167:
2168: scan_position.current_rh = null;
2169: scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
2170:
2171: // release the scan lock now that we have saved away the row.
2172:
2173: if (scan_position.current_scan_pageno != 0) {
2174: this .getLockingPolicy().unlockScan(
2175: scan_position.current_scan_pageno);
2176: scan_position.current_scan_pageno = 0;
2177: }
2178: }
2179: }
2180: }
2181:
2182: public RecordHandle getCurrentRecordHandleForDebugging() {
2183: return (scan_position.current_rh);
2184: }
2185:
2186: /*
2187: ** Standard toString() method. Prints out current position in scan.
2188: */
2189: public String toString() {
2190: if (SanityManager.DEBUG) {
2191: String string = "\n\tbtree = "
2192: + this .getConglomerate()
2193: + "\n\tscan direction = "
2194: + (this instanceof BTreeForwardScan ? "forward"
2195: : (this instanceof BTreeMaxScan ? "backward"
2196: : "illegal"))
2197: + "\n\t(scan_state:"
2198: + (this .scan_state == SCAN_INIT ? "SCAN_INIT"
2199: : this .scan_state == SCAN_INPROGRESS ? "SCAN_INPROGRESS"
2200: : this .scan_state == SCAN_DONE ? "SCAN_DONE"
2201: : this .scan_state == SCAN_HOLD_INIT ? "SCAN_HOLD_INIT"
2202: : this .scan_state == SCAN_HOLD_INPROGRESS ? "SCAN_HOLD_INPROGRESS"
2203: : "BAD_SCAN_STATE")
2204: + "\n\trh:"
2205: + scan_position.current_rh
2206: + "\n\tkey:"
2207: + scan_position.current_positionKey
2208: + ")"
2209: + "\n\tinit_rawtran = "
2210: + init_rawtran
2211: + "\n\tinit_hold = "
2212: + init_hold
2213: + "\n\tinit_forUpdate = "
2214: + init_forUpdate
2215: + "\n\tinit_useUpdateLocks = "
2216: + init_useUpdateLocks
2217: + "\n\tinit_scanColumnList = "
2218: + init_scanColumnList
2219: + "\n\tinit_scanColumnList.size() = "
2220: + ((init_scanColumnList != null ? init_scanColumnList
2221: .size()
2222: : 0))
2223: + "\n\tinit_template = "
2224: + RowUtil.toString(init_template)
2225: + "\n\tinit_startKeyValue = "
2226: + RowUtil.toString(init_startKeyValue)
2227: + "\n\tinit_startSearchOperator = "
2228: + (init_startSearchOperator == ScanController.GE ? "GE"
2229: : (init_startSearchOperator == ScanController.GT ? "GT"
2230: : Integer
2231: .toString(init_startSearchOperator)))
2232: + "\n\tinit_qualifier[] = "
2233: + init_qualifier
2234: + "\n\tinit_stopKeyValue = "
2235: + RowUtil.toString(init_stopKeyValue)
2236: + "\n\tinit_stopSearchOperator = "
2237: + (init_stopSearchOperator == ScanController.GE ? "GE"
2238: : (init_stopSearchOperator == ScanController.GT ? "GT"
2239: : Integer
2240: .toString(init_stopSearchOperator)))
2241: + "\n\tstat_numpages_visited = "
2242: + stat_numpages_visited
2243: + "\n\tstat_numrows_visited = "
2244: + stat_numrows_visited
2245: + "\n\tstat_numrows_qualified = "
2246: + stat_numrows_qualified
2247: + "\n\tstat_numdeleted_rows_visited = "
2248: + stat_numdeleted_rows_visited;
2249:
2250: return (string);
2251: } else {
2252: return (null);
2253: }
2254: }
2255: }
|