use of org.apache.derby.iapi.store.access.ScanController in project derby by apache.
the class AlterTableConstantAction method getSemiRowCount.
// class implementation
/**
* Return the "semi" row count of a table. We are only interested in
* whether the table has 0, 1 or > 1 rows.
*
* @return Number of rows (0, 1 or > 1) in table.
*
* @exception StandardException Thrown on failure
*/
private int getSemiRowCount(TransactionController tc) throws StandardException {
int numRows = 0;
ScanController sc = tc.openScan(td.getHeapConglomerateId(), // hold
false, // open read only
0, TransactionController.MODE_TABLE, TransactionController.ISOLATION_SERIALIZABLE, // scanColumnList
RowUtil.EMPTY_ROW_BITSET, // start position
null, // startSearchOperation
ScanController.GE, // scanQualifier
null, // stop position - through last row
null, // stopSearchOperation
ScanController.GT);
while (sc.next()) {
numRows++;
// We're only interested in whether the table has 0, 1 or > 1 rows
if (numRows == 2) {
break;
}
}
sc.close();
return numRows;
}
use of org.apache.derby.iapi.store.access.ScanController in project derby by apache.
the class AlterTableConstantAction method defragmentRows.
/**
* Defragment rows in the given table.
* <p>
* Scans the rows at the end of a table and moves them to free spots
* towards the beginning of the table. In the same transaction all
* associated indexes are updated to reflect the new location of the
* base table row.
* <p>
* After a defragment pass, if was possible, there will be a set of
* empty pages at the end of the table which can be returned to the
* operating system by calling truncateEnd(). The allocation bit
* maps will be set so that new inserts will tend to go to empty and
* half filled pages starting from the front of the conglomerate.
*
* @param tc transaction controller to use to do updates.
*/
private void defragmentRows(TransactionController tc) throws StandardException {
GroupFetchScanController base_group_fetch_cc = null;
int num_indexes = 0;
int[][] index_col_map = null;
ScanController[] index_scan = null;
ConglomerateController[] index_cc = null;
DataValueDescriptor[][] index_row = null;
TransactionController nested_tc = null;
try {
nested_tc = tc.startNestedUserTransaction(false, true);
switch(td.getTableType()) {
/* Skip views and vti tables */
case TableDescriptor.VIEW_TYPE:
case TableDescriptor.VTI_TYPE:
return;
// DERBY-719,DERBY-720
default:
break;
}
/* Get a row template for the base table */
ExecRow br = lcc.getLanguageConnectionFactory().getExecutionFactory().getValueRow(td.getNumberOfColumns());
/* Fill the row with nulls of the correct type */
for (ColumnDescriptor cd : td.getColumnDescriptorList()) {
br.setColumn(cd.getPosition(), cd.getType().getNull());
}
DataValueDescriptor[][] row_array = new DataValueDescriptor[100][];
row_array[0] = br.getRowArray();
RowLocation[] old_row_location_array = new RowLocation[100];
RowLocation[] new_row_location_array = new RowLocation[100];
// Create the following 3 arrays which will be used to update
// each index as the scan moves rows about the heap as part of
// the compress:
// index_col_map - map location of index cols in the base row,
// ie. index_col_map[0] is column offset of 1st
// key column in base row. All offsets are 0
// based.
// index_scan - open ScanController used to delete old index row
// index_cc - open ConglomerateController used to insert new
// row
ConglomerateDescriptor[] conglom_descriptors = td.getConglomerateDescriptors();
// conglom_descriptors has an entry for the conglomerate and each
// one of it's indexes.
num_indexes = conglom_descriptors.length - 1;
// if indexes exist, set up data structures to update them
if (num_indexes > 0) {
// allocate arrays
index_col_map = new int[num_indexes][];
index_scan = new ScanController[num_indexes];
index_cc = new ConglomerateController[num_indexes];
index_row = new DataValueDescriptor[num_indexes][];
setup_indexes(nested_tc, td, index_col_map, index_scan, index_cc, index_row);
}
/* Open the heap for reading */
base_group_fetch_cc = nested_tc.defragmentConglomerate(td.getHeapConglomerateId(), false, true, TransactionController.OPENMODE_FORUPDATE, TransactionController.MODE_TABLE, TransactionController.ISOLATION_SERIALIZABLE);
int num_rows_fetched;
while ((num_rows_fetched = base_group_fetch_cc.fetchNextGroup(row_array, old_row_location_array, new_row_location_array)) != 0) {
if (num_indexes > 0) {
for (int row = 0; row < num_rows_fetched; row++) {
for (int index = 0; index < num_indexes; index++) {
fixIndex(row_array[row], index_row[index], old_row_location_array[row], new_row_location_array[row], index_cc[index], index_scan[index], index_col_map[index]);
}
}
}
}
// TODO - It would be better if commits happened more frequently
// in the nested transaction, but to do that there has to be more
// logic to catch a ddl that might jump in the middle of the
// above loop and invalidate the various table control structures
// which are needed to properly update the indexes. For example
// the above loop would corrupt an index added midway through
// the loop if not properly handled. See DERBY-1188.
nested_tc.commit();
} finally {
/* Clean up before we leave */
if (base_group_fetch_cc != null) {
base_group_fetch_cc.close();
base_group_fetch_cc = null;
}
if (num_indexes > 0) {
for (int i = 0; i < num_indexes; i++) {
if (index_scan != null && index_scan[i] != null) {
index_scan[i].close();
index_scan[i] = null;
}
if (index_cc != null && index_cc[i] != null) {
index_cc[i].close();
index_cc[i] = null;
}
}
}
if (nested_tc != null) {
nested_tc.destroy();
}
}
}
use of org.apache.derby.iapi.store.access.ScanController in project derby by apache.
the class IndexChanger method insertAndCheckDups.
/**
* Insert the given row into the given conglomerate and check for duplicate
* key error.
*
* @param row The row to insert
*
* @exception StandardException Thrown on duplicate key error unless
* we have a deferred constraint. In that
* index rows are saved for checking
* on commit.
*/
private void insertAndCheckDups(ExecIndexRow row) throws StandardException {
openIndexCC();
int insertStatus;
final DataValueDescriptor[] rowArray = row.getRowArray();
if (deferrable) {
insertStatus = indexCC.insert(row.getRowArray());
if (SanityManager.DEBUG) {
// deferrable: we use a non-unique index
SanityManager.ASSERT(insertStatus != ConglomerateController.ROWISDUPLICATE);
}
final DataValueDescriptor[] key = new DataValueDescriptor[rowArray.length - 1];
System.arraycopy(rowArray, 0, key, 0, key.length);
// If the constraint mode is deferred, perform the check without
// waiting for any locks; we will just presume any lock conflicts
// constitute duplicates (not always the case), and check those keys
// again at commit time.
final boolean deferred = lcc.isEffectivelyDeferred(lcc.getCurrentSQLSessionContext(activation), getUniqueConstraintId());
// TODO add assert getUniqueConstraintId() != null
ScanController idxScan = tc.openScan(indexCID, false, (deferred ? TransactionController.OPENMODE_LOCK_ROW_NOWAIT : 0), TransactionController.MODE_RECORD, TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK, // retrieve all fields
(FormatableBitSet) null, key, // startSearchOp
ScanController.GE, null, key, ScanController.GT);
boolean duplicate = false;
try {
final boolean foundOne = idxScan.next();
if (SanityManager.DEBUG) {
SanityManager.ASSERT(foundOne, "IndexChanger: inserted row gone?");
}
duplicate = foundOne && idxScan.next();
} catch (StandardException e) {
if ((e.getSQLState().equals(SQLState.LOCK_TIMEOUT) || e.getSQLState().equals(SQLState.DEADLOCK)) && deferred) {
// Assume there is a duplicate, so we'll check again at
// commit time.
duplicate = true;
} else {
throw e;
}
}
if (duplicate && irg.isUniqueWithDuplicateNulls()) {
int keyParts = rowArray.length - 1;
for (int i = 0; i < keyParts; i++) {
// Keys with null in it are always unique
if (rowArray[i].isNull()) {
duplicate = false;
break;
}
}
}
if (duplicate) {
if (deferred) {
// Save duplicate row so we can check at commit time there is
// no longer any duplicate.
deferredDuplicates = DeferredConstraintsMemory.rememberDuplicate(lcc, deferredDuplicates, getUniqueConstraintId(), row.getRowArray());
} else {
// the constraint is not deferred, so throw
insertStatus = ConglomerateController.ROWISDUPLICATE;
}
}
} else {
// not a deferred constraint
insertStatus = indexCC.insert(row.getRowArray());
}
if (insertStatus == ConglomerateController.ROWISDUPLICATE) {
/*
** We have a duplicate key error.
*/
String indexOrConstraintName = indexName;
// now get table name, and constraint name if needed
LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
DataDictionary dd = lcc.getDataDictionary();
// get the descriptors
ConglomerateDescriptor cd = dd.getConglomerateDescriptor(indexCID);
UUID tableID = cd.getTableID();
TableDescriptor td = dd.getTableDescriptor(tableID);
String tableName = td.getName();
if (// no index name passed in
indexOrConstraintName == null) {
ConstraintDescriptor conDesc = dd.getConstraintDescriptor(td, cd.getUUID());
indexOrConstraintName = conDesc.getConstraintName();
}
StandardException se = StandardException.newException(SQLState.LANG_DUPLICATE_KEY_CONSTRAINT, indexOrConstraintName, tableName);
throw se;
} else {
if (SanityManager.DEBUG) {
if (insertStatus != 0) {
SanityManager.THROWASSERT("Unknown insert status " + insertStatus);
}
}
}
}
use of org.apache.derby.iapi.store.access.ScanController in project derby by apache.
the class GenericRIChecker method getScanController.
/**
* Get a scan controller positioned using searchRow as
* the start/stop position. The assumption is that searchRow
* is of the same format as the index being opened.
* The scan is set up to return no columns.
* NOTE: We only need an instantaneous lock on the
* table that we are probing as we are just checking
* for the existence of a row. All updaters, whether
* to the primary or foreign key tables, will hold an
* X lock on the table that they are updating and will
* be probing the other table, so instantaneous locks
* will not change the semantics.
*
* RESOLVE: Due to the current RI implementation
* we cannot always get instantaneous locks. We
* will call a method to find out what kind of
* locking to do until the implementation changes.
*
* @param conglomNumber the particular conglomerate we
* are interested in
* @param scoci
* @param dcoci
* @param searchRow the row to match
* @return scan controller
*
* @exception StandardException on error
*/
protected ScanController getScanController(long conglomNumber, StaticCompiledOpenConglomInfo scoci, DynamicCompiledOpenConglomInfo dcoci, ExecRow searchRow) throws StandardException {
int isoLevel = getRICheckIsolationLevel();
ScanController scan;
Long hashKey = Long.valueOf(conglomNumber);
/*
** If we haven't already opened this scan controller,
** we'll open it now and stick it in the hash table.
*/
if ((scan = scanControllers.get(hashKey)) == null) {
setupQualifierRow(searchRow);
scan = tc.openCompiledScan(// hold
false, // read only
0, // row locking
TransactionController.MODE_RECORD, isoLevel, // retrieve all fields
(FormatableBitSet) null, // startKeyValue
indexQualifierRow.getRowArray(), // startSearchOp
ScanController.GE, // qualifier
null, // stopKeyValue
indexQualifierRow.getRowArray(), // stopSearchOp
ScanController.GT, scoci, dcoci);
scanControllers.put(hashKey, scan);
} else {
/*
** If the base row is the same row as the previous
** row, this call to setupQualfierRow is redundant,
** but it is safer this way so we'll take the
** marginal performance hit (marginal relative
** to the index scans that we are making).
*/
setupQualifierRow(searchRow);
scan.reopenScan(// startKeyValue
indexQualifierRow.getRowArray(), // startSearchOp
ScanController.GE, // qualifier
null, // stopKeyValue
indexQualifierRow.getRowArray(), // stopSearchOp
ScanController.GT);
}
return scan;
}
use of org.apache.derby.iapi.store.access.ScanController in project derby by apache.
the class T_QualifierTest method t_scanFetchNextPartial.
/**
* Test scan which does FetchNext with subset of fields.
* <p>
* FetchNext() may be optimized by the underlying scan code to try and
* not do multiple fetches of the same row for the user, but if the user
* asks for one column, but the stop position depends on the whole row
* this optimization is not possible.
* <p>
*
* @return Whether the test succeeded or not.
*
* @exception StandardException Standard exception policy.
*/
public static boolean t_scanFetchNextPartial(TransactionController tc, long conglomid, DataValueDescriptor[] fetch_template, DataValueDescriptor[] start_key, int start_op, Qualifier[][] qualifier, DataValueDescriptor[] stop_key, int stop_op, int expect_numrows, int input_expect_key, int order) throws StandardException, T_Fail {
HashSet set = null;
boolean ordered = (order == ORDER_FORWARD || order == ORDER_DESC);
/**
********************************************************************
* setup shared by both.
**********************************************************************
*/
// In the fetchNext call only ask the minimum set of columns
// necessary, which is the union of the "key" (col[2]) and other
// columns referenced in the qualifier list.
FormatableBitSet fetch_row_validColumns = RowUtil.getQualifierBitSet(qualifier);
// now add in column 2, as we always need the key field.
// grow to length of 3
fetch_row_validColumns.grow(3);
fetch_row_validColumns.set(2);
// add in any fields in start and stop positions
if (start_key != null) {
for (int i = 0; i < start_key.length; i++) {
fetch_row_validColumns.set(i);
}
}
if (stop_key != null) {
for (int i = 0; i < stop_key.length; i++) {
fetch_row_validColumns.set(i);
}
}
// point key at the right column in the fetch_template
SQLLongint key_column = (SQLLongint) fetch_template[2];
if (!ordered) {
set = create_hash_set(input_expect_key, expect_numrows, order);
}
ScanController scan = tc.openScan(conglomid, false, 0, TransactionController.MODE_RECORD, TransactionController.ISOLATION_SERIALIZABLE, (FormatableBitSet) fetch_row_validColumns, start_key, start_op, qualifier, stop_key, stop_op);
int expect_key = input_expect_key;
long key = -42;
long key2 = -42;
long numrows = 0;
while (scan.fetchNext(fetch_template)) {
// see if we are getting the right keys.
key = key_column.getLong();
// make sure a subsequent fetch also works.
key_column.setValue(-42);
scan.fetch(fetch_template);
key2 = key_column.getLong();
if (ordered) {
if ((key != expect_key) || (key2 != expect_key)) {
return (fail("(t_scanFetchNext) wrong key, expected (" + expect_key + ")" + "but got (" + key + ")."));
} else {
if (order == ORDER_DESC)
expect_key--;
else
expect_key++;
}
} else {
if (!set.remove(key)) {
return (fail("(t_scanFetchNext) wrong key, expected (" + expect_key + ")" + "but got (" + key + ")."));
}
}
numrows++;
}
scan.close();
if (numrows != expect_numrows) {
return (fail("(t_scanFetchNext) wrong number of rows. Expected " + expect_numrows + " rows, but got " + numrows + "rows."));
}
return (true);
}
Aggregations