001: /*
002:
003: Derby - Class org.apache.derby.impl.store.access.heap.HeapScan
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.store.access.heap;
023:
024: /**
025:
026: A heap scan object represents an instance of an scan on a heap conglomerate.
027:
028: **/
029:
030: import org.apache.derby.iapi.reference.SQLState;
031:
032: import org.apache.derby.iapi.services.sanity.SanityManager;
033:
034: import org.apache.derby.iapi.services.io.Storable;
035:
036: import org.apache.derby.iapi.error.StandardException;
037:
038: import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;
039: import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
040: import org.apache.derby.iapi.store.access.conglomerate.ScanManager;
041: import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
042:
043: import org.apache.derby.iapi.store.access.ConglomerateController;
044: import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
045: import org.apache.derby.iapi.store.access.Qualifier;
046: import org.apache.derby.iapi.store.access.RowUtil;
047: import org.apache.derby.iapi.store.access.ScanInfo;
048: import org.apache.derby.iapi.store.access.ScanController;
049: import org.apache.derby.iapi.store.access.SpaceInfo;
050: import org.apache.derby.iapi.store.access.TransactionController;
051:
052: import org.apache.derby.iapi.types.RowLocation;
053:
054: import org.apache.derby.iapi.store.raw.ContainerHandle;
055: import org.apache.derby.iapi.store.raw.LockingPolicy;
056: import org.apache.derby.iapi.store.raw.Transaction;
057: import org.apache.derby.iapi.store.raw.Page;
058: import org.apache.derby.iapi.store.raw.RecordHandle;
059:
060: import org.apache.derby.iapi.types.DataValueDescriptor;
061:
062: import org.apache.derby.impl.store.access.conglomerate.ConglomerateUtil;
063: import org.apache.derby.impl.store.access.conglomerate.GenericScanController;
064: import org.apache.derby.impl.store.access.conglomerate.RowPosition;
065:
066: import org.apache.derby.iapi.store.access.BackingStoreHashtable;
067: import org.apache.derby.iapi.services.io.FormatableBitSet;
068:
069: import java.util.Hashtable;
070: import java.util.Vector;
071:
072: class HeapCompressScan extends HeapScan {
073:
074: /**************************************************************************
075: * Constants of HeapScan
076: **************************************************************************
077: */
078:
079: /**************************************************************************
080: * Fields of HeapScan
081: **************************************************************************
082: */
083: private long pagenum_to_start_moving_rows = -1;
084:
085: /**************************************************************************
086: * Constructors for This class:
087: **************************************************************************
088: */
089:
090: /**
091: ** The only constructor for a HeapCompressScan returns a scan in the
092: ** closed state, the caller must call open.
093: **/
094:
095: public HeapCompressScan() {
096: }
097:
098: /**************************************************************************
099: * Protected override implementation of routines in
100: * GenericController class:
101: **************************************************************************
102: */
103:
104: public int fetchNextGroup(DataValueDescriptor[][] row_array,
105: RowLocation[] old_rowloc_array,
106: RowLocation[] new_rowloc_array) throws StandardException {
107: return (fetchRowsForCompress(row_array, old_rowloc_array,
108: new_rowloc_array));
109: }
110:
111: /**
112: * Fetch the next N rows from the table.
113: * <p>
114: * Utility routine used by both fetchSet() and fetchNextGroup().
115: *
116: * @exception StandardException Standard exception policy.
117: **/
118: private int fetchRowsForCompress(DataValueDescriptor[][] row_array,
119: RowLocation[] oldrowloc_array, RowLocation[] newrowloc_array)
120: throws StandardException {
121: int ret_row_count = 0;
122: DataValueDescriptor[] fetch_row = null;
123:
124: if (SanityManager.DEBUG) {
125: SanityManager.ASSERT(row_array != null);
126: SanityManager
127: .ASSERT(row_array[0] != null,
128: "first array slot in fetchNextGroup() must be non-null.");
129: }
130:
131: if (getScanState() == SCAN_INPROGRESS) {
132: positionAtResumeScan(scan_position);
133: } else if (getScanState() == SCAN_INIT) {
134: // For first implementation of defragment use a conservative
135: // approach, only move rows from the last "number of free pages"
136: // of the container. Should always at least be able to empty
137: // that number of pages.
138: SpaceInfo info = open_conglom.getContainer().getSpaceInfo();
139:
140: pagenum_to_start_moving_rows = info.getNumAllocatedPages();
141:
142: positionAtStartForForwardScan(scan_position);
143: } else if (getScanState() == SCAN_HOLD_INPROGRESS) {
144: reopenAfterEndTransaction();
145:
146: if (SanityManager.DEBUG) {
147: SanityManager.ASSERT(scan_position.current_rh != null,
148: this .toString());
149: }
150:
151: // reposition the scan at the row just before the next one to
152: // return.
153: // This routine handles the mess of repositioning if the row or
154: // the page has disappeared. This can happen if a lock was not
155: // held on the row while not holding the latch.
156: open_conglom.latchPageAndRepositionScan(scan_position);
157:
158: setScanState(SCAN_INPROGRESS);
159: } else if (getScanState() == SCAN_HOLD_INIT) {
160: reopenAfterEndTransaction();
161:
162: positionAtStartForForwardScan(scan_position);
163:
164: } else {
165: if (SanityManager.DEBUG)
166: SanityManager.ASSERT(getScanState() == SCAN_DONE);
167:
168: return (0);
169: }
170:
171: // At this point:
172: // scan_position.current_page is latched.
173: // scan_position.current_slot is the slot on scan_position.current_page
174: // just before the "next" record this routine should process.
175:
176: // loop through successive pages and successive slots on those
177: // pages. Stop when either the last page is reached
178: // (scan_position.current_page will be null).
179: // Along the way apply qualifiers to skip rows which don't qualify.
180:
181: while (scan_position.current_page != null) {
182: while ((scan_position.current_slot + 1) < scan_position.current_page
183: .recordCount()) {
184: // Allocate a new row to read the row into.
185: if (fetch_row == null) {
186: // point at allocated row in array if one exists.
187: if (row_array[ret_row_count] == null) {
188: row_array[ret_row_count] = open_conglom
189: .getRuntimeMem().get_row_for_export();
190: }
191:
192: fetch_row = row_array[ret_row_count];
193: }
194:
195: // move scan current position forward.
196: scan_position.positionAtNextSlot();
197:
198: this .stat_numrows_visited++;
199:
200: if (scan_position.current_page
201: .isDeletedAtSlot(scan_position.current_slot)) {
202: // At this point assume table level lock, and that this
203: // transcation did not delete the row, so any
204: // deleted row must be a committed deleted row which can
205: // be purged.
206: scan_position.current_page.purgeAtSlot(
207: scan_position.current_slot, 1, false);
208:
209: // raw store shuffles following rows down, so
210: // postion the scan at previous slot, so next trip
211: // through loop will pick up correct row.
212: scan_position.positionAtPrevSlot();
213: continue;
214: }
215:
216: if (scan_position.current_page.getPageNumber() > pagenum_to_start_moving_rows) {
217: // Give raw store a chance to move the row for compression
218: RecordHandle[] old_handle = new RecordHandle[1];
219: RecordHandle[] new_handle = new RecordHandle[1];
220: long[] new_pageno = new long[1];
221:
222: if (scan_position.current_page
223: .moveRecordForCompressAtSlot(
224: scan_position.current_slot,
225: fetch_row, old_handle, new_handle) == 1) {
226: // raw store moved the row, so bump the row count but
227: // postion the scan at previous slot, so next trip
228: // through loop will pick up correct row.
229: // The subsequent rows will have been moved forward
230: // to take place of moved row.
231: scan_position.positionAtPrevSlot();
232:
233: ret_row_count++;
234: stat_numrows_qualified++;
235:
236: setRowLocationArray(oldrowloc_array,
237: ret_row_count - 1, old_handle[0]);
238: setRowLocationArray(newrowloc_array,
239: ret_row_count - 1, new_handle[0]);
240:
241: fetch_row = null;
242:
243: }
244: }
245: }
246:
247: this .stat_numpages_visited++;
248:
249: if (scan_position.current_page.recordCount() == 0) {
250: // need to set the scan position before removing page
251: scan_position.current_pageno = scan_position.current_page
252: .getPageNumber();
253:
254: open_conglom.getContainer().removePage(
255: scan_position.current_page);
256:
257: // removePage unlatches the page, and page not available
258: // again until after commit.
259: scan_position.current_page = null;
260: } else {
261: positionAfterThisPage(scan_position);
262: scan_position.unlatch();
263: }
264:
265: if (ret_row_count > 0) {
266: // rows were moved on this page, give caller a chance to
267: // process those and free up access to the table.
268: return (ret_row_count);
269: } else {
270: // no rows were moved so go ahead and commit the transaction
271: // to allow other threads a chance at table. Compress does
272: // need to sync as long as transaction either completely
273: // commits or backs out, either is fine.
274: /*
275: open_conglom.getXactMgr().commitNoSync(
276: TransactionController.RELEASE_LOCKS);
277: open_conglom.reopen();
278: */
279: positionAtResumeScan(scan_position);
280:
281: }
282: }
283:
284: // Reached last page of scan.
285: positionAtDoneScan(scan_position);
286:
287: // we need to decrement when we stop scan at the end of the table.
288: this .stat_numpages_visited--;
289:
290: return (ret_row_count);
291: }
292:
293: /**
294: * Reposition the scan upon entering the fetchRows loop.
295: * <p>
296: * Called upon entering fetchRows() while in the SCAN_INPROGRESS state.
297: * Do work necessary to look at rows in the current page of the scan.
298: * <p>
299: * The default implementation uses a record handle to maintain a scan
300: * position. It will get the latch again on the current
301: * scan position and set the slot to the current record handle.
302: *
303: * @exception StandardException Standard exception policy.
304: **/
305: protected void positionAtResumeScan(RowPosition pos)
306: throws StandardException {
307: // reposition the scan at the row just before the next one to return.
308: // This routine handles the mess of repositioning if the row or the
309: // page has disappeared. This can happen if a lock was not held on the
310: // row while not holding the latch.
311: open_conglom.latchPageAndRepositionScan(scan_position);
312: }
313:
314: /**
315: * Move the scan from SCAN_INIT to SCAN_INPROGRESS.
316: * <p>
317: * This routine is called to move the scan from SCAN_INIT to
318: * SCAN_INPROGRESS. Upon return from this routine it is expected
319: * that scan_position is set such that calling the generic
320: * scan loop will reach the first row of the scan. Note that this
321: * usually means setting the scan_postion to one before the 1st
322: * row to be returned.
323: * <p>
324: *
325: * @exception StandardException Standard exception policy.
326: **/
327: protected void positionAtStartForForwardScan(RowPosition pos)
328: throws StandardException {
329: if (pos.current_rh == null) {
330: // 1st positioning of scan (delayed from openScan). Do not
331: // compress the first page, there is no previous page to move
332: // rows to, and moving the special Heap metadata row from the
333: // first page would cause problems. Setting to next page is
334: // why this scan overrides generic implementation.
335: pos.current_page = open_conglom.getContainer().getNextPage(
336: ContainerHandle.FIRST_PAGE_NUMBER);
337:
338: // set up for scan to continue at beginning of page following
339: // the first page of the container.
340: pos.current_slot = Page.FIRST_SLOT_NUMBER - 1;
341: } else {
342: // 1st positioning of scan following a reopenScanByRowLocation
343:
344: // reposition the scan at the row just before the next one to
345: // return. This routine handles the mess of repositioning if the
346: // row or the page has disappeared. This can happen if a lock was
347: // not held on the row while not holding the latch.
348: open_conglom.latchPageAndRepositionScan(pos);
349:
350: // set up for scan to at the specified record handle (position one
351: // before it so that the loop increment and find it).
352: pos.current_slot -= 1;
353: }
354:
355: pos.current_rh = null;
356: this .stat_numpages_visited = 1;
357: this .setScanState(SCAN_INPROGRESS);
358: }
359:
360: /**************************************************************************
361: * Private/Protected methods of This class:
362: **************************************************************************
363: */
364:
365: /**
366: * Set scan position to just after current page.
367: * <p>
368: * Used to set the position of the scan if a record handle is not
369: * avaliable. In this case current_rh will be set to null, and
370: * current_pageno will be set to the current page number.
371: * On resume of the scan, the scan will be set to just before the first
372: * row returned form a getNextPage(current_pageno) call.
373: * <p>
374: * A positionAtResumeScan(scan_position) is necessary to continue the
375: * scan after this call.
376: *
377: * @exception StandardException Standard exception policy.
378: **/
379: private void positionAfterThisPage(RowPosition pos)
380: throws StandardException {
381: pos.current_rh = null;
382: pos.current_pageno = pos.current_page.getPageNumber();
383: }
384:
385: /*
386: ** Methods of ScanManager
387: */
388:
389: }
|