0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.store.access.conglomerate.GenericScanController
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.conglomerate;
0023:
0024: import org.apache.derby.iapi.reference.SQLState;
0025:
0026: import org.apache.derby.iapi.services.sanity.SanityManager;
0027:
0028: import org.apache.derby.iapi.error.StandardException;
0029:
0030: import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;
0031: import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
0032: import org.apache.derby.iapi.store.access.conglomerate.ScanManager;
0033: import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
0034:
0035: import org.apache.derby.iapi.store.access.ConglomerateController;
0036: import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
0037: import org.apache.derby.iapi.store.access.Qualifier;
0038: import org.apache.derby.iapi.store.access.RowUtil;
0039: import org.apache.derby.iapi.store.access.ScanController;
0040: import org.apache.derby.iapi.store.access.ScanInfo;
0041: import org.apache.derby.iapi.store.access.SpaceInfo;
0042:
0043: import org.apache.derby.iapi.store.raw.ContainerHandle;
0044: import org.apache.derby.iapi.store.raw.FetchDescriptor;
0045: import org.apache.derby.iapi.store.raw.Page;
0046: import org.apache.derby.iapi.store.raw.RecordHandle;
0047: import org.apache.derby.iapi.store.raw.Transaction;
0048:
0049: import org.apache.derby.iapi.store.access.Qualifier;
0050:
0051: import org.apache.derby.iapi.types.DataValueDescriptor;
0052:
0053: import org.apache.derby.iapi.types.Orderable;
0054: import org.apache.derby.iapi.types.RowLocation;
0055:
0056: import org.apache.derby.iapi.store.access.BackingStoreHashtable;
0057: import org.apache.derby.iapi.services.io.FormatableBitSet;
0058:
0059: import java.util.Properties;
0060:
0061: /**
0062: Generic class implementing shared ScanController methods.
0063:
0064: Logically a scancontroller is used to scan a set of rows that meet some
0065: specified qualification. Rows that meet the qualification may be operated
0066: upon by the scan to fetch, delete, or replace. The ScanController also
0067: supports the notion or "repositioning" the scan, which simply resets the
0068: beginning of the scan to a new place, and allows the user to continue from
0069: there.
0070:
0071: This class attempts to abstract out some of the parts of the scan such that
0072: maybe multiple access methods can share code, even if they perform parts of
0073: the scan wildly differently. Here is how the scan has been broken apart:
0074:
0075: scan_position - this variable holds the current scan position, it may be
0076: extended
0077: to provide more information if necessary.
0078:
0079: scan_state - a scan has 3 possible states:
0080: SCAN_INIT, SCAN_INPROGRESS, SCAN_DONE
0081:
0082: positionAtInitScan()
0083: - This routine is called to move the scan to the SCAN_INIT state.
0084: It is used both for initialization of the ScanController and
0085: by reopenScan().
0086:
0087: positionAtStartForForwardScan()
0088: - This routine is called to move the scan from SCAN_INIT to
0089: SCAN_INPROGRESS. Upon return from this routine it is expected
0090: that scan_position is set such that calling the generic
0091: scan loop will reach the first row of the scan. Note that this
0092: usually means setting the scan_postion to one before the 1st
0093: row to be returned.
0094:
0095: fetchRows() - This routine is the meat of the scan, it moves the scan to the
0096: next row, applies necessary qualifiers, and handles group or
0097: non-group operations. It moves through rows on a page in
0098: order and then moves to the "next" page.
0099:
0100: positionAtNextPage()
0101: - This routine handles moving the scan from the current
0102: scan_position to the next page.
0103:
0104: positionAtDoneScan()
0105: - Handle all cleanup associated with moving the scan state from
0106: SCAN_INPROGRESS to SCAN_DONE. This may include releasing locks,
0107: and setting the state of the scan. This does not close the
0108: scan, it allows for a reopenScan() to be called.
0109: **/
0110:
0111: public abstract class GenericScanController extends GenericController
0112: implements ScanManager {
0113:
0114: /**************************************************************************
0115: * Constants of the class
0116: **************************************************************************
0117: */
0118:
0119: /*
0120: * There are 5 states a scan can be in.
0121: * SCAN_INIT - A scan has started but no positioning has been done.
0122: * The scan will be positioned when the first next() call
0123: * has been made. None of the positioning state variables
0124: * are valid in this state.
0125: * SCAN_INPROGRESS -
0126: * A scan is in this state after the first next() call.
0127: * On exit from any GenericScanController method, while in
0128: * 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: public static final int SCAN_INIT = 1;
0162: public static final int SCAN_INPROGRESS = 2;
0163: public static final int SCAN_DONE = 3;
0164: public static final int SCAN_HOLD_INIT = 4;
0165: public static final int SCAN_HOLD_INPROGRESS = 5;
0166:
0167: /**************************************************************************
0168: * Fields of the class
0169: **************************************************************************
0170: */
0171:
0172: /**
0173: * The following group of fields are all basic input parameters which are
0174: * provided by the calling code when doing a scan.
0175: * These are just saved values from what was initially input.
0176: **/
0177: private FormatableBitSet init_scanColumnList;
0178: private DataValueDescriptor[] init_startKeyValue;
0179: private int init_startSearchOperator;
0180: private Qualifier[][] init_qualifier;
0181: private DataValueDescriptor[] init_stopKeyValue;
0182: private int init_stopSearchOperator;
0183:
0184: private FetchDescriptor init_fetchDesc;
0185:
0186: /**
0187: * Delay positioning the table at the start position until the first
0188: * next() call.
0189: */
0190: private int scan_state;
0191:
0192: /**
0193: * If this flag is set to true, a RowLocation returned from this controller
0194: * may have been reused for another row.
0195: */
0196: protected boolean rowLocationsInvalidated = false;
0197:
0198: /**
0199: * This is the sequence number for when a record id can be
0200: * reused. If it has been changed in the container, a RowLocation
0201: * may be reused for another row.
0202: */
0203: private long reusableRecordIdSequenceNumber = 0;
0204:
0205: /**
0206: * The position for the current scan. The can be maintained in any
0207: * of the following ways:
0208: * record handle - scan_position.current_rh:
0209: * The scan maintains it's position using the record handle while
0210: * it does not have a latch on the page, which is the case anytime
0211: * control leaves access. The access method must take appropriate
0212: * steps to make sure the record handle will still be valid when
0213: * the scan needs to reposition using the record handle.
0214: * slot number - scan_position.current_slot:
0215: * While the scan has a latch on the page the scan is positioned
0216: * using the slot number as the order of the rows cannot change
0217: * while the latch is held (unless the holder of the latch causes
0218: * them to move).
0219: * page number - (RESOLVE - TODO)
0220: * Sometimes it would be interesting to position a scan "between"
0221: * pages, such that the next time the scan starts is starts at
0222: * the next page. This would allow us to efficiently do group
0223: * scans returning page at atime results.
0224: * NOT IMPLEMENTED CURRENTLY.
0225: **/
0226: protected RowPosition scan_position;
0227:
0228: /**
0229: * Performance counters ...
0230: */
0231: protected int stat_numpages_visited = 0;
0232: protected int stat_numrows_visited = 0;
0233: protected int stat_numrows_qualified = 0;
0234:
0235: /**************************************************************************
0236: * Constructors for This class:
0237: **************************************************************************
0238: */
0239:
0240: /**************************************************************************
0241: * Private methods of This class:
0242: **************************************************************************
0243: */
0244:
0245: private final void repositionScanForUpateOper()
0246: throws StandardException {
0247: if (scan_state != SCAN_INPROGRESS)
0248: throw StandardException
0249: .newException(SQLState.AM_SCAN_NOT_POSITIONED);
0250:
0251: if (!open_conglom.latchPage(scan_position)) {
0252: throw StandardException.newException(
0253: SQLState.AM_RECORD_NOT_FOUND, open_conglom
0254: .getContainer().getId(), new Long(
0255: scan_position.current_rh.getId()));
0256: }
0257:
0258: if (open_conglom.isUseUpdateLocks()) {
0259: // we only have an U lock at this point which was acquired when the
0260: // scan positioned on the row, need to request an
0261: // X lock before we can actually perform the delete
0262:
0263: open_conglom.lockPositionForWrite(scan_position,
0264: false /* not insert */, true);
0265: }
0266: }
0267:
0268: /**************************************************************************
0269: * Protected methods implementing mechanics of scanning rows:
0270: *
0271: * positionAtInitScan() - move scan state to SCAN_INIT
0272: * positionAtStartForForwardScan() - SCAN_INIT -> SCAN_INPROGRESS
0273: * positionAtResumeScan() - reposition after losing scan latch
0274: * fetchRows() - move scan while in SCAN_INPROGRESS
0275: * positionAtNextPage() - move page while in SCAN_INPROGRESS
0276: * positionAtDoneScan() - SCAN_INPROGRESS -> SCAN_DONE
0277: *
0278: **************************************************************************
0279: */
0280:
0281: /**
0282: * Move scan to the the SCAN_INIT state.
0283: * <p>
0284: * This routine is called to move the scan to the SCAN_INIT state.
0285: * It is used both for initialization of the ScanController and
0286: * by reopenScan().
0287: **/
0288: protected void positionAtInitScan(
0289: DataValueDescriptor[] startKeyValue,
0290: int startSearchOperator, Qualifier qualifier[][],
0291: DataValueDescriptor[] stopKeyValue, int stopSearchOperator,
0292: RowPosition pos) throws StandardException {
0293: // startKeyValue init.
0294: this .init_startKeyValue = startKeyValue;
0295: if (RowUtil.isRowEmpty(this .init_startKeyValue))
0296: this .init_startKeyValue = null;
0297:
0298: // startSearchOperator init.
0299: this .init_startSearchOperator = startSearchOperator;
0300:
0301: // qualifier init.
0302: if ((qualifier != null) && (qualifier.length == 0))
0303: qualifier = null;
0304: this .init_qualifier = qualifier;
0305:
0306: // TODO (mikem) - this could be more efficient, by writing
0307: // code to figure out length of row, but scratch row is cached
0308: // so allocating it here is probably not that bad.
0309: init_fetchDesc = new FetchDescriptor((open_conglom
0310: .getRuntimeMem().get_scratch_row()).length,
0311: init_scanColumnList, init_qualifier);
0312:
0313: // stopKeyValue init.
0314: this .init_stopKeyValue = stopKeyValue;
0315: if (RowUtil.isRowEmpty(this .init_stopKeyValue))
0316: this .init_stopKeyValue = null;
0317:
0318: // stopSearchOperator init.
0319: this .init_stopSearchOperator = stopSearchOperator;
0320:
0321: // reset the "current" position to starting condition.
0322: pos.init();
0323:
0324: // Verify that all columns in start key value, stop key value, and
0325: // qualifiers are present in the list of columns described by the
0326: // scanColumnList.
0327: if (SanityManager.DEBUG) {
0328: if (init_scanColumnList != null) {
0329: // verify that all columns specified in qualifiers, start
0330: // and stop positions are specified in the scanColumnList.
0331:
0332: FormatableBitSet required_cols;
0333:
0334: if (qualifier != null)
0335: required_cols = RowUtil
0336: .getQualifierBitSet(qualifier);
0337: else
0338: required_cols = new FormatableBitSet(0);
0339:
0340: // add in start columns
0341: if (this .init_startKeyValue != null) {
0342: required_cols.grow(this .init_startKeyValue.length);
0343: for (int i = 0; i < this .init_startKeyValue.length; i++)
0344: required_cols.set(i);
0345: }
0346:
0347: if (this .init_stopKeyValue != null) {
0348: required_cols.grow(this .init_stopKeyValue.length);
0349: for (int i = 0; i < this .init_stopKeyValue.length; i++)
0350: required_cols.set(i);
0351: }
0352:
0353: FormatableBitSet required_cols_and_scan_list = (FormatableBitSet) required_cols
0354: .clone();
0355:
0356: required_cols_and_scan_list.and(init_scanColumnList);
0357:
0358: // FormatableBitSet equals requires the two FormatableBitSets to be of same
0359: // length.
0360: required_cols.grow(init_scanColumnList.size());
0361:
0362: if (!required_cols_and_scan_list.equals(required_cols)) {
0363: SanityManager
0364: .THROWASSERT("Some column specified in a Btree "
0365: + " qualifier/start/stop list is "
0366: + "not represented in the scanColumnList."
0367: + "\n:required_cols_and_scan_list = "
0368: + required_cols_and_scan_list
0369: + "\n;required_cols = "
0370: + required_cols
0371: + "\n;init_scanColumnList = "
0372: + init_scanColumnList);
0373: }
0374: }
0375: }
0376:
0377: // Scan is fully initialized and ready to go.
0378: scan_state = SCAN_INIT;
0379: }
0380:
0381: /**
0382: * Reposition the scan upon entering the fetchRows loop.
0383: * <p>
0384: * Called upon entering fetchRows() while in the SCAN_INPROGRESS state.
0385: * Do work necessary to look at rows in the current page of the scan.
0386: * <p>
0387: * The default implementation uses a record handle to maintain a scan
0388: * position. It will get the latch again on the current
0389: * scan position and set the slot to the current record handle.
0390: *
0391: * @exception StandardException Standard exception policy.
0392: **/
0393: protected void positionAtResumeScan(RowPosition pos)
0394: throws StandardException {
0395: if (SanityManager.DEBUG) {
0396: SanityManager.ASSERT(scan_position.current_rh != null, this
0397: .toString());
0398: }
0399:
0400: // reposition the scan at the row just before the next one to return.
0401: // This routine handles the mess of repositioning if the row or the
0402: // page has disappeared. This can happen if a lock was not held on the
0403: // row while not holding the latch.
0404: open_conglom.latchPageAndRepositionScan(scan_position);
0405: }
0406:
0407: /**
0408: * Move the scan from SCAN_INIT to SCAN_INPROGRESS.
0409: * <p>
0410: * This routine is called to move the scan from SCAN_INIT to
0411: * SCAN_INPROGRESS. Upon return from this routine it is expected
0412: * that scan_position is set such that calling the generic
0413: * scan loop will reach the first row of the scan. Note that this
0414: * usually means setting the scan_postion to one before the 1st
0415: * row to be returned.
0416: * <p>
0417: *
0418: * @exception StandardException Standard exception policy.
0419: **/
0420: protected void positionAtStartForForwardScan(RowPosition pos)
0421: throws StandardException {
0422: if (pos.current_rh == null) {
0423: // 1st positioning of scan (delayed from openScan).
0424: pos.current_page = open_conglom.getContainer()
0425: .getFirstPage();
0426:
0427: if (SanityManager.DEBUG) {
0428: SanityManager
0429: .ASSERT(pos.current_page.getPageNumber() == ContainerHandle.FIRST_PAGE_NUMBER);
0430:
0431: if (pos.current_page.recordCount() < 1)
0432: SanityManager.THROWASSERT("record count = "
0433: + pos.current_page.recordCount());
0434: }
0435:
0436: // set up for scan to continue at beginning of first page just
0437: // after first first control row on first page.
0438: pos.current_slot = Page.FIRST_SLOT_NUMBER;
0439: } else {
0440: // 1st positioning of scan following a reopenScanByRowLocation
0441:
0442: // reposition the scan at the row just before the next one to
0443: // return. This routine handles the mess of repositioning if the
0444: // row or the page has disappeared. This can happen if a lock was
0445: // not held on the row while not holding the latch.
0446: open_conglom.latchPageAndRepositionScan(pos);
0447:
0448: // set up for scan to at the specified record handle (position one
0449: // before it so that the loop increment and find it).
0450: pos.current_slot -= 1;
0451: }
0452:
0453: pos.current_rh = null;
0454: this .stat_numpages_visited = 1;
0455: this .scan_state = SCAN_INPROGRESS;
0456: }
0457:
0458: /**
0459: * Position scan to slot before first slot on next page.
0460: * <p>
0461: * @exception StandardException Standard exception policy.
0462: **/
0463: protected void positionAtNextPage(RowPosition pos)
0464: throws StandardException {
0465: // The current_page can become null, in a rare multi-user case, where
0466: // all pages in the heap are deallocated, in the middle of the scan
0467: // loop, when no latches are held, and the scan is waiting on a lock.
0468: // In this case the lockPositionForRead code, has nowhere good to
0469: // position the scan, so it just sets the page to null and returns.
0470: if (pos.current_page != null) {
0471: // save current page number.
0472: long pageid = pos.current_page.getPageNumber();
0473:
0474: // unlatch old page.
0475: pos.unlatch();
0476:
0477: // latch page after current page number.
0478: pos.current_page = open_conglom.getContainer().getNextPage(
0479: pageid);
0480:
0481: // set up for scan to continue at beginning of this new page.
0482: pos.current_slot = Page.FIRST_SLOT_NUMBER - 1;
0483: }
0484: }
0485:
0486: /**
0487: * Do any necessary work to complete the scan.
0488: *
0489: * @exception StandardException Standard exception policy.
0490: **/
0491: protected void positionAtDoneScan(RowPosition pos)
0492: throws StandardException {
0493: // Unlatch current page if any.
0494: pos.unlatch();
0495:
0496: // unlock the previous row.
0497: if (scan_position.current_rh != null) {
0498: open_conglom.unlockPositionAfterRead(scan_position);
0499: scan_position.current_rh = null;
0500: }
0501:
0502: this .scan_state = SCAN_DONE;
0503: }
0504:
0505: public void reopenScanByRowLocation(RowLocation startRowLocation,
0506: Qualifier qualifier[][]) throws StandardException {
0507: throw StandardException
0508: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
0509: }
0510:
0511: /**************************************************************************
0512: * Protected methods of This class:
0513: **************************************************************************
0514: */
0515:
0516: /**
0517: * Create object which represents the scan position.
0518: * <p>
0519: * Designed so that extending classes can override and allocate
0520: * implementation specific row position's.
0521: *
0522: * @exception StandardException Standard exception policy.
0523: **/
0524: protected RowPosition allocateScanPosition()
0525: throws StandardException {
0526: return (new RowPosition());
0527: }
0528:
0529: /**
0530: * Fetch the next N rows from the table.
0531: * <p>
0532: * Utility routine used by both fetchSet() and fetchNextGroup().
0533: *
0534: * @exception StandardException Standard exception policy.
0535: **/
0536: protected int fetchRows(DataValueDescriptor[][] row_array,
0537: RowLocation[] rowloc_array,
0538: BackingStoreHashtable hash_table, long max_rowcnt,
0539: int[] key_column_numbers) throws StandardException {
0540: int ret_row_count = 0;
0541: DataValueDescriptor[] fetch_row = null;
0542:
0543: if (max_rowcnt == -1)
0544: max_rowcnt = Long.MAX_VALUE;
0545:
0546: if (SanityManager.DEBUG) {
0547: if (row_array != null) {
0548: SanityManager
0549: .ASSERT(row_array[0] != null,
0550: "first array slot in fetchNextGroup() must be non-null.");
0551: SanityManager.ASSERT(hash_table == null);
0552: } else {
0553: SanityManager.ASSERT(hash_table != null);
0554: }
0555: }
0556:
0557: if (this .scan_state == SCAN_INPROGRESS) {
0558: positionAtResumeScan(scan_position);
0559: } else if (this .scan_state == SCAN_INIT) {
0560: positionAtStartForForwardScan(scan_position);
0561:
0562: } else if (this .scan_state == SCAN_HOLD_INPROGRESS) {
0563: reopenAfterEndTransaction();
0564:
0565: if (SanityManager.DEBUG) {
0566: SanityManager.ASSERT(scan_position.current_rh != null,
0567: this .toString());
0568: }
0569:
0570: // reposition the scan at the row just before the next one to
0571: // return.
0572: // This routine handles the mess of repositioning if the row or
0573: // the page has disappeared. This can happen if a lock was not
0574: // held on the row while not holding the latch.
0575: open_conglom.latchPageAndRepositionScan(scan_position);
0576:
0577: this .scan_state = SCAN_INPROGRESS;
0578: } else if (this .scan_state == SCAN_HOLD_INIT) {
0579: reopenAfterEndTransaction();
0580:
0581: positionAtStartForForwardScan(scan_position);
0582:
0583: } else {
0584: if (SanityManager.DEBUG)
0585: SanityManager.ASSERT(this .scan_state == SCAN_DONE);
0586:
0587: return (0);
0588: }
0589:
0590: // At this point:
0591: // scan_position.current_page is latched.
0592: // scan_position.current_slot is the slot on scan_position.current_page
0593: // just before the "next" record this routine should process.
0594:
0595: // loop through successive pages and successive slots on those
0596: // pages. Stop when either the last page is reached
0597: // (scan_position.current_page will be null).
0598: // Along the way apply qualifiers to skip rows which don't qualify.
0599:
0600: while (scan_position.current_page != null) {
0601: while ((scan_position.current_slot + 1) < scan_position.current_page
0602: .recordCount()) {
0603: // unlock the previous row.
0604: if (scan_position.current_rh != null) {
0605: open_conglom.unlockPositionAfterRead(scan_position);
0606:
0607: }
0608: // Allocate a new row to read the row into.
0609: if (fetch_row == null) {
0610: if (hash_table == null) {
0611: // point at allocated row in array if one exists.
0612: if (row_array[ret_row_count] == null) {
0613: row_array[ret_row_count] = open_conglom
0614: .getRuntimeMem()
0615: .get_row_for_export();
0616: }
0617:
0618: fetch_row = row_array[ret_row_count];
0619: } else {
0620: fetch_row = open_conglom.getRuntimeMem()
0621: .get_row_for_export();
0622: }
0623: }
0624:
0625: // move scan current position forward.
0626: scan_position.positionAtNextSlot();
0627:
0628: // Lock the row.
0629: boolean lock_granted_while_latch_held = open_conglom
0630: .lockPositionForRead(scan_position,
0631: (RowPosition) null, true, true);
0632:
0633: if (!lock_granted_while_latch_held) {
0634: // if lock could not be granted while holding
0635: // latch, then the row may either be on the same page
0636: // or it may no longer exist, this implementation does not
0637: // handle rows which move to different pages.
0638: //
0639: // If the row moved on the same page then
0640: // lockPositionForRead() will have automatically updated
0641: // the scan_postion argument to point to it, and we
0642: // wil now have a latch and a lock on that row.
0643: //
0644: // If the row no longer exists then the
0645: // "moveForwardIfRowDisappears" argument makes this routine
0646: // find the "next" row in the heap and position on it. If
0647: // a valid row exists in the current page to position on,
0648: // then lockPositionForRead() will position on it, get
0649: // a lock on it, and return with a latch on the page.
0650: // Otherwise the routine will return with current_slot == -1
0651: // and it is up to this routine to continue the scan as
0652: // normal at the top of the loop.
0653:
0654: if (scan_position.current_page == null) {
0655: // page has been unlatched and the scan is done, there
0656: // are no more pages. getNextPage() has been coded to
0657: // handle a null current_page.
0658:
0659: break;
0660: } else if (scan_position.current_slot == -1) {
0661: // This means that lockPositionForRead() had to
0662: // reposition the scan forward to a new page, because
0663: // the row the scan was locking was purged, when the
0664: // latch was released to wait on the lock. In this
0665: // case just jump back to the top of loop and continue
0666: // scan.
0667:
0668: if (SanityManager.DEBUG) {
0669: SanityManager
0670: .ASSERT(scan_position.current_rh == null);
0671: }
0672:
0673: continue;
0674: }
0675: }
0676:
0677: this .stat_numrows_visited++;
0678:
0679: // lockRowAtPosition set pos.current_rh as part of getting lock.
0680: if (SanityManager.DEBUG) {
0681: SanityManager
0682: .ASSERT(scan_position.current_rh != null);
0683:
0684: // make sure current_rh and current_slot are in sync
0685: if (scan_position.current_slot != scan_position.current_page
0686: .getSlotNumber(scan_position.current_rh)) {
0687: SanityManager
0688: .THROWASSERT("current_slot = "
0689: + scan_position.current_slot
0690: + "current_rh = "
0691: + scan_position.current_rh
0692: + "current_rh.slot = "
0693: + scan_position.current_page
0694: .getSlotNumber(scan_position.current_rh));
0695: }
0696: }
0697:
0698: // fetchFromSlot returns null if row does not qualify.
0699:
0700: scan_position.current_rh_qualified = (scan_position.current_page
0701: .fetchFromSlot(scan_position.current_rh,
0702: scan_position.current_slot, fetch_row,
0703: init_fetchDesc, false) != null);
0704:
0705: if (scan_position.current_rh_qualified) {
0706: // qualifying row.
0707:
0708: // scan_position.current_rh is save position of scan while
0709: // latch is not held. It currently points at the
0710: // scan_position.current_slot in search (while latch is
0711: // held).
0712: if (SanityManager.DEBUG) {
0713: // make sure current_rh and current_slot are in sync
0714: SanityManager
0715: .ASSERT(scan_position.current_slot == scan_position.current_page
0716: .getSlotNumber(scan_position.current_rh));
0717: }
0718:
0719: // Found qualifying row. Done fetching rows for the group?
0720: ret_row_count++;
0721: stat_numrows_qualified++;
0722:
0723: if (hash_table == null) {
0724: if (rowloc_array != null) {
0725: // if requested return the associated row location.
0726: setRowLocationArray(rowloc_array,
0727: ret_row_count - 1, scan_position);
0728: }
0729:
0730: fetch_row = null;
0731: } else {
0732: if (hash_table.put(false, fetch_row)) {
0733: // The row was inserted into the hash table so we
0734: // need to create a new row next time through.
0735: fetch_row = null;
0736: }
0737: }
0738:
0739: if (max_rowcnt <= ret_row_count) {
0740: // exit fetch row loop and return to the client.
0741: scan_position.unlatch();
0742:
0743: if (SanityManager.DEBUG) {
0744: SanityManager
0745: .ASSERT(scan_position.current_rh != null);
0746: }
0747:
0748: return (ret_row_count);
0749: }
0750: }
0751: }
0752:
0753: positionAtNextPage(scan_position);
0754:
0755: this .stat_numpages_visited++;
0756: }
0757:
0758: // Reached last page of scan.
0759: positionAtDoneScan(scan_position);
0760:
0761: // we need to decrement when we stop scan at the end of the table.
0762: this .stat_numpages_visited--;
0763:
0764: return (ret_row_count);
0765: }
0766:
0767: /**
0768: Reposition the current scan. This call is semantically the same as if
0769: the current scan had been closed and a openScan() had been called instead.
0770: The scan is reopened against the same conglomerate, and the scan
0771: is reopened with the same "scan column list", "hold" and "forUpdate"
0772: parameters passed in the original openScan.
0773: <p>
0774: The statistics gathered by the scan are not reset to 0 by a reopenScan(),
0775: rather they continue to accumulate.
0776: <p>
0777: Note that this operation is currently only supported on Heap conglomerates.
0778: Also note that order of rows within are heap are not guaranteed, so for
0779: instance positioning at a RowLocation in the "middle" of a heap, then
0780: inserting more data, then continuing the scan is not guaranteed to see
0781: the new rows - they may be put in the "beginning" of the heap.
0782:
0783: @param startRecordHandle An existing RecordHandle within the conglomerate,
0784: at which to position the start of the scan. The scan will begin at this
0785: location and continue forward until the end of the conglomerate.
0786: Positioning at a non-existent RowLocation (ie. an invalid one or one that
0787: had been deleted), will result in an exception being thrown when the
0788: first next operation is attempted.
0789:
0790: @param qualifier An array of qualifiers which, applied
0791: to each key, restrict the rows returned by the scan. Rows
0792: for which any one of the qualifiers returns false are not
0793: returned by the scan. If null, all rows are returned.
0794:
0795: @exception StandardException Standard exception policy.
0796: **/
0797: protected void reopenScanByRecordHandle(
0798: RecordHandle startRecordHandle, Qualifier qualifier[][])
0799: throws StandardException {
0800: // initialize scan position parameters at beginning of scan
0801: this .scan_state = (!open_conglom.getHold() ? SCAN_INIT
0802: : SCAN_HOLD_INIT);
0803:
0804: // position the scan at the row before the given record id, so that
0805: // the first "next" starts on the given row.
0806: scan_position.current_rh = startRecordHandle;
0807: }
0808:
0809: protected abstract void setRowLocationArray(
0810: RowLocation[] rowloc_array, int index, RowPosition pos)
0811: throws StandardException;
0812:
0813: /**************************************************************************
0814: * abstract protected Methods of This class:
0815: **************************************************************************
0816: */
0817:
0818: /**************************************************************************
0819: * Public Methods of This class:
0820: **************************************************************************
0821: */
0822: public void init(OpenConglomerate open_conglom,
0823: FormatableBitSet scanColumnList,
0824: DataValueDescriptor[] startKeyValue,
0825: int startSearchOperator, Qualifier qualifier[][],
0826: DataValueDescriptor[] stopKeyValue, int stopSearchOperator)
0827: throws StandardException {
0828: super .init(open_conglom);
0829:
0830: // RESOLVE (mikem) - move this into runtime_mem
0831: scan_position = allocateScanPosition();
0832:
0833: // remember inputs
0834: init_scanColumnList = scanColumnList;
0835:
0836: positionAtInitScan(startKeyValue, startSearchOperator,
0837: qualifier, stopKeyValue, stopSearchOperator,
0838: scan_position);
0839:
0840: reusableRecordIdSequenceNumber = open_conglom.getContainer()
0841: .getReusableRecordIdSequenceNumber();
0842: }
0843:
0844: public final int getNumPagesVisited() {
0845: return (stat_numpages_visited);
0846: }
0847:
0848: public final int getNumRowsVisited() {
0849: return (stat_numrows_visited);
0850: }
0851:
0852: public final int getNumRowsQualified() {
0853: return (stat_numrows_qualified);
0854: }
0855:
0856: public final FormatableBitSet getScanColumnList() {
0857: return (init_scanColumnList);
0858: }
0859:
0860: public final DataValueDescriptor[] getStartKeyValue() {
0861: return (init_startKeyValue);
0862: }
0863:
0864: public final int getStartSearchOperator() {
0865: return (init_startSearchOperator);
0866: }
0867:
0868: public final DataValueDescriptor[] getStopKeyValue() {
0869: return (init_stopKeyValue);
0870: }
0871:
0872: public final int getStopSearchOperator() {
0873: return (init_stopSearchOperator);
0874: }
0875:
0876: public final Qualifier[][] getQualifier() {
0877: return (init_qualifier);
0878: }
0879:
0880: public final int getScanState() {
0881: return (scan_state);
0882: }
0883:
0884: public final void setScanState(int state) {
0885: scan_state = state;
0886: }
0887:
0888: public final RowPosition getScanPosition() {
0889: return (scan_position);
0890: }
0891:
0892: public final void setScanPosition(RowPosition pos) {
0893: scan_position = pos;
0894: }
0895:
0896: /**************************************************************************
0897: * Public Methods implementing ScanController:
0898: **************************************************************************
0899: */
0900: private void closeScan() throws StandardException {
0901: super .close();
0902:
0903: // If we are closed due to catching an error in the middle of init,
0904: // xact_manager may not be set yet.
0905: if (open_conglom.getXactMgr() != null)
0906: open_conglom.getXactMgr().closeMe(this );
0907:
0908: // help the garbage collector.
0909: this .init_qualifier = null;
0910: init_scanColumnList = null;
0911: init_startKeyValue = null;
0912: init_stopKeyValue = null;
0913: }
0914:
0915: public void close() throws StandardException {
0916: // Finish the scan - this may release locks if read committed and scan
0917: // still holds some locks, and close comes before scan.next() returned
0918: // that scan was done.
0919: positionAtDoneScan(scan_position);
0920:
0921: closeScan();
0922: }
0923:
0924: /**
0925: * Reopens the scan after it has been closed as part of a commit.
0926: * This method will check the reusableRecordIdSequenceNumber of the
0927: * container, and will set the rowLocationsInvalidated flag if it has
0928: * changed.
0929: * @return true if the conglomerate has been reopened
0930: * @exception StandardException Derby standard exception
0931: */
0932: protected final boolean reopenAfterEndTransaction()
0933: throws StandardException {
0934: // Only reopen if holdable
0935: if (!open_conglom.getHold()) {
0936: return (false);
0937: }
0938:
0939: ContainerHandle container = open_conglom.reopen();
0940: switch (scan_state) {
0941: case SCAN_INPROGRESS:
0942: case SCAN_HOLD_INPROGRESS:
0943: case SCAN_DONE:
0944: if (container.getReusableRecordIdSequenceNumber() != reusableRecordIdSequenceNumber) {
0945: rowLocationsInvalidated = true;
0946: }
0947: break;
0948: case SCAN_INIT:
0949: case SCAN_HOLD_INIT:
0950: reusableRecordIdSequenceNumber = container
0951: .getReusableRecordIdSequenceNumber();
0952: break;
0953: default:
0954: break;
0955: }
0956: return (true);
0957: }
0958:
0959: public boolean closeForEndTransaction(boolean closeHeldScan)
0960: throws StandardException {
0961: if ((!open_conglom.getHold()) || closeHeldScan) {
0962: // close the scan as part of the commit/abort
0963:
0964: this .scan_state = SCAN_DONE;
0965:
0966: closeScan();
0967:
0968: return (true);
0969: } else {
0970: super .close();
0971:
0972: // allow the scan to continue after the commit.
0973: // locks and latches will be released as part of the commit, so
0974: // no need to release them by hand.
0975:
0976: if (this .scan_state == SCAN_INPROGRESS)
0977: this .scan_state = SCAN_HOLD_INPROGRESS;
0978: else if (this .scan_state == SCAN_INIT)
0979: this .scan_state = SCAN_HOLD_INIT;
0980:
0981: return (false);
0982: }
0983: }
0984:
0985: /**
0986: @see ScanController#delete
0987: **/
0988: public boolean delete() throws StandardException {
0989: repositionScanForUpateOper();
0990:
0991: boolean ret_val = true;
0992:
0993: // RESOLVE (mikem) - RECID - performance could be better if we did not
0994: // have to call isDeletedAtSlot().
0995:
0996: // RESOLVE (mikem) - share code below with conglomerateController.
0997:
0998: if (scan_position.current_page
0999: .isDeletedAtSlot(scan_position.current_slot)) {
1000: ret_val = false;
1001: } else {
1002: // Delete the row
1003: scan_position.current_page.deleteAtSlot(
1004: scan_position.current_slot, true,
1005: (LogicalUndo) null);
1006:
1007: if (scan_position.current_page.nonDeletedRecordCount() == 0) {
1008: queueDeletePostCommitWork(scan_position);
1009: }
1010: }
1011:
1012: scan_position.unlatch();
1013:
1014: return (ret_val);
1015: }
1016:
1017: /**
1018: * A call to allow client to indicate that current row does not qualify.
1019: * <p>
1020: * Indicates to the ScanController that the current row does not
1021: * qualify for the scan. If the isolation level of the scan allows,
1022: * this may result in the scan releasing the lock on this row.
1023: * <p>
1024: * Note that some scan implimentations may not support releasing locks on
1025: * non-qualifying rows, or may delay releasing the lock until sometime
1026: * later in the scan (ie. it may be necessary to keep the lock until
1027: * either the scan is repositioned on the next row or page).
1028: * <p>
1029: * This call should only be made while the scan is positioned on a current
1030: * valid row.
1031: * RESOLVE (mikem-05/29/98) - Implement this when we support levels of
1032: * concurrency less than serializable.
1033: *
1034: * @exception StandardException Standard exception policy.
1035: **/
1036: public void didNotQualify() throws StandardException {
1037: }
1038:
1039: /**
1040: * Insert all rows that qualify for the current scan into the input
1041: * Hash table.
1042: * <p>
1043: * This routine scans executes the entire scan as described in the
1044: * openScan call. For every qualifying unique row value an entry is
1045: * placed into the HashTable. For unique row values the entry in the
1046: * Hashtable has a key value of the object stored in
1047: * row[key_column_number], and the value of the data is row. For row
1048: * values with duplicates, the key value is also row[key_column_number],
1049: * but the value of the data is a Vector of
1050: * rows. The caller will have to call "instanceof" on the data value
1051: * object if duplicates are expected, to determine if the data value
1052: * of the Hashtable entry is a row or is a Vector of rows.
1053: * <p>
1054: * Note, that for this routine to work efficiently the caller must
1055: * ensure that the object in row[key_column_number] implements
1056: * the hashCode and equals method as appropriate for it's datatype.
1057: * <p>
1058: * It is expected that this call will be the first and only call made in
1059: * an openscan. Qualifiers and stop position of the openscan are applied
1060: * just as in a normal scan. This call is logically equivalent to the
1061: * caller performing the following:
1062: *
1063: * import java.util.Hashtable;
1064: *
1065: * hash_table = new Hashtable();
1066: *
1067: * while (next())
1068: * {
1069: * row = create_new_row();
1070: * fetch(row);
1071: * if ((duplicate_value =
1072: * hash_table.put(row[key_column_number], row)) != null)
1073: * {
1074: * Vector row_vec;
1075: *
1076: * // inserted a duplicate
1077: * if ((duplicate_value instanceof vector))
1078: * {
1079: * row_vec = (Vector) duplicate_value;
1080: * }
1081: * else
1082: * {
1083: * // allocate vector to hold duplicates
1084: * row_vec = new Vector(2);
1085: *
1086: * // insert original row into vector
1087: * row_vec.addElement(duplicate_value);
1088: *
1089: * // put the vector as the data rather than the row
1090: * hash_table.put(row[key_column_number], row_vec);
1091: * }
1092: *
1093: * // insert new row into vector
1094: * row_vec.addElement(row);
1095: * }
1096: * }
1097: * <p>
1098: * The columns of the row will be the standard columns returned as
1099: * part of a scan, as described by the validColumns - see openScan for
1100: * description.
1101: * RESOLVE - is this ok? or should I hard code somehow the row to
1102: * be the first column and the row location?
1103: * <p>
1104: * Currently it is only possible to hash on the first column in the
1105: * conglomerate, in the future we may change the interface to allow
1106: * hashing either on a different column or maybe on a combination of
1107: * columns.
1108: * <p>
1109: * No overflow to external storage is provided, so calling this routine
1110: * on a 1 gigabyte conglomerate will incur at least 1 gigabyte of memory
1111: * (probably failing with a java out of memory condition). If this
1112: * routine gets an out of memory condition, or if "max_rowcnt" is
1113: * exceeded then then the routine will give up, empty the Hashtable,
1114: * and return "false."
1115: * <p>
1116: * On exit from this routine, whether the fetchSet() succeeded or not
1117: * the scan is complete, it is positioned just the same as if the scan
1118: * had been drained by calling "next()" until it returns false (ie.
1119: * fetchNext() and next() calls will return false).
1120: * reopenScan() can be called to restart the scan.
1121: * <p>
1122: *
1123: * RESOLVE - until we get row counts what should we do for sizing the
1124: * the size, capasity, and load factor of the hash table.
1125: * For now it is up to the caller to create the Hashtable,
1126: * Access does not reset any parameters.
1127: * <p>
1128: * RESOLVE - I am not sure if access should be in charge of allocating
1129: * the new row objects. I know that I can do this in the
1130: * case of btree's, but I don't think I can do this in heaps.
1131: * Maybe this is solved by work to be done on the sort
1132: * interface.
1133: *
1134: *
1135: * @param max_rowcnt The maximum number of rows to insert into the
1136: * Hash table. Pass in -1 if there is no maximum.
1137: * @param key_column_numbers The column numbers of the columns in the
1138: * scan result row to be the key to the Hashtable.
1139: * "0" is the first column in the scan result
1140: * row (which may be different than the first
1141: * column in the row in the table of the scan).
1142: * @param hash_table The java HashTable to load into.
1143: *
1144: * @exception StandardException Standard exception policy.
1145: **/
1146: public void fetchSet(long max_rowcnt, int[] key_column_numbers,
1147: BackingStoreHashtable hash_table) throws StandardException {
1148: fetchRows((DataValueDescriptor[][]) null, (RowLocation[]) null,
1149: hash_table, max_rowcnt, key_column_numbers);
1150:
1151: return;
1152: }
1153:
1154: /**
1155: Reposition the current scan. This call is semantically the same as if
1156: the current scan had been closed and a openScan() had been called instead.
1157: The scan is reopened with against the same conglomerate, and the scan
1158: is reopened with the same "hold" and "forUpdate" parameters passed in
1159: the original openScan. The previous template row continues to be used.
1160:
1161: @param startKeyValue An indexable row which holds a
1162: (partial) key value which, in combination with the
1163: startSearchOperator, defines the starting position of
1164: the scan. If null, the starting position of the scan
1165: is the first row of the conglomerate.
1166:
1167: @param startSearchOperator an operator which defines
1168: how the startKeyValue is to be searched for. If
1169: startSearchOperator is ScanController.GE, the scan starts on
1170: the first row which is greater than or equal to the
1171: startKeyValue. If startSearchOperation is ScanController.GT,
1172: the scan starts on the first row whose key is greater than
1173: startKeyValue. The startSearchOperation parameter is
1174: ignored if the startKeyValue parameter is null.
1175:
1176: @param qualifier An array of qualifiers which, applied
1177: to each key, restrict the rows returned by the scan. Rows
1178: for which any one of the qualifiers returns false are not
1179: returned by the scan. If null, all rows are returned.
1180:
1181: @param stopKeyValue An indexable row which holds a
1182: (partial) key value which, in combination with the
1183: stopSearchOperator, defines the ending position of
1184: the scan. If null, the ending position of the scan
1185: is the last row of the conglomerate.
1186:
1187: @param stopSearchOperator an operator which defines
1188: how the stopKeyValue is used to determine the scan stopping
1189: position. If stopSearchOperation is ScanController.GE, the scan
1190: stops just before the first row which is greater than or
1191: equal to the stopKeyValue. If stopSearchOperation is
1192: ScanController.GT, the scan stops just before the first row whose
1193: key is greater than startKeyValue. The stopSearchOperation
1194: parameter is ignored if the stopKeyValue parameter is null.
1195:
1196: @exception StandardException Standard exception policy.
1197: **/
1198: public void reopenScan(DataValueDescriptor[] startKeyValue,
1199: int startSearchOperator, Qualifier qualifier[][],
1200: DataValueDescriptor[] stopKeyValue, int stopSearchOperator)
1201: throws StandardException {
1202: if (SanityManager.DEBUG) {
1203: if (!open_conglom.getHold()) {
1204: SanityManager
1205: .ASSERT(!open_conglom.isClosed(),
1206: "GenericScanController.reopenScan() called on a non-held closed scan.");
1207: }
1208: }
1209:
1210: // initialize scan position parameters at beginning of scan
1211: this .scan_state = (!open_conglom.getHold() ? SCAN_INIT
1212: : SCAN_HOLD_INIT);
1213:
1214: scan_position.current_rh = null;
1215: }
1216:
1217: /**
1218: @see ScanController#replace
1219: **/
1220: public boolean replace(DataValueDescriptor[] row,
1221: FormatableBitSet validColumns) throws StandardException {
1222: repositionScanForUpateOper();
1223:
1224: boolean ret_val = scan_position.current_page.update(
1225: scan_position.current_rh, row, validColumns);
1226:
1227: scan_position.unlatch();
1228:
1229: return (ret_val);
1230: }
1231:
1232: /**
1233: Returns true if the current position of the scan still qualifies
1234: under the set of qualifiers passed to the openScan(). When called
1235: this routine will reapply all qualifiers against the row currently
1236: positioned and return true if the row still qualifies. If the row
1237: has been deleted or no longer passes the qualifiers then this routine
1238: will return false.
1239:
1240: This case can come about if the current scan
1241: or another scan on the same table in the same transaction
1242: deleted the row or changed columns referenced by the qualifier after
1243: the next() call which positioned the scan at this row.
1244:
1245: Note that for comglomerates which don't support update, like btree's,
1246: there is no need to recheck the qualifiers.
1247:
1248: The results of a fetch() performed on a scan positioned on
1249: a deleted row are undefined.
1250:
1251: @exception StandardException Standard exception policy.
1252: **/
1253: public boolean doesCurrentPositionQualify()
1254: throws StandardException {
1255: if (scan_state != SCAN_INPROGRESS)
1256: throw StandardException
1257: .newException(SQLState.AM_SCAN_NOT_POSITIONED);
1258:
1259: if (!open_conglom.latchPage(scan_position)) {
1260: return (false);
1261: }
1262:
1263: DataValueDescriptor row[] = open_conglom.getRuntimeMem()
1264: .get_scratch_row();
1265:
1266: // If fetchFromSlot returns null it either means the row is deleted,
1267: // or the qualifier evaluates to false.
1268:
1269: boolean ret_val = (scan_position.current_page.fetchFromSlot(
1270: scan_position.current_rh, scan_position.current_slot,
1271: row, init_fetchDesc, false) != null);
1272:
1273: scan_position.unlatch();
1274:
1275: return (ret_val);
1276: }
1277:
1278: /**
1279: Fetch the row at the current position of the Scan without applying the
1280: qualifiers.
1281:
1282: @see ScanController#fetchWithoutQualify
1283: **/
1284: public void fetchWithoutQualify(DataValueDescriptor[] row)
1285: throws StandardException {
1286: fetch(row, false);
1287: }
1288:
1289: /**
1290: Fetch the row at the current position of the Scan.
1291:
1292: @see ScanController#fetch
1293: **/
1294: public void fetch(DataValueDescriptor[] row)
1295: throws StandardException {
1296: fetch(row, true);
1297: }
1298:
1299: /**
1300: Fetch the row at the current position of the Scan.
1301:
1302: @param row The row into which the value of the current
1303: position in the scan is to be stored.
1304:
1305: @param qualify Indicates whether the qualifiers should be applied.
1306:
1307: @exception StandardException Standard exception policy.
1308: **/
1309: private void fetch(DataValueDescriptor[] row, boolean qualify)
1310: throws StandardException {
1311: if (scan_state != SCAN_INPROGRESS)
1312: throw StandardException
1313: .newException(SQLState.AM_SCAN_NOT_POSITIONED);
1314:
1315: if (!open_conglom.latchPage(scan_position)) {
1316: throw StandardException.newException(
1317: SQLState.AM_RECORD_NOT_FOUND, open_conglom
1318: .getContainer().getId(), new Long(
1319: scan_position.current_rh.getId()));
1320: }
1321:
1322: // RESOLVE (mikem) - should this call apply the qualifiers again?
1323: RecordHandle rh = scan_position.current_page.fetchFromSlot(
1324: scan_position.current_rh, scan_position.current_slot,
1325: row, qualify ? init_fetchDesc : null, false);
1326:
1327: scan_position.unlatch();
1328:
1329: if (rh == null) {
1330: /*
1331: if (SanityManager.DEBUG)
1332: {
1333: if (isCurrentPositionDeleted())
1334: SanityManager.THROWASSERT(
1335: "The record (" +
1336: open_conglom.getContainer().getId() +
1337: ", " +
1338: scan_position.current_rh.getPageNumber() + ", " +
1339: scan_position.current_rh.getId() + ") " +
1340: "being fetched is marked deleted on page.:\n");
1341: }
1342: */
1343:
1344: throw StandardException.newException(
1345: SQLState.AM_RECORD_NOT_FOUND, open_conglom
1346: .getContainer().getId(), new Long(
1347: scan_position.current_rh.getId()));
1348: }
1349:
1350: return;
1351: }
1352:
1353: /**
1354: Fetch the location of the current position in the scan.
1355: @see ScanController#fetchLocation
1356:
1357: @exception StandardException Standard exception policy.
1358: **/
1359: public void fetchLocation(RowLocation templateLocation)
1360: throws StandardException {
1361: throw StandardException
1362: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1363: }
1364:
1365: /**
1366: * Return ScanInfo object which describes performance of scan.
1367: * <p>
1368: * Return ScanInfo object which contains information about the current
1369: * scan.
1370: * <p>
1371: *
1372: * @see ScanInfo
1373: *
1374: * @return The ScanInfo object which contains info about current scan.
1375: *
1376: * @exception StandardException Standard exception policy.
1377: **/
1378: public ScanInfo getScanInfo() throws StandardException {
1379: throw StandardException
1380: .newException(SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1381: }
1382:
1383: /**
1384: Returns true if the current position of the scan is at a
1385: deleted row. This case can come about if the current scan
1386: or another scan on the same table in the same transaction
1387: deleted the row after the next() call which positioned the
1388: scan at this row.
1389:
1390: The results of a fetch() performed on a scan positioned on
1391: a deleted row are undefined.
1392:
1393: @exception StandardException Standard exception policy.
1394: **/
1395: public boolean isCurrentPositionDeleted() throws StandardException {
1396: if (scan_state != SCAN_INPROGRESS)
1397: throw StandardException
1398: .newException(SQLState.AM_SCAN_NOT_POSITIONED);
1399:
1400: if (!open_conglom.latchPage(scan_position)) {
1401: return (true);
1402: }
1403:
1404: boolean ret_val = scan_position.current_page
1405: .isDeletedAtSlot(scan_position.current_slot);
1406:
1407: scan_position.unlatch();
1408:
1409: return (ret_val);
1410: }
1411: }
|