
com.mckoi.database.InsertSearch Maven / Gradle / Ivy
Show all versions of mckoisqldb Show documentation
/**
* com.mckoi.database.InsertSearch 14 Mar 1998
*
* Mckoi SQL Database ( http://www.mckoi.com/database )
* Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* Version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License Version 2 for more details.
*
* You should have received a copy of the GNU General Public License
* Version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Change Log:
*
*
*/
package com.mckoi.database;
import com.mckoi.util.IntegerVector;
import com.mckoi.util.BlockIntegerList;
import com.mckoi.util.IntegerListInterface;
import com.mckoi.util.IndexComparator;
import com.mckoi.util.IntegerIterator;
import java.util.Comparator;
import java.util.Arrays;
import java.io.*;
/**
* This is a SelectableScheme similar in some ways to the binary tree. When
* a new row is added, it is inserted into a sorted list of rows. We can then
* use this list to select out the sorted list of elements.
*
* This requires less memory than the BinaryTree, however it is not as fast.
* Even though, it should still perform fairly well on medium size data sets.
* On large size data sets, insert and remove performance may suffer.
*
* This object retains knowledge of all set elements unlike BlindSearch which
* has no memory overhead.
*
* Performance should be very comparable to BinaryTree for sets that aren't
* altered much.
*
* @author Tobias Downer
*/
public final class InsertSearch extends CollatedBaseSearch {
/**
* The sorted list of rows in this set. This is sorted from min to max
* (not sorted by row number - sorted by entity row value).
*/
private IntegerListInterface set_list;
/**
* If this is true, then this SelectableScheme records additional rid
* information that can be used to very quickly identify whether a value is
* greater, equal or less.
*/
boolean RECORD_UID;
/**
* The IndexComparator that we use to refer elements in the set to actual
* data objects.
*/
private IndexComparator set_comparator;
// ----- DEBUGGING -----
/**
* If this is immutable, this stores the number of entries in 'set_list'
* when this object was made.
*/
private int DEBUG_immutable_set_size;
/**
* The Constructor.
*/
public InsertSearch(TableDataSource table, int column) {
super(table, column);
set_list = new BlockIntegerList();
// The internal comparator that enables us to sort and lookup on the data
// in this column.
setupComparator();
}
/**
* Constructor sets the scheme with a pre-sorted list. The Vector 'vec'
* should not be used again after this is called. 'vec' must be sorted from
* low key to high key.
*/
public InsertSearch(TableDataSource table, int column, IntegerVector vec) {
this(table, column);
for (int i = 0; i < vec.size(); ++i) {
set_list.add(vec.intAt(i));
}
// NOTE: This must be removed in final, this is a post condition check to
// make sure 'vec' is infact sorted
checkSchemeSorted();
}
/**
* Constructor sets the scheme with a pre-sorted list. The list 'list'
* should not be used again after this is called. 'list' must be sorted from
* low key to high key.
*/
InsertSearch(TableDataSource table, int column, IntegerListInterface list) {
this(table, column);
this.set_list = list;
// NOTE: This must be removed in final, this is a post condition check to
// make sure 'vec' is infact sorted
checkSchemeSorted();
}
/**
* Constructs this as a copy of the given, either mutable or immutable
* copy.
*/
private InsertSearch(TableDataSource table, InsertSearch from,
boolean immutable) {
super(table, from.getColumn());
if (immutable) {
setImmutable();
}
if (immutable) {
// Immutable is a shallow copy
set_list = from.set_list;
DEBUG_immutable_set_size = set_list.size();
}
else {
set_list = new BlockIntegerList(from.set_list);
}
// Do we generate lookup caches?
RECORD_UID = from.RECORD_UID;
// The internal comparator that enables us to sort and lookup on the data
// in this column.
setupComparator();
}
/**
* Sets the internal comparator that enables us to sort and lookup on the
* data in this column.
*/
private void setupComparator() {
set_comparator = new IndexComparator() {
private int internalCompare(int index, TObject cell2) {
TObject cell1 = getCellContents(index);
return cell1.compareTo(cell2);
}
public int compare(int index, Object val) {
return internalCompare(index, (TObject) val);
}
public int compare(int index1, int index2) {
TObject cell = getCellContents(index2);
return internalCompare(index1, cell);
}
};
}
/**
* Inserts a row into the list. This will always be thread safe, table
* changes cause a write lock which prevents reads while we are writing to
* the table.
*/
public void insert(int row) {
if (isImmutable()) {
throw new Error("Tried to change an immutable scheme.");
}
final TObject cell = getCellContents(row);
set_list.insertSort(cell, row, set_comparator);
}
/**
* Removes a row from the list. This will always be thread safe, table
* changes cause a write lock which prevents reads while we are writing to
* the table.
*/
public void remove(int row) {
if (isImmutable()) {
throw new Error("Tried to change an immutable scheme.");
}
TObject cell = getCellContents(row);
int removed = set_list.removeSort(cell, row, set_comparator);
if (removed != row) {
throw new Error("Removed value different than row asked to remove. " +
"To remove: " + row + " Removed: " + removed);
}
}
/**
* This needs to be called to access 'set_comparator' in thread busy
* methods. Because creating a UID cache will modify set_comparator, we
* need to make sure we access this variable safely.
*
* NOTE: This is a throwback method for an idea I had to speed up the
* 'select*' methods, but it proved unworkable. The reason being that
* the UID only contains knowledge of relations between rows, and the
* 'select*' methods find the relationship of a TObject in the column
* set.
*/
private final IndexComparator safeSetComparator() {
// synchronized (uid_lock) {
return set_comparator;
// }
}
/**
* Reads the entire state of the scheme from the input stream. Throws an
* exception if the scheme is not empty.
*/
public void readFrom(InputStream in) throws IOException {
if (set_list.size() != 0) {
throw new RuntimeException("Error reading scheme, already a set in the Scheme");
}
DataInputStream din = new DataInputStream(in);
int vec_size = din.readInt();
int row_count = getTable().getRowCount();
// Check we read in as many indices as there are rows in the table
if (row_count != vec_size) {
throw new IOException(
"Different table row count to indices in scheme. " +
"table=" + row_count +
", vec_size=" + vec_size);
}
for (int i = 0; i < vec_size; ++i) {
int row = din.readInt();
if (row < 0) { // || row >= row_count) {
set_list = new BlockIntegerList();
throw new IOException("Scheme contains out of table bounds index.");
}
set_list.add(row);
}
getSystem().stats().add(vec_size, "{session} InsertSearch.read_indices");
// NOTE: This must be removed in final, this is a post condition check to
// make sure 'vec' is infact sorted
checkSchemeSorted();
}
/**
* Writes the entire state of the scheme to the output stream.
*/
public void writeTo(OutputStream out) throws IOException {
DataOutputStream dout = new DataOutputStream(out);
int list_size = set_list.size();
dout.writeInt(list_size);
IntegerIterator i = set_list.iterator(0, list_size - 1);
while (i.hasNext()) {
dout.writeInt(i.next());
}
}
/**
* Returns an exact copy of this scheme including any optimization
* information. The copied scheme is identical to the original but does not
* share any parts. Modifying any part of the copied scheme will have no
* effect on the original and vice versa.
*/
public SelectableScheme copy(TableDataSource table, boolean immutable) {
// ASSERTION: If immutable, check the size of the current set is equal to
// when the scheme was created.
if (isImmutable()) {
if (DEBUG_immutable_set_size != set_list.size()) {
throw new Error("Assert failed: " +
"Immutable set size is different from when created.");
}
}
// We must create a new InsertSearch object and copy all the state
// information from this object to the new one.
return new InsertSearch(table, this, immutable);
}
/**
* Disposes this scheme.
*/
public void dispose() {
// Close and invalidate.
set_list = null;
set_comparator = null;
}
/**
* Checks that the scheme is in sorted order. This is a debug check to
* ensure we maintain a sorted index.
* NOTE: This *MUST* be removed in a release version because it uses up
* many cycles for each check.
*/
private void checkSchemeSorted() {
// int list_size = set_list.size();
// DataCell last_cell = null;
// for (int i = 0; i < list_size; ++i) {
// int row = set_list.intAt(i);
// DataCell this_cell = getCellContents(row);
// if (last_cell != null) {
// if (this_cell.compareTo(last_cell) < 0) {
// throw new Error("checkSchemeSorted failed. Corrupt index.");
// }
// }
// last_cell = this_cell;
// }
// if (Debug().isInterestedIn(Lvl.WARNING)) {
// StringBuffer info_string = new StringBuffer();
// info_string.append("POST CONDITION CHECK - Checked index of size: ");
// info_string.append(list_size);
// info_string.append(". Sorted correctly (REMOVE THIS CHECK IN FINAL)");
// Debug().write(Lvl.WARNING, this, new String(info_string));
// }
}
// ---------- Implemented/Overwritten from CollatedBaseSearch ----------
protected int searchFirst(TObject val) {
return set_list.searchFirst(val, safeSetComparator());
}
protected int searchLast(TObject val) {
return set_list.searchLast(val, safeSetComparator());
}
protected int setSize() {
return set_list.size();
}
protected TObject firstInCollationOrder() {
return getCellContents(set_list.get(0));
}
protected TObject lastInCollationOrder() {
return getCellContents(set_list.get(setSize() - 1));
}
protected IntegerVector addRangeToSet(int start, int end,
IntegerVector ivec) {
if (ivec == null) {
ivec = new IntegerVector((end - start) + 2);
}
IntegerIterator i = set_list.iterator(start, end);
while (i.hasNext()) {
ivec.addInt(i.next());
}
return ivec;
}
/**
* The select operations for this scheme.
*/
public IntegerVector selectAll() {
IntegerVector ivec = new IntegerVector(set_list);
return ivec;
}
}