You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by mi...@apache.org on 2009/06/09 19:19:56 UTC

svn commit: r783072 - /db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/store/raw/data/BasePage.java

Author: mikem
Date: Tue Jun  9 17:19:56 2009
New Revision: 783072

URL: http://svn.apache.org/viewvc?rev=783072&view=rev
Log:
DERBY-4182

backporting svn #778926 from trunk to 10.2 branch.

The test is junit based and did not backport cleanly.  I hand tested
the fix and am just backporting the code changes to 10.2 and 10.1.

Before this fix abort of inserts that included clob or blob chains would
destroy the links of the allocated pages of the chains. This would leave
allocated pages that could never be reclaimed either by subsequent post
commit processing or inplace compress. Only offline compress could reclaim
the space. This fix changes insert abort processing to automatically put
all pieces of long columns except for the head page on the free list as part
of the abort.

Note this does not fix existing tables that have had this problem happen in
the past, only stops it from happening. One must run an offline compress to
reclaim this space to fix any instances of this bug prior to this fix. 


Modified:
    db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/store/raw/data/BasePage.java

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/store/raw/data/BasePage.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/store/raw/data/BasePage.java?rev=783072&r1=783071&r2=783072&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/store/raw/data/BasePage.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/store/raw/data/BasePage.java Tue Jun  9 17:19:56 2009
@@ -878,31 +878,93 @@
 
 	}
 
-	/**
-	  
-		When we update a column, it turned into a long column.  Need to change
-		the update to effectively insert a new long column chain.
-
-		@exception StandardException Unexpected exception from the implementation
-	 */
-	protected RecordHandle insertLongColumn(BasePage mainChainPage,
-			LongColumnException lce, byte insertFlag)
+    /**
+     * Routine to insert a long column.
+     * <p>
+     * This code inserts a long column as a linked list of rows on overflow
+     * pages.  This list is pointed to by a small pointer in the main page
+     * row column.  The operation does the following:
+     *     allocate new overflow page
+     *     insert single row filling overflow page
+     *     while (more of column exists)
+     *         allocate new overflow page
+     *         insert single row with next piece of row
+     *         update previous piece to point to this new piece of row
+     *
+     * Same code is called both from an initial insert of a long column and
+     * from a subsequent update that results in a long column.
+     *
+     * @return The recordHandle of the first piece of the long column chain.
+     *
+     * @param mainChainPage The parent page with row piece containing column
+     *                      that will eventually point to this long column
+     *                      chain.
+     * @param lce           The LongColumnException thrown when we recognized
+     *                      that the column being inserted was "long", this 
+     *                      structure is used to cache the info that we have
+     *                      read so far about column.  In the case of an insert
+     *                      of the stream it will have a copy of just the first
+     *                      page of the stream that has already been read once.
+     * @param insertFlag    flags for insert operation.    
+     *
+     *
+     * @exception  StandardException  Standard exception policy.
+     **/
+	protected RecordHandle insertLongColumn(
+    BasePage            mainChainPage,
+    LongColumnException lce, 
+    byte                insertFlag)
 		throws StandardException
 	{
 
-		// Object[] row = new Object[1];
-		// row[0] = (Object) lce.getColumn();
 		Object[] row = new Object[1];
-		row[0] = lce.getColumn();
+		row[0]       = lce.getColumn();
 
 		RecordHandle firstHandle = null;
-		RecordHandle handle = null;
-		RecordHandle prevHandle = null;
-		BasePage curPage = mainChainPage;
-		BasePage prevPage = null;
-		boolean isFirstPage = true;
+		RecordHandle handle      = null;
+		RecordHandle prevHandle  = null;
+		BasePage     curPage     = mainChainPage;
+		BasePage     prevPage    = null;
+		boolean      isFirstPage = true;
+        
+        // undo inserts as purges of all pieces of the overflow column
+        // except for the 1st overflow page pointed at by the main row.  
+        //
+        // Consider a row with one column which is a long column
+        // that takes 2 pages for itself plus an entry in main parent page.
+        // the log records in order for this look something like:
+        //     insert overflow page 1
+        //     insert overflow page 2
+        //     update overflow page 1 record to have pointer to overflow page 2
+        //     insert main row (which has pointer to overflow page 1)
+        //
+        // If this insert gets aborted then something like the following 
+        // happens:
+        //     main row is marked deleted (but ptr to overflow 1 still exists)
+        //     update is aborted so link on page 2 to page 1 is lost
+        //     overflow row on page 2 is marked deleted
+        //     overflow row on page 1 is marked deleted
+        //
+        // There is no way to reclaim page 2 later as the abort of the update
+        // has now lost the link from overflow page 1 to overflow 2, so 
+        // the system has to do it as part of the abort of the insert.  But, 
+        // it can't for page 1 as the main page will attempt to follow
+        // it's link in the deleted row during it's space reclamation and it 
+        // can't tell the difference 
+        // between a row that has been marked deleted as part of an aborted 
+        // insert or as part of a committed delete.  When it follows the link
+        // it could find no page and that could be coded against, but it could 
+        // be that the page is now used by some other overflow row which would 
+        // lead to lots of different kinds of problems.
+        //
+        // So the code leaves the 1st overflow page to be cleaned up with the
+        // main page row is purged, but goes ahead and immediately purges all
+        // the segments that will be lost as part of the links being lost due
+        // to aborted updates.
+        byte after_first_page_insertFlag = 
+            (byte) (insertFlag | Page.INSERT_UNDO_WITH_PURGE);
 
-		// when inserting a long column startCOlumn is just used
+		// when inserting a long column startColumn is just used
 		// as a flag. -1 means the insert is complete, != -1 indicates
 		// more inserts are required.
 		int startColumn = 0;
@@ -936,9 +998,13 @@
 				firstHandle = handle;
 
 			// step 2: insert column portion
-			startColumn = owner.getActionSet().actionInsert(t, curPage, slot, recordId,
-				row, (FormatableBitSet)null, (LogicalUndo) null, insertFlag,
-				startColumn, true, -1, (DynamicByteArrayOutputStream) null, -1, 100);
+			startColumn = 
+                owner.getActionSet().actionInsert(
+                    t, curPage, slot, recordId, row, (FormatableBitSet)null, 
+                    (LogicalUndo) null, 
+                    (isFirstPage ? insertFlag : after_first_page_insertFlag), 
+                    startColumn, true, -1, (DynamicByteArrayOutputStream) null, 
+                    -1, 100);
 
 			// step 3: if it is not the first page, update previous page,
 			// then release latch on prevPage
@@ -948,8 +1014,9 @@
 				prevPage.updateFieldOverflowDetails(prevHandle, handle);
 				prevPage.unlatch();
 				prevPage = null;
-			} else
+			} else {
 				isFirstPage = false;
+            }
 
 		} while (startColumn != (-1)) ;
 
@@ -1304,7 +1371,7 @@
 			// Before we purge these rows, we need to make sure they don't have
 			// overflow rows and columns.  Only clean up long rows and long
 			// columns if this is not a temporary container, otherwise, just
-			// loose the space.
+			// lose the space.
 
 			if (owner.isTemporaryContainer() ||	entireRecordOnPage(slot+i))
 				continue;