| /* |
| |
| Derby - Class org.apache.derby.impl.store.access.conglomerate.GenericConglomerateController |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to you under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.derby.impl.store.access.conglomerate; |
| |
| import org.apache.derby.iapi.reference.SQLState; |
| |
| import org.apache.derby.shared.common.sanity.SanityManager; |
| |
| import org.apache.derby.iapi.error.StandardException; |
| |
| import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo; |
| |
| import org.apache.derby.iapi.store.access.ConglomerateController; |
| import org.apache.derby.iapi.store.access.Qualifier; |
| import org.apache.derby.iapi.store.access.RowUtil; |
| |
| import org.apache.derby.iapi.store.raw.FetchDescriptor; |
| |
| import org.apache.derby.iapi.types.DataValueDescriptor; |
| |
| import org.apache.derby.iapi.types.RowLocation; |
| |
| import org.apache.derby.iapi.services.io.FormatableBitSet; |
| |
| /** |
| **/ |
| |
| public abstract class GenericConglomerateController |
| extends GenericController implements ConglomerateController |
| { |
| |
| /************************************************************************** |
| * Fields of the class |
| ************************************************************************** |
| */ |
| |
| /************************************************************************** |
| * Constructors for This class: |
| ************************************************************************** |
| */ |
| |
| /************************************************************************** |
| * Private/Protected methods of This class: |
| ************************************************************************** |
| */ |
| |
| /************************************************************************** |
| * Public Methods of This class: |
| ************************************************************************** |
| */ |
| |
| |
| /************************************************************************** |
| * Public Methods implementing ConglomerateController which just |
| * delegate to OpenConglomerate: |
| ************************************************************************** |
| */ |
| |
| /************************************************************************** |
| * Public Methods implementing ConglomerateController: |
| ************************************************************************** |
| */ |
| |
| /** |
| * @see ConglomerateController#close |
| **/ |
| public void close() |
| throws StandardException |
| { |
| super.close(); |
| |
| // If we are closed due to catching an error in the middle of init, |
| // xact_manager may not be set yet. |
| if ((open_conglom != null) && (open_conglom.getXactMgr() != null)) |
| open_conglom.getXactMgr().closeMe(this); |
| } |
| |
| /** |
| * Close conglomerate controller as part of terminating a transaction. |
| * <p> |
| * Use this call to close the conglomerate controller resources as part of |
| * committing or aborting a transaction. The normal close() routine may |
| * do some cleanup that is either unnecessary, or not correct due to the |
| * unknown condition of the controller following a transaction ending error. |
| * Use this call when closing all controllers as part of an abort of a |
| * transaction. |
| * <p> |
| * This call is meant to only be used internally by the Storage system, |
| * clients of the storage system should use the simple close() interface. |
| * <p> |
| * RESOLVE (mikem) - move this call to ConglomerateManager so it is |
| * obvious that non-access clients should not call this. |
| * |
| * @param closeHeldScan If true, means to close controller even if |
| * it has been opened to be kept opened |
| * across commit. This is |
| * used to close these controllers on abort. |
| * |
| * @return boolean indicating that the close has resulted in a real close |
| * of the controller. A held scan will return false if |
| * called by closeForEndTransaction(false), otherwise it |
| * will return true. A non-held scan will always return |
| * true. |
| * |
| * @exception StandardException Standard exception policy. |
| **/ |
| public boolean closeForEndTransaction(boolean closeHeldScan) |
| throws StandardException |
| { |
| super.close(); |
| |
| if ((!open_conglom.getHold()) || closeHeldScan) |
| { |
| // If we are closed due to catching an error in the middle of init, |
| // xact_manager may not be set yet. |
| if ((open_conglom != null) && (open_conglom.getXactMgr() != null)) |
| open_conglom.getXactMgr().closeMe(this); |
| |
| return(true); |
| |
| } |
| else |
| { |
| return(false); |
| } |
| } |
| |
| /** |
| * @see ConglomerateController#delete |
| **/ |
| public boolean delete(RowLocation loc) |
| throws StandardException |
| { |
| if (open_conglom.isClosed()) |
| { |
| if (open_conglom.getHold()) |
| { |
| if (open_conglom.isClosed()) |
| open_conglom.reopen(); |
| } |
| else |
| { |
| throw( |
| StandardException.newException( |
| SQLState.HEAP_IS_CLOSED, |
| open_conglom.getConglomerate().getId())); |
| } |
| } |
| |
| RowPosition pos = |
| open_conglom.getRuntimeMem().get_scratch_row_position(); |
| |
| getRowPositionFromRowLocation(loc, pos); |
| |
| if (!open_conglom.latchPage(pos)) |
| { |
| return false; |
| } |
| |
| open_conglom.lockPositionForWrite(pos, true); |
| |
| boolean ret_val = true; |
| |
| // RESOLVE (mikem) - RECID - performance could be better if we did not |
| // have to call isDeletedAtSlot(). |
| |
| if (pos.current_page.isDeletedAtSlot(pos.current_slot)) |
| { |
| ret_val = false; |
| } |
| else |
| { |
| // Delete the row |
| pos.current_page.deleteAtSlot( |
| pos.current_slot, true, (LogicalUndo) null); |
| |
| // try to reclaim rows when the page is only full of deleted rows, |
| // or in the special case of the first page when all rows except the |
| // "control row" are deleted. Or if the row we just deleted is |
| // a long row or has a long column. |
| if (pos.current_page.shouldReclaimSpace( |
| pos.current_page.getPageNumber() == 1 ? 1 : 0, |
| pos.current_slot)) |
| { |
| queueDeletePostCommitWork(pos); |
| } |
| } |
| |
| pos.current_page.unlatch(); |
| |
| return(ret_val); |
| } |
| |
| /** |
| * @see ConglomerateController#fetch |
| **/ |
| public boolean fetch( |
| RowLocation loc, |
| DataValueDescriptor[] row, |
| FormatableBitSet validColumns) |
| throws StandardException |
| { |
| if (open_conglom.isClosed()) |
| { |
| if (open_conglom.getHold()) |
| { |
| if (open_conglom.isClosed()) |
| open_conglom.reopen(); |
| } |
| else |
| { |
| throw( |
| StandardException.newException( |
| SQLState.HEAP_IS_CLOSED, |
| open_conglom.getConglomerate().getId())); |
| } |
| } |
| |
| if (SanityManager.DEBUG) |
| { |
| // Make sure valid columns are in the list. The RowUtil |
| // call is too expensive to make in a released system for |
| // every fetch. |
| int invalidColumn = |
| RowUtil.columnOutOfRange( |
| row, validColumns, open_conglom.getFormatIds().length); |
| |
| if (invalidColumn >= 0) |
| { |
| throw(StandardException.newException( |
| SQLState.HEAP_TEMPLATE_MISMATCH, |
| invalidColumn, |
| open_conglom.getFormatIds().length)); |
| } |
| } |
| |
| // Get the record handle out of its wrapper. |
| |
| RowPosition pos = |
| open_conglom.getRuntimeMem().get_scratch_row_position(); |
| |
| getRowPositionFromRowLocation(loc, pos); |
| |
| if (!open_conglom.latchPage(pos)) |
| { |
| return(false); |
| } |
| |
| |
| // Do not get U row lock - only get X or S. There is no good point |
| // currently to convert the U lock to an S lock, we don't know when |
| // the calling code is through with the lock. |
| // RESOLVE (mikem) - talk to language and see if it is worth it to |
| // get U lock and have language call back when we should take |
| // appropriate action on the U lock. |
| |
| if (open_conglom.isForUpdate()) |
| { |
| open_conglom.lockPositionForWrite(pos, true); |
| } |
| else |
| { |
| open_conglom.lockPositionForRead( |
| pos, (RowPosition) null, false, true); |
| } |
| |
| if (pos.current_page == null) |
| { |
| // The page is not latched after locking the row. This happens if |
| // the row was deleted while we were waiting for the lock. Return |
| // false to indicate that the row is no longer valid. (DERBY-4676) |
| return false; |
| } |
| |
| // Fetch the row. |
| // RESOLVE (STO061) - don't know whether the fetch is for update or not. |
| // |
| // RESOLVE (mikem) - get rid of new here. |
| boolean ret_val = |
| (pos.current_page.fetchFromSlot( |
| pos.current_rh, pos.current_slot, |
| row, |
| new FetchDescriptor( |
| row.length, validColumns, (Qualifier[][]) null), |
| false) != null); |
| |
| // RESOLVE (mikem) - should be some way to hide this in the unlock call, |
| // and just always make the unlock call. |
| |
| if (!open_conglom.isForUpdate()) |
| open_conglom.unlockPositionAfterRead(pos); |
| |
| pos.current_page.unlatch(); |
| |
| return(ret_val); |
| } |
| |
| /** |
| * @see ConglomerateController#fetch |
| **/ |
| public boolean fetch( |
| RowLocation loc, |
| DataValueDescriptor[] row, |
| FormatableBitSet validColumns, |
| boolean waitForLock) |
| throws StandardException |
| { |
| if (open_conglom.isClosed()) |
| { |
| if (open_conglom.getHold()) |
| { |
| if (open_conglom.isClosed()) |
| open_conglom.reopen(); |
| } |
| else |
| { |
| throw( |
| StandardException.newException( |
| SQLState.HEAP_IS_CLOSED, |
| open_conglom.getConglomerate().getId())); |
| } |
| } |
| |
| if (SanityManager.DEBUG) |
| { |
| // Make sure valid columns are in the list. The RowUtil |
| // call is too expensive to make in a released system for |
| // every fetch. |
| int invalidColumn = |
| RowUtil.columnOutOfRange( |
| row, validColumns, open_conglom.getFormatIds().length); |
| |
| if (invalidColumn >= 0) |
| { |
| throw(StandardException.newException( |
| SQLState.HEAP_TEMPLATE_MISMATCH, |
| invalidColumn, |
| open_conglom.getFormatIds().length)); |
| } |
| } |
| |
| // Get the record handle out of its wrapper. |
| |
| RowPosition pos = |
| open_conglom.getRuntimeMem().get_scratch_row_position(); |
| |
| getRowPositionFromRowLocation(loc, pos); |
| |
| if (!open_conglom.latchPage(pos)) |
| { |
| return false; |
| } |
| |
| // Do not get U row lock - only get X or S. There is not good point |
| // currently to convert the U lock to an S lock, we don't know when |
| // the calling code is through with the lock. |
| // RESOLVE (mikem) - talk to language and see if it is worth it to |
| // get U lock and have language call back when we should take |
| // appropriate action on the U lock. |
| |
| if (open_conglom.isForUpdate()) |
| { |
| open_conglom.lockPositionForWrite(pos, waitForLock); |
| } |
| else |
| { |
| open_conglom.lockPositionForRead( |
| pos, (RowPosition) null, false, waitForLock); |
| } |
| |
| if (pos.current_page == null) |
| { |
| // The page is not latched after locking the row. This happens if |
| // the row was deleted while we were waiting for the lock. Return |
| // false to indicate that the row is no longer valid. (DERBY-4676) |
| return false; |
| } |
| |
| // Fetch the row. |
| // RESOLVE (STO061) - don't know whether the fetch is for update or not. |
| // |
| // |
| // RESOLVE (mikem) - get rid of new here. |
| boolean ret_val = |
| (pos.current_page.fetchFromSlot( |
| pos.current_rh, pos.current_slot, |
| row, |
| new FetchDescriptor( |
| row.length, validColumns, (Qualifier[][]) null), |
| false) != null); |
| |
| // RESOLVE (mikem) - should be some way to hide this in the unlock call, |
| // and just always make the unlock call. |
| if (!open_conglom.isForUpdate()) |
| open_conglom.unlockPositionAfterRead(pos); |
| |
| pos.current_page.unlatch(); |
| |
| return(ret_val); |
| } |
| |
| |
| /** |
| * @see ConglomerateController#replace |
| **/ |
| public boolean replace( |
| RowLocation loc, |
| DataValueDescriptor[] row, |
| FormatableBitSet validColumns) |
| throws StandardException |
| { |
| if (open_conglom.isClosed()) |
| { |
| if (open_conglom.getHold()) |
| { |
| if (open_conglom.isClosed()) |
| open_conglom.reopen(); |
| } |
| else |
| { |
| throw( |
| StandardException.newException( |
| SQLState.HEAP_IS_CLOSED, |
| open_conglom.getConglomerate().getId())); |
| } |
| } |
| |
| if (SanityManager.DEBUG) |
| { |
| // Make sure valid columns are in the list. The RowUtil |
| // call is too expensive to make in a released system for |
| // every fetch. |
| int invalidColumn = |
| RowUtil.columnOutOfRange( |
| row, validColumns, open_conglom.getFormatIds().length); |
| |
| if (invalidColumn >= 0) |
| { |
| throw(StandardException.newException( |
| SQLState.HEAP_TEMPLATE_MISMATCH, |
| invalidColumn, |
| open_conglom.getFormatIds().length)); |
| } |
| } |
| |
| RowPosition pos = |
| open_conglom.getRuntimeMem().get_scratch_row_position(); |
| |
| getRowPositionFromRowLocation(loc, pos); |
| |
| if (!open_conglom.latchPage(pos)) |
| { |
| return false; |
| } |
| |
| open_conglom.lockPositionForWrite(pos, true); |
| |
| boolean ret_val = true; |
| |
| if (pos.current_page.isDeletedAtSlot(pos.current_slot)) |
| { |
| ret_val = false; |
| } |
| else |
| { |
| // Update the record. |
| pos.current_page.updateAtSlot(pos.current_slot, row, validColumns); |
| } |
| |
| pos.current_page.unlatch(); |
| |
| return(ret_val); |
| } |
| } |