blob: a16c6fad6592bd7664cc3a2f049045911e93550c [file] [log] [blame]
/*
Derby - Class org.apache.derby.impl.sql.catalog.TabInfoImpl
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.sql.catalog;
import org.apache.derby.iapi.services.io.FormatableBitSet;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.services.io.StreamStorable;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.sql.dictionary.CatalogRowFactory;
import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
import org.apache.derby.iapi.sql.execute.ExecIndexRow;
import org.apache.derby.iapi.sql.execute.ExecRow;
import org.apache.derby.iapi.sql.execute.RowChanger;
import org.apache.derby.iapi.sql.execute.TupleFilter;
import org.apache.derby.iapi.sql.Activation;
import org.apache.derby.iapi.store.access.ConglomerateController;
import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
import org.apache.derby.iapi.store.access.Qualifier;
import org.apache.derby.iapi.store.access.ScanController;
import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
import org.apache.derby.iapi.store.access.TransactionController;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.RowLocation;
import java.util.Properties;
/**
* A poor mans structure used in DataDictionaryImpl.java.
* Used to save heapId, name pairs for non core tables.
*
*/
class TabInfoImpl
{
/**
* ROWNOTDUPLICATE is out of range for a row
* number. If a return code does not equal
* this value, then it refers to the row
* that is a duplicate.
*/
static final int ROWNOTDUPLICATE = -1;
private IndexInfoImpl[] indexes;
private long heapConglomerate;
private int numIndexesSet;
private boolean heapSet;
private final CatalogRowFactory crf;
/**
* Constructor
*
* @param crf the associated CatalogRowFactory
*/
TabInfoImpl(CatalogRowFactory crf)
{
this.heapConglomerate = -1;
this.crf = crf;
int numIndexes = crf.getNumIndexes();
if (numIndexes > 0)
{
indexes = new IndexInfoImpl[numIndexes];
/* Init indexes */
for (int indexCtr = 0; indexCtr < numIndexes; indexCtr++)
{
indexes[indexCtr] = new IndexInfoImpl(
indexCtr,
crf);
}
}
}
/**
* Get the conglomerate for the heap.
*
* @return long The conglomerate for the heap.
*/
long getHeapConglomerate()
{
return heapConglomerate;
}
/**
* Set the heap conglomerate for this.
*
* @param heapConglomerate The new heap conglomerate.
*/
void setHeapConglomerate(long heapConglomerate)
{
this.heapConglomerate = heapConglomerate;
heapSet = true;
}
/**
* Get the conglomerate for the specified index.
*
* @return long The conglomerate for the specified index.
*/
long getIndexConglomerate(int indexID)
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes != null,
"indexes is expected to be non-null");
if (indexID >= indexes.length)
{
SanityManager.THROWASSERT(
"indexID (" + indexID + ") is out of range(0-" +
indexes.length + ")");
}
}
return indexes[indexID].getConglomerateNumber();
}
/**
* Set the index conglomerate for the table.
*
* @param index Index number for index for table
* @param indexConglomerate The conglomerate for that index
*/
void setIndexConglomerate(int index, long indexConglomerate)
{
/* Index names must be set before conglomerates.
* Also verify that we are not setting the same conglomerate
* twice.
*/
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes[index] != null,
"indexes[index] expected to be non-null");
SanityManager.ASSERT(indexes[index].getConglomerateNumber() == -1,
"indexes[index] expected to be -1");
}
indexes[index].setConglomerateNumber(indexConglomerate);
/* We are completely initialized when all indexes have
* their conglomerates initialized
*/
numIndexesSet++;
}
/**
* Set the index conglomerate for the table.
*
* @param cd The ConglomerateDescriptor for one of the index
* for this table.
*/
void setIndexConglomerate(ConglomerateDescriptor cd)
{
int index;
String indexName = cd.getConglomerateName();
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes != null,
"indexes is expected to be non-null");
}
for (index = 0; index < indexes.length; index++)
{
/* All index names expected to be set before
* any conglomerate is set.
*/
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes[index] != null,
"indexes[index] expected to be non-null");
SanityManager.ASSERT(indexes[index].getIndexName() != null,
"indexes[index].getIndexName() expected to be non-null");
}
/* Do we have a match? */
if (indexes[index].getIndexName().equals(indexName))
{
indexes[index].setConglomerateNumber(cd.getConglomerateNumber());
break;
}
}
if (SanityManager.DEBUG)
{
if (index == indexes.length)
{
SanityManager.THROWASSERT("match not found for " + indexName);
}
}
/* We are completely initialized when all indexIds are initialized */
numIndexesSet++;
}
/**
* Get the table name.
*
* @return String The table name.
*/
String getTableName()
{
return crf.getCatalogName();
}
/**
* Get the index name.
*
* @param indexId Index number for index for table
*
* @return String The index name.
*/
String getIndexName(int indexId)
{
return indexes[indexId].getIndexName();
}
/**
* Get the CatalogRowFactory for this.
*
* @return CatalogRowFactory The CatalogRowFactory for this.
*/
CatalogRowFactory getCatalogRowFactory()
{
return crf;
}
/**
* Is this fully initialized.
* (i.e., is all conglomerate info initialized)
*
* @return boolean Whether or not this is fully initialized.
*/
boolean isComplete()
{
/* We are complete when heap conglomerate and all
* index conglomerates are set.
*/
if (! heapSet)
{
return false;
}
return (indexes == null || indexes.length == numIndexesSet);
}
/**
* Get the column count for the specified index number.
*
* @param indexNumber The index number.
*
* @return int The column count for the specified index.
*/
int getIndexColumnCount(int indexNumber)
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes != null,
"indexes is expected to be non-null");
if (!(indexNumber < indexes.length))
{
SanityManager.THROWASSERT("indexNumber (" + indexNumber + ") is out of range(0-" +
indexes.length + ")");
}
}
return indexes[indexNumber].getColumnCount();
}
/**
* Get the IndexRowGenerator for the specified index number.
*
* @param indexNumber The index number.
*
* @return IndexRowGenerator The IRG for the specified index number.
*/
IndexRowGenerator getIndexRowGenerator(int indexNumber)
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes != null,
"indexes is expected to be non-null");
if (indexNumber >= indexes.length)
{
SanityManager.THROWASSERT(
"indexNumber (" + indexNumber + ") is out of range(0-" +
indexes.length + ")");
}
}
return indexes[indexNumber].getIndexRowGenerator();
}
/**
* Set the IndexRowGenerator for the specified index number.
*
* @param indexNumber The index number.
* @param irg The IndexRowGenerator for the specified index number.
*/
void setIndexRowGenerator(int indexNumber, IndexRowGenerator irg)
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes != null,
"indexes is expected to be non-null");
if (indexNumber >= indexes.length)
{
SanityManager.THROWASSERT(
"indexNumber (" + indexNumber + ") is out of range(0-" +
indexes.length + ")");
}
}
indexes[indexNumber].setIndexRowGenerator(irg);
}
/**
* Get the number of indexes on this catalog.
*
* @return int The number of indexes on this catalog.
*/
int getNumberOfIndexes()
{
if (indexes == null)
{
return 0;
}
else
{
return indexes.length;
}
}
/**
* Get the base column position for a column within a catalog
* given the (0-based) index number for this catalog and the
* (0-based) column number for the column within the index.
*
* @param indexNumber The index number
* @param colNumber The column number within the index
*
* @return int The base column position for the column.
*/
int getBaseColumnPosition(int indexNumber, int colNumber)
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes != null,
"indexes is expected to be non-null");
if (indexNumber >= indexes.length)
{
SanityManager.THROWASSERT("indexNumber (" + indexNumber + ") is out of range(0-" +
indexes.length + ")");
}
}
return indexes[indexNumber].getBaseColumnPosition(colNumber);
}
/**
* Return whether or not this index is declared unique
*
* @param indexNumber The index number
*
* @return boolean Whether or not this index is declared unique
*/
boolean isIndexUnique(int indexNumber)
{
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(indexes != null,
"indexes is expected to be non-null");
if (indexNumber >= indexes.length)
{
SanityManager.THROWASSERT("indexNumber (" + indexNumber + ") is out of range(0-" +
indexes.length + ")");
}
}
return indexes[indexNumber].isIndexUnique();
}
/**
* Inserts a base row into a catalog and inserts all the corresponding
* index rows.
*
* @param row row to insert
* @param tc transaction
* @return row number (&gt;= 0) if duplicate row inserted into an index
* ROWNOTDUPLICATE otherwise
*
* @exception StandardException Thrown on failure
*/
int insertRow( ExecRow row, TransactionController tc)
throws StandardException
{
RowLocation[] notUsed = new RowLocation[1];
return insertRowListImpl(new ExecRow[] {row},tc,notUsed);
}
/**
* Inserts a list of base rows into a catalog and inserts all the corresponding
* index rows.
*
* @param rowList List of rows to insert
* @param tc transaction controller
*
*
* @return row number (&gt;= 0) if duplicate row inserted into an index
* ROWNOTDUPLICATE otherwise
*
* @exception StandardException Thrown on failure
*/
int insertRowList(ExecRow[] rowList, TransactionController tc )
throws StandardException
{
RowLocation[] notUsed = new RowLocation[1];
return insertRowListImpl(rowList,tc,notUsed);
}
/**
Insert logic to insert a list of rows into a table. This logic has two
odd features.
<OL>
<LI>Returns an indication if any returned row was a duplicate.
<LI>Returns the RowLocation of the last row inserted.
</OL>
@param rowList the list of rows to insert
@param tc transaction controller
@param rowLocationOut on output rowLocationOut[0] is set to the
last RowLocation inserted.
@return row number (&gt;= 0) if duplicate row inserted into an index
ROWNOTDUPLICATE otherwise
*/
private int insertRowListImpl(ExecRow[] rowList, TransactionController tc,
RowLocation[] rowLocationOut)
throws StandardException
{
ConglomerateController heapController;
RowLocation heapLocation;
ExecIndexRow indexableRow;
int insertRetCode;
int retCode = ROWNOTDUPLICATE;
int indexCount = crf.getNumIndexes();
ConglomerateController[] indexControllers = new ConglomerateController[ indexCount ];
// Open the conglomerates
heapController =
tc.openConglomerate(
getHeapConglomerate(),
false,
TransactionController.OPENMODE_FORUPDATE,
TransactionController.MODE_RECORD,
TransactionController.ISOLATION_REPEATABLE_READ);
/* NOTE: Due to the lovely problem of trying to add
* a new column to syscolumns and an index on that
* column during upgrade, we have to deal with the
* issue of the index not existing yet. So, it's okay
* if the index doesn't exist yet. (It will magically
* get created at a later point during upgrade.)
*/
for ( int ictr = 0; ictr < indexCount; ictr++ )
{
long conglomNumber = getIndexConglomerate(ictr);
if (conglomNumber > -1)
{
indexControllers[ ictr ] =
tc.openConglomerate(
conglomNumber,
false,
TransactionController.OPENMODE_FORUPDATE,
TransactionController.MODE_RECORD,
TransactionController.ISOLATION_REPEATABLE_READ);
}
}
heapLocation = heapController.newRowLocationTemplate();
rowLocationOut[0]=heapLocation;
// loop through rows on this list, inserting them into system table
for (int rowNumber = 0; rowNumber < rowList.length; rowNumber++)
{
ExecRow row = rowList[rowNumber];
// insert the base row and get its new location
heapController.insertAndFetchLocation(row.getRowArray(), heapLocation);
for ( int ictr = 0; ictr < indexCount; ictr++ )
{
if (indexControllers[ ictr ] == null)
{
continue;
}
// Get an index row based on the base row
indexableRow = getIndexRowFromHeapRow( getIndexRowGenerator(ictr),
heapLocation,
row );
insertRetCode = indexControllers[ ictr ].insert(
indexableRow.getRowArray());
if ( insertRetCode == ConglomerateController.ROWISDUPLICATE )
{
retCode = rowNumber;
}
}
} // end loop through rows on list
// Close the open conglomerates
for ( int ictr = 0; ictr < indexCount; ictr++ )
{
if (indexControllers[ ictr ] == null)
{
continue;
}
indexControllers[ ictr ].close();
}
heapController.close();
return retCode;
}
/**
* Given a key row, delete all matching heap rows and their index
* rows.
* <p>
* LOCKING: row locking if there is a key; otherwise,
* table locking.
*
* @param tc transaction controller
* @param key key to delete by.
* @param indexNumber Key is appropriate for this index.
* @return the number of rows deleted. If key is not unique,
* this may be more than one.
* @exception StandardException Thrown on failure
*/
int deleteRow( TransactionController tc, ExecIndexRow key, int indexNumber )
throws StandardException
{
// Always row locking
return deleteRows(tc,
key,
ScanController.GE,
null,
null,
key,
ScanController.GT,
indexNumber,
true);
}
int deleteRow( TransactionController tc, ExecIndexRow key,
int indexNumber, boolean wait)
throws StandardException
{
// Always row locking
return deleteRows(tc,
key,
ScanController.GE,
null,
null,
key,
ScanController.GT,
indexNumber,
wait);
}
/**
* Delete the set of rows defined by a scan on an index
* from the table. Most of the parameters are simply passed
* to TransactionController.openScan. Please refer to the
* TransactionController documentation for details.
* <p>
* LOCKING: row locking if there is a start and a stop
* key; otherwise, table locking
*
* @param tc transaction controller
* @param startKey key to start the scan.
* @param startOp operation to start the scan.
* @param stopKey key to start the scan.
* @param qualifier a qualifier for the scan.
* @param filter filter on base rows
* @param stopOp operation to start the scan.
* @param indexNumber Key is appropriate for this index.
* @return the number of rows deleted.
* @exception StandardException Thrown on failure
* @see TransactionController#openScan
*/
int deleteRows(TransactionController tc,
ExecIndexRow startKey,
int startOp,
Qualifier[][] qualifier,
TupleFilter filter,
ExecIndexRow stopKey,
int stopOp,
int indexNumber) throws StandardException
{
return deleteRows(tc,
startKey,
startOp,
qualifier,
filter,
stopKey,
stopOp,
indexNumber,
true);
}
/**
* @inheritDoc
*/
private int deleteRows(TransactionController tc,
ExecIndexRow startKey,
int startOp,
Qualifier[][] qualifier,
TupleFilter filter,
ExecIndexRow stopKey,
int stopOp,
int indexNumber,
boolean wait)
throws StandardException
{
ConglomerateController heapCC;
ScanController drivingScan;
ExecIndexRow drivingIndexRow;
RowLocation baseRowLocation;
RowChanger rc;
ExecRow baseRow = crf.makeEmptyRow();
int rowsDeleted = 0;
boolean passedFilter = true;
rc = getRowChanger( tc, (int[])null,baseRow );
/*
** If we have a start and a stop key, then we are going to
** get row locks, otherwise, we are getting table locks.
** This may be excessive locking for the case where there
** is a start key and no stop key or vice versa.
*/
int lockMode = ((startKey != null) && (stopKey != null)) ?
TransactionController.MODE_RECORD :
TransactionController.MODE_TABLE;
/*
** Don't use level 3 if we have the same start/stop key.
*/
int isolation =
((startKey != null) && (stopKey != null) && (startKey == stopKey)) ?
TransactionController.ISOLATION_REPEATABLE_READ :
TransactionController.ISOLATION_SERIALIZABLE;
// Row level locking
rc.open(lockMode, wait);
DataValueDescriptor[] startKeyRow =
startKey == null ? null : startKey.getRowArray();
DataValueDescriptor[] stopKeyRow =
stopKey == null ? null : stopKey.getRowArray();
/* Open the heap conglomerate */
heapCC = tc.openConglomerate(
getHeapConglomerate(),
false,
(TransactionController.OPENMODE_FORUPDATE |
((wait) ? 0 : TransactionController.OPENMODE_LOCK_NOWAIT)),
lockMode,
TransactionController.ISOLATION_REPEATABLE_READ);
drivingScan = tc.openScan(
getIndexConglomerate(indexNumber), // conglomerate to open
false, // don't hold open across commit
(TransactionController.OPENMODE_FORUPDATE |
((wait) ? 0 : TransactionController.OPENMODE_LOCK_NOWAIT)),
lockMode,
isolation,
(FormatableBitSet) null, // all fields as objects
startKeyRow, // start position - first row
startOp, // startSearchOperation
qualifier, //scanQualifier
stopKeyRow, // stop position - through last row
stopOp); // stopSearchOperation
// Get an index row based on the base row
drivingIndexRow = getIndexRowFromHeapRow(
getIndexRowGenerator( indexNumber ),
heapCC.newRowLocationTemplate(),
crf.makeEmptyRow());
while (drivingScan.fetchNext(drivingIndexRow.getRowArray()))
{
baseRowLocation = (RowLocation)
drivingIndexRow.getColumn(drivingIndexRow.nColumns());
boolean base_row_exists =
heapCC.fetch(
baseRowLocation, baseRow.getRowArray(), (FormatableBitSet) null);
if (SanityManager.DEBUG)
{
// it can not be possible for heap row to disappear while
// holding scan cursor on index at ISOLATION_REPEATABLE_READ.
SanityManager.ASSERT(base_row_exists, "base row not found");
}
// only delete rows which pass the base-row filter
if ( filter != null ) { passedFilter = filter.execute( baseRow ).equals( true ); }
if ( passedFilter )
{
rc.deleteRow( baseRow, baseRowLocation );
rowsDeleted++;
}
}
heapCC.close();
drivingScan.close();
rc.close();
return rowsDeleted;
}
/**
* Given a key row, return the first matching heap row.
* <p>
* LOCKING: shared row locking.
*
* @param tc transaction controller
* @param key key to read by.
* @param indexNumber Key is appropriate for this index.
* @exception StandardException Thrown on failure
*/
ExecRow getRow( TransactionController tc,
ExecIndexRow key,
int indexNumber )
throws StandardException
{
ConglomerateController heapCC;
/* Open the heap conglomerate */
heapCC = tc.openConglomerate(
getHeapConglomerate(),
false,
0, // for read only
TransactionController.MODE_RECORD,
TransactionController.ISOLATION_REPEATABLE_READ);
try { return getRow( tc, heapCC, key, indexNumber ); }
finally { heapCC.close(); }
}
/**
* Given an index row and index number return the RowLocation
* in the heap of the first matching row.
* Used by the autoincrement code to get the RowLocation in
* syscolumns given a &lt;tablename, columname&gt; pair.
*
* @see DataDictionaryImpl#computeRowLocation(TransactionController, TableDescriptor, String)
*
* @param tc Transaction Controller to use.
* @param key Index Row to search in the index.
* @param indexNumber Identifies the index to use.
*
* @exception StandardException thrown on failure.
*/
RowLocation getRowLocation(TransactionController tc,
ExecIndexRow key,
int indexNumber)
throws StandardException
{
ConglomerateController heapCC;
heapCC = tc.openConglomerate(
getHeapConglomerate(),
false,
0, // for read only
TransactionController.MODE_RECORD,
TransactionController.ISOLATION_REPEATABLE_READ);
try
{
RowLocation rl[] = new RowLocation[1];
ExecRow notUsed = getRowInternal(tc, heapCC, key, indexNumber, rl);
return rl[0];
}
finally
{
heapCC.close();
}
}
/**
* Given a key row, return the first matching heap row.
* <p>
* LOCKING: shared row locking.
*
* @param tc transaction controller
* @param heapCC heap to look in
* @param key key to read by.
* @param indexNumber Key is appropriate for this index.
* @exception StandardException Thrown on failure
*/
ExecRow getRow( TransactionController tc,
ConglomerateController heapCC,
ExecIndexRow key,
int indexNumber)
throws StandardException
{
RowLocation rl[] = new RowLocation[1];
return getRowInternal(tc, heapCC, key, indexNumber, rl);
}
/**
* @exception StandardException Thrown on failure
*/
private ExecRow getRowInternal( TransactionController tc,
ConglomerateController heapCC,
ExecIndexRow key,
int indexNumber,
RowLocation rl[])
throws StandardException
{
ScanController drivingScan;
ExecIndexRow drivingIndexRow;
RowLocation baseRowLocation;
ExecRow baseRow = crf.makeEmptyRow();
drivingScan = tc.openScan(
getIndexConglomerate(indexNumber),
// conglomerate to open
false, // don't hold open across commit
0, // open for read
TransactionController.MODE_RECORD,
TransactionController.ISOLATION_REPEATABLE_READ,
(FormatableBitSet) null, // all fields as objects
key.getRowArray(), // start position - first row
ScanController.GE, // startSearchOperation
null, //scanQualifier
key.getRowArray(), // stop position - through last row
ScanController.GT); // stopSearchOperation
// Get an index row based on the base row
drivingIndexRow = getIndexRowFromHeapRow(
getIndexRowGenerator( indexNumber ),
heapCC.newRowLocationTemplate(),
crf.makeEmptyRow());
try {
if (drivingScan.fetchNext(drivingIndexRow.getRowArray()))
{
rl[0] = baseRowLocation = (RowLocation)
drivingIndexRow.getColumn(drivingIndexRow.nColumns());
boolean base_row_exists =
heapCC.fetch(
baseRowLocation, baseRow.getRowArray(), (FormatableBitSet) null);
if (SanityManager.DEBUG)
{
// it can not be possible for heap row to disappear while
// holding scan cursor on index at ISOLATION_REPEATABLE_READ.
SanityManager.ASSERT(base_row_exists, "base row not found");
}
return baseRow;
}
else
{
return null;
}
}
finally {
drivingScan.close();
}
}
/**
* Updates a base row in a catalog and updates all the corresponding
* index rows.
*
* @param key key row
* @param newRow new version of the row
* @param indexNumber index that key operates
* @param indicesToUpdate array of booleans, one for each index on the catalog.
* if a boolean is true, that means we must update the
* corresponding index because changes in the newRow
* affect it.
* @param colsToUpdate array of ints indicating which columns (1 based)
* to update. If null, do all.
* @param tc transaction controller
*
* @exception StandardException Thrown on failure
*/
void updateRow( ExecIndexRow key,
ExecRow newRow,
int indexNumber,
boolean[] indicesToUpdate,
int[] colsToUpdate,
TransactionController tc)
throws StandardException
{
ExecRow[] newRows = new ExecRow[1];
newRows[0] = newRow;
updateRow(key, newRows, indexNumber, indicesToUpdate, colsToUpdate, tc);
}
/**
* Updates a set of base rows in a catalog with the same key on an index
* and updates all the corresponding index rows.
*
* @param key key row
* @param newRows new version of the array of rows
* @param indexNumber index that key operates
* @param indicesToUpdate array of booleans, one for each index on the catalog.
* if a boolean is true, that means we must update the
* corresponding index because changes in the newRow
* affect it.
* @param colsToUpdate array of ints indicating which columns (1 based)
* to update. If null, do all.
* @param tc transaction controller
*
* @exception StandardException Thrown on failure
*/
void updateRow( ExecIndexRow key,
ExecRow[] newRows,
int indexNumber,
boolean[] indicesToUpdate,
int[] colsToUpdate,
TransactionController tc )
throws StandardException
{
ConglomerateController heapCC;
ScanController drivingScan;
ExecIndexRow drivingIndexRow;
RowLocation baseRowLocation;
ExecRow baseRow = crf.makeEmptyRow();
if (SanityManager.DEBUG)
{
SanityManager.ASSERT( indicesToUpdate.length == crf.getNumIndexes(),
"Wrong number of indices." );
}
RowChanger rc = getRowChanger( tc, colsToUpdate,baseRow );
// Row level locking
rc.openForUpdate(indicesToUpdate, TransactionController.MODE_RECORD, true);
/* Open the heap conglomerate */
heapCC = tc.openConglomerate(
getHeapConglomerate(),
false,
TransactionController.OPENMODE_FORUPDATE,
TransactionController.MODE_RECORD,
TransactionController.ISOLATION_REPEATABLE_READ);
drivingScan = tc.openScan(
getIndexConglomerate(indexNumber), // conglomerate to open
false, // don't hold open across commit
TransactionController.OPENMODE_FORUPDATE,
TransactionController.MODE_RECORD,
TransactionController.ISOLATION_REPEATABLE_READ,
(FormatableBitSet) null, // all fields as objects
key.getRowArray(), // start position - first row
ScanController.GE, // startSearchOperation
null, //scanQualifier
key.getRowArray(), // stop position - through last row
ScanController.GT); // stopSearchOperation
// Get an index row based on the base row
drivingIndexRow = getIndexRowFromHeapRow(
getIndexRowGenerator( indexNumber ),
heapCC.newRowLocationTemplate(),
crf.makeEmptyRow());
int rowNum = 0;
while (drivingScan.fetchNext(drivingIndexRow.getRowArray()))
{
baseRowLocation = (RowLocation)
drivingIndexRow.getColumn(drivingIndexRow.nColumns());
boolean base_row_exists =
heapCC.fetch(
baseRowLocation, baseRow.getRowArray(), (FormatableBitSet) null);
if (SanityManager.DEBUG)
{
// it can not be possible for heap row to disappear while
// holding scan cursor on index at ISOLATION_REPEATABLE_READ.
SanityManager.ASSERT(base_row_exists, "base row not found");
}
rc.updateRow(baseRow, (rowNum == newRows.length - 1) ?
newRows[rowNum] : newRows[rowNum++], baseRowLocation );
}
rc.finish();
heapCC.close();
drivingScan.close();
rc.close();
}
/**
* Get the Properties associated with creating the heap.
*
* @return The Properties associated with creating the heap.
*/
Properties getCreateHeapProperties()
{
return crf.getCreateHeapProperties();
}
/**
* Get the Properties associated with creating the specified index.
*
* @param indexNumber The specified index number.
*
* @return The Properties associated with creating the specified index.
*/
Properties getCreateIndexProperties(int indexNumber)
{
return crf.getCreateIndexProperties(indexNumber);
}
/**
* Gets a row changer for this catalog.
*
* @param tc transaction controller
* @param changedCols the columns to change (1 based), may be null
* @param baseRow used to detemine column types at creation time
* only. The row changer does ***Not*** keep a referance to
* this row or change it in any way.
*
* @return a row changer for this catalog.
* @exception StandardException Thrown on failure
*/
private RowChanger getRowChanger( TransactionController tc,
int[] changedCols,
ExecRow baseRow)
throws StandardException
{
RowChanger rc;
int indexCount = crf.getNumIndexes();
IndexRowGenerator[] irgs = new IndexRowGenerator[ indexCount ];
long[] cids = new long[ indexCount ];
if (SanityManager.DEBUG)
{
if (changedCols != null)
{
for (int i = changedCols.length - 1; i >= 0; i--)
{
SanityManager.ASSERT(changedCols[i] != 0,
"Column id is 0, but should be 1 based");
}
}
}
for ( int ictr = 0; ictr < indexCount; ictr++ )
{
irgs[ictr] = getIndexRowGenerator(ictr);
cids[ictr] = getIndexConglomerate(ictr);
}
rc = crf.getExecutionFactory().getRowChanger(getHeapConglomerate(),
(StaticCompiledOpenConglomInfo) null,
(DynamicCompiledOpenConglomInfo) null,
irgs,
cids,
(StaticCompiledOpenConglomInfo[]) null,
(DynamicCompiledOpenConglomInfo[]) null,
crf.getHeapColumnCount(),
tc,
changedCols,
getStreamStorableHeapColIds(baseRow),
(Activation) null);
return rc;
}
private boolean computedStreamStorableHeapColIds = false;
private int[] streamStorableHeapColIds;
private int[] getStreamStorableHeapColIds(ExecRow baseRow) throws StandardException
{
if (!computedStreamStorableHeapColIds)
{
int sshcidLen = 0;
//
//Compute the length of streamStorableHeapColIds
//One entry for each column id.
DataValueDescriptor[] ra = baseRow.getRowArray();
for(int ix=0;ix<ra.length;ix++)
if (ra[ix] instanceof StreamStorable) sshcidLen++;
//
//If we have some streamStorableHeapColIds we
//allocate an array to remember them and fill in
//the array with the 0 based column ids. If we
//have none leave streamStorableHeapColIds Null.
if (sshcidLen > 0)
{
streamStorableHeapColIds = new int[sshcidLen];
int sshcidOffset=0;
for(int ix=0;ix<ra.length;ix++)
if (ra[ix] instanceof StreamStorable)
streamStorableHeapColIds[sshcidOffset++] = ix;
}
computedStreamStorableHeapColIds = true;
}
return streamStorableHeapColIds;
}
/**
* Get an index row based on a row from the heap.
*
* @param irg IndexRowGenerator to use
* @param rl RowLocation for heap
* @param heapRow Row from the heap
*
* @return ExecIndexRow Index row.
*
* @exception StandardException Thrown on error
*/
private ExecIndexRow getIndexRowFromHeapRow(IndexRowGenerator irg,
RowLocation rl,
ExecRow heapRow)
throws StandardException
{
ExecIndexRow indexRow;
indexRow = irg.getIndexRowTemplate();
// Get an index row based on the base row
irg.getIndexRow(heapRow, rl, indexRow, (FormatableBitSet) null);
return indexRow;
}
public String toString()
{
if (SanityManager.DEBUG)
{
return "name: " + this.getTableName() +
"\n\theapCongolomerate: "+heapConglomerate +
"\n\tnumIndexes: " + ((indexes != null) ? indexes.length : 0) +
"\n\tnumIndexesSet: " + numIndexesSet +
"\n\theapSet: " + heapSet +
"\n";
}
else
{
return "";
}
}
}