Return-Path: Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: (qmail 19713 invoked from network); 9 Jun 2009 17:22:23 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 9 Jun 2009 17:22:23 -0000 Received: (qmail 67748 invoked by uid 500); 9 Jun 2009 17:21:46 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 67499 invoked by uid 500); 9 Jun 2009 17:21:46 -0000 Mailing-List: contact derby-commits-help@db.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: "Derby Development" List-Id: Delivered-To: mailing list derby-commits@db.apache.org Received: (qmail 66842 invoked by uid 99); 9 Jun 2009 17:20:19 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 09 Jun 2009 17:20:19 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 09 Jun 2009 17:20:17 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 199892388874; Tue, 9 Jun 2009 17:19:57 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r783072 - /db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/store/raw/data/BasePage.java Date: Tue, 09 Jun 2009 17:19:56 -0000 To: derby-commits@db.apache.org From: mikem@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090609171957.199892388874@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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. + *

+ * 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;