--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.access.heap.HeapScan\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+*/\r
+\r
+package org.apache.derby.impl.store.access.heap;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.store.access.SpaceInfo;\r
+\r
+import org.apache.derby.impl.store.access.conglomerate.RowPosition;\r
+\r
+import org.apache.derby.iapi.store.raw.ContainerHandle;\r
+import org.apache.derby.iapi.store.raw.Page;\r
+import org.apache.derby.iapi.store.raw.RecordHandle;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+/**\r
+ * A heap scan object represents an instance of a scan on a heap conglomerate.\r
+ */\r
+class HeapCompressScan \r
+ extends HeapScan\r
+{\r
+\r
+ /**************************************************************************\r
+ * Constants of HeapScan\r
+ **************************************************************************\r
+ */\r
+\r
+ /**************************************************************************\r
+ * Fields of HeapScan\r
+ **************************************************************************\r
+ */\r
+ private long pagenum_to_start_moving_rows = -1;\r
+\r
+\r
+\r
+ /**************************************************************************\r
+ * Constructors for This class:\r
+ **************************************************************************\r
+ */\r
+\r
+ /**\r
+ ** The only constructor for a HeapCompressScan returns a scan in the\r
+ ** closed state, the caller must call open.\r
+ **/\r
+ \r
+ public HeapCompressScan()\r
+ {\r
+ }\r
+\r
+ /**************************************************************************\r
+ * Protected override implementation of routines in\r
+ * GenericController class:\r
+ **************************************************************************\r
+ */\r
+\r
+ public int fetchNextGroup(\r
+ DataValueDescriptor[][] row_array,\r
+ RowLocation[] old_rowloc_array,\r
+ RowLocation[] new_rowloc_array)\r
+ throws StandardException\r
+ {\r
+ return(fetchRowsForCompress(\r
+ row_array, old_rowloc_array, new_rowloc_array));\r
+ }\r
+\r
+ /**\r
+ * Fetch the next N rows from the table.\r
+ * <p>\r
+ * Utility routine used by both fetchSet() and fetchNextGroup().\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ private int fetchRowsForCompress(\r
+ DataValueDescriptor[][] row_array,\r
+ RowLocation[] oldrowloc_array,\r
+ RowLocation[] newrowloc_array)\r
+ throws StandardException\r
+ {\r
+ int ret_row_count = 0;\r
+ DataValueDescriptor[] fetch_row = null;\r
+\r
+ // only fetch maximum number of rows per "group" as the size of\r
+ // the array. If more than one group is available on page, just\r
+ // leave the scan on the page and the next group will come from\r
+ // this page also.\r
+ int max_rowcnt = row_array.length;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(row_array != null);\r
+ SanityManager.ASSERT(row_array[0] != null,\r
+ "first array slot in fetchNextGroup() must be non-null.");\r
+ }\r
+\r
+ if (getScanState() == SCAN_INPROGRESS)\r
+ {\r
+ positionAtResumeScan(scan_position);\r
+ }\r
+ else if (getScanState() == SCAN_INIT)\r
+ {\r
+ // For first implementation of defragment use a conservative\r
+ // approach, only move rows from the last "number of free pages"\r
+ // of the container. Should always at least be able to empty\r
+ // that number of pages.\r
+ SpaceInfo info = \r
+ open_conglom.getContainer().getSpaceInfo();\r
+\r
+ pagenum_to_start_moving_rows = info.getNumAllocatedPages();\r
+\r
+ positionAtStartForForwardScan(scan_position);\r
+ }\r
+ else if (getScanState() == SCAN_HOLD_INPROGRESS)\r
+ {\r
+ reopenAfterEndTransaction();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(\r
+ scan_position.current_rh != null, this.toString()); \r
+ }\r
+\r
+ // reposition the scan at the row just before the next one to \r
+ // return.\r
+ // This routine handles the mess of repositioning if the row or \r
+ // the page has disappeared. This can happen if a lock was not \r
+ // held on the row while not holding the latch.\r
+ open_conglom.latchPageAndRepositionScan(scan_position);\r
+\r
+ setScanState(SCAN_INPROGRESS);\r
+ }\r
+ else if (getScanState() == SCAN_HOLD_INIT)\r
+ {\r
+ reopenAfterEndTransaction();\r
+\r
+ positionAtStartForForwardScan(scan_position);\r
+\r
+ }\r
+ else\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(getScanState() == SCAN_DONE);\r
+\r
+ return(0);\r
+ }\r
+\r
+ // At this point:\r
+ // scan_position.current_page is latched. \r
+ // scan_position.current_slot is the slot on scan_position.current_page\r
+ // just before the "next" record this routine should process.\r
+\r
+ // loop through successive pages and successive slots on those\r
+ // pages. Stop when either the last page is reached \r
+ // (scan_position.current_page will be null). \r
+ // Along the way apply qualifiers to skip rows which don't qualify.\r
+\r
+ while (scan_position.current_page != null)\r
+ {\r
+ while ((scan_position.current_slot + 1) < \r
+ scan_position.current_page.recordCount())\r
+ {\r
+\r
+ // Allocate a new row to read the row into.\r
+ if (fetch_row == null)\r
+ {\r
+ // point at allocated row in array if one exists.\r
+ if (row_array[ret_row_count] == null)\r
+ {\r
+ row_array[ret_row_count] = \r
+ open_conglom.getRuntimeMem().get_row_for_export(\r
+ open_conglom.getRawTran());\r
+ }\r
+\r
+ fetch_row = row_array[ret_row_count];\r
+ }\r
+\r
+ // move scan current position forward.\r
+ scan_position.positionAtNextSlot();\r
+ int restart_slot = scan_position.current_slot;\r
+\r
+ this.stat_numrows_visited++;\r
+\r
+ if (scan_position.current_page.isDeletedAtSlot(\r
+ scan_position.current_slot))\r
+ {\r
+ // At this point assume table level lock, and that this\r
+ // transcation did not delete the row, so any\r
+ // deleted row must be a committed deleted row which can\r
+ // be purged.\r
+ scan_position.current_page.purgeAtSlot(\r
+ scan_position.current_slot, 1, false);\r
+\r
+ // raw store shuffles following rows down, so \r
+ // postion the scan at previous slot, so next trip\r
+ // through loop will pick up correct row.\r
+ scan_position.positionAtPrevSlot();\r
+ continue;\r
+ }\r
+\r
+ if (scan_position.current_page.getPageNumber() > \r
+ pagenum_to_start_moving_rows)\r
+ {\r
+ // Give raw store a chance to move the row for compression\r
+ RecordHandle[] old_handle = new RecordHandle[1];\r
+ RecordHandle[] new_handle = new RecordHandle[1];\r
+ long[] new_pageno = new long[1];\r
+\r
+ if (scan_position.current_page.moveRecordForCompressAtSlot(\r
+ scan_position.current_slot,\r
+ fetch_row,\r
+ old_handle,\r
+ new_handle) == 1)\r
+ {\r
+ // raw store moved the row, so bump the row count but \r
+ // position the scan at previous slot, so next trip\r
+ // through loop will pick up correct row.\r
+ // The subsequent rows will have been moved forward\r
+ // to take place of moved row.\r
+ scan_position.positionAtPrevSlot();\r
+\r
+ ret_row_count++;\r
+ stat_numrows_qualified++;\r
+\r
+\r
+ setRowLocationArray(\r
+ oldrowloc_array, ret_row_count - 1, old_handle[0]);\r
+ setRowLocationArray(\r
+ newrowloc_array, ret_row_count - 1, new_handle[0]);\r
+\r
+ fetch_row = null;\r
+\r
+ }\r
+ }\r
+\r
+ // Derby-2549. If ret_row_count reaches the limit of the buffer,\r
+ // then return the maximum number and come back into the same \r
+ // method to fetch the remaining rows. In this block we ensure\r
+ // that the scan_position is appropriate.\r
+ if (ret_row_count >= max_rowcnt)\r
+ {\r
+ // filled group buffer, exit fetch loop and return to caller\r
+\r
+ // save current scan position by record handle.\r
+ scan_position.current_rh =\r
+ scan_position.current_page.getRecordHandleAtSlot(\r
+ restart_slot);\r
+\r
+ scan_position.unlatch();\r
+\r
+ return(ret_row_count);\r
+ }\r
+ }\r
+\r
+ this.stat_numpages_visited++;\r
+\r
+ if (scan_position.current_page.recordCount() == 0)\r
+ {\r
+ // need to set the scan position before removing page\r
+ scan_position.current_pageno = \r
+ scan_position.current_page.getPageNumber();\r
+\r
+ open_conglom.getContainer().removePage(\r
+ scan_position.current_page);\r
+\r
+ // removePage unlatches the page, and page not available\r
+ // again until after commit.\r
+ scan_position.current_page = null;\r
+ }\r
+ else\r
+ {\r
+ positionAfterThisPage(scan_position);\r
+ scan_position.unlatch();\r
+ }\r
+\r
+\r
+ if (ret_row_count > 0)\r
+ {\r
+ // rows were moved on this page, give caller a chance to\r
+ // process those and free up access to the table.\r
+ return(ret_row_count);\r
+ }\r
+ else\r
+ {\r
+ // no rows were moved so go ahead and commit the transaction\r
+ // to allow other threads a chance at table. Compress does\r
+ // need to sync as long as transaction either completely \r
+ // commits or backs out, either is fine.\r
+ /*\r
+ open_conglom.getXactMgr().commitNoSync(\r
+ TransactionController.RELEASE_LOCKS);\r
+ open_conglom.reopen();\r
+ */\r
+ positionAtResumeScan(scan_position);\r
+\r
+ }\r
+ }\r
+\r
+ // Reached last page of scan.\r
+ positionAtDoneScan(scan_position);\r
+\r
+ // we need to decrement when we stop scan at the end of the table.\r
+ this.stat_numpages_visited--;\r
+\r
+ return(ret_row_count);\r
+ }\r
+\r
+ /**\r
+ * Reposition the scan upon entering the fetchRows loop.\r
+ * <p>\r
+ * Called upon entering fetchRows() while in the SCAN_INPROGRESS state.\r
+ * Do work necessary to look at rows in the current page of the scan.\r
+ * <p>\r
+ * The default implementation uses a record handle to maintain a scan\r
+ * position. It will get the latch again on the current\r
+ * scan position and set the slot to the current record handle.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected void positionAtResumeScan(\r
+ RowPosition pos)\r
+ throws StandardException\r
+ {\r
+ // reposition the scan at the row just before the next one to return.\r
+ // This routine handles the mess of repositioning if the row or the\r
+ // page has disappeared. This can happen if a lock was not held on the\r
+ // row while not holding the latch.\r
+ open_conglom.latchPageAndRepositionScan(scan_position);\r
+ }\r
+\r
+ /**\r
+ * Move the scan from SCAN_INIT to SCAN_INPROGRESS.\r
+ * <p>\r
+ * This routine is called to move the scan from SCAN_INIT to \r
+ * SCAN_INPROGRESS. Upon return from this routine it is expected\r
+ * that scan_position is set such that calling the generic \r
+ * scan loop will reach the first row of the scan. Note that this\r
+ * usually means setting the scan_postion to one before the 1st \r
+ * row to be returned.\r
+ * <p>\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected void positionAtStartForForwardScan(\r
+ RowPosition pos)\r
+ throws StandardException\r
+ {\r
+ if (pos.current_rh == null)\r
+ {\r
+ // 1st positioning of scan (delayed from openScan). Do not\r
+ // compress the first page, there is no previous page to move\r
+ // rows to, and moving the special Heap metadata row from the\r
+ // first page would cause problems. Setting to next page is\r
+ // why this scan overrides generic implementation.\r
+ pos.current_page = \r
+ open_conglom.getContainer().getNextPage(\r
+ ContainerHandle.FIRST_PAGE_NUMBER);\r
+\r
+ // set up for scan to continue at beginning of page following\r
+ // the first page of the container.\r
+ pos.current_slot = Page.FIRST_SLOT_NUMBER - 1;\r
+ }\r
+ else\r
+ {\r
+ // 1st positioning of scan following a reopenScanByRowLocation\r
+\r
+ // reposition the scan at the row just before the next one to \r
+ // return. This routine handles the mess of repositioning if the \r
+ // row or the page has disappeared. This can happen if a lock was \r
+ // not held on the row while not holding the latch.\r
+ open_conglom.latchPageAndRepositionScan(pos);\r
+\r
+ // set up for scan to at the specified record handle (position one\r
+ // before it so that the loop increment and find it).\r
+ pos.current_slot -= 1;\r
+ }\r
+\r
+ pos.current_rh = null;\r
+ this.stat_numpages_visited = 1;\r
+ this.setScanState(SCAN_INPROGRESS);\r
+ }\r
+\r
+\r
+ /**************************************************************************\r
+ * Private/Protected methods of This class:\r
+ **************************************************************************\r
+ */\r
+\r
+ /**\r
+ * Set scan position to just after current page.\r
+ * <p>\r
+ * Used to set the position of the scan if a record handle is not\r
+ * avaliable. In this case current_rh will be set to null, and \r
+ * current_pageno will be set to the current page number.\r
+ * On resume of the scan, the scan will be set to just before the first\r
+ * row returned form a getNextPage(current_pageno) call.\r
+ * <p>\r
+ * A positionAtResumeScan(scan_position) is necessary to continue the\r
+ * scan after this call.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ private void positionAfterThisPage(\r
+ RowPosition pos)\r
+ throws StandardException\r
+ {\r
+ pos.current_rh = null;\r
+ pos.current_pageno = pos.current_page.getPageNumber();\r
+ }\r
+\r
+ /*\r
+ ** Methods of ScanManager\r
+ */\r
+\r
+}\r