initialize();
return _entries.size();
}
-
+
+ /**
+ * Whether or not the complete index state has been read.
+ */
public boolean isInitialized() {
return _initialized;
}
public void addRow(Object[] row, RowId rowId)
throws IOException
{
+ if(shouldIgnoreNulls() && isNullEntry(row)) {
+ // nothing to do
+ return;
+ }
+
// make sure we've parsed the entries
initialize();
- Entry newEntry = new Entry(row, rowId, _columns,
- _table.getMaxColumnCount());
+ Entry newEntry = new Entry(row, rowId, this);
if(addEntry(newEntry)) {
++_rowCount;
++_modCount;
public void deleteRow(Object[] row, RowId rowId)
throws IOException
{
+ if(shouldIgnoreNulls() && isNullEntry(row)) {
+ // nothing to do
+ return;
+ }
+
// make sure we've parsed the entries
initialize();
- Entry oldEntry = new Entry(row, rowId, _columns,
- _table.getMaxColumnCount());
+ Entry oldEntry = new Entry(row, rowId, this);
if(removeEntry(oldEntry)) {
--_rowCount;
++_modCount;
Entry startEntry = new Entry(startRow,
(startInclusive ?
RowId.FIRST_ROW_ID : RowId.LAST_ROW_ID),
- _columns, _table.getMaxColumnCount());
+ this);
startPos = new Position(FIRST_ENTRY_IDX, startEntry);
}
Position endPos = LAST_POSITION;
Entry endEntry = new Entry(endRow,
(endInclusive ?
RowId.LAST_ROW_ID : RowId.FIRST_ROW_ID),
- _columns, _table.getMaxColumnCount());
+ this);
endPos = new Position(LAST_ENTRY_IDX, endEntry);
}
return new EntryCursor(startPos, endPos);
/**
* Adds an entry to the _entries list, maintaining the order.
*/
- private boolean addEntry(Entry newEntry) {
+ private boolean addEntry(Entry newEntry)
+ throws IOException
+ {
int idx = findEntry(newEntry);
if(idx < 0) {
// this is a new entry
idx = missingIndexToInsertionPoint(idx);
+
+ // determine if the addition of this entry would break the uniqueness
+ // constraint
+ if(isUnique() &&
+ (((idx > 0) &&
+ newEntry.equalsEntryBytes(_entries.get(idx - 1))) ||
+ ((idx < _entries.size()) &&
+ newEntry.equalsEntryBytes(_entries.get(idx)))))
+ {
+ throw new IOException(
+ "New entry " + newEntry +
+ " violates uniqueness constrain for index " + this);
+ }
+
_entries.add(idx, newEntry);
return true;
}
}
}
+ /**
+ * Determines if all values for this index from the given row are
+ * {@code null}.
+ */
+ private boolean isNullEntry(Object[] values)
+ {
+ if(values == null) {
+ return true;
+ }
+
+ // annoyingly, the values array could come from different sources, one
+ // of which will make it a different size than the other. we need to
+ // handle both situations.
+ boolean useColNumber = (values.length >= _table.getMaxColumnCount());
+ for(ColumnDescriptor col : _columns) {
+ Object value = values[
+ useColNumber ? col.getColumnNumber() : col.getColumnIndex()];
+ if(!col.isNullValue(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Creates the entry bytes for a row of values.
+ */
+ private byte[] createEntryBytes(Object[] values) throws IOException
+ {
+ if(values == null) {
+ return null;
+ }
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+ // annoyingly, the values array could come from different sources, one
+ // of which will make it a different size than the other. we need to
+ // handle both situations.
+ boolean useColNumber = (values.length >= _table.getMaxColumnCount());
+ for(ColumnDescriptor col : _columns) {
+ Object value = values[
+ useColNumber ? col.getColumnNumber() : col.getColumnIndex()];
+ col.writeValue(value, bout);
+ }
+
+ return bout.toByteArray();
+ }
+
/**
* Flips the first bit in the byte at the given index.
*/
*/
private static Entry createSpecialEntry(RowId rowId) {
try {
- return new Entry(null, rowId, null, 0);
+ return new Entry(null, rowId, null);
} catch(IOException e) {
// should never happen
throw new IllegalStateException(e);
return getColumn().getName();
}
- protected void writeValue(Object value, ByteArrayOutputStream bout)
+ protected boolean isNullValue(Object value) {
+ return (value == null);
+ }
+
+ protected final void writeValue(Object value, ByteArrayOutputStream bout)
throws IOException
{
- if(value == null) {
+ if(isNullValue(value)) {
// write null value
bout.write(getNullEntryFlag(isAscending()));
return;
}
@Override
- protected void writeValue(Object value, ByteArrayOutputStream bout)
- throws IOException
- {
+ protected boolean isNullValue(Object value) {
// null values are handled as booleans
- bout.write(
- Column.toBooleanValue(value) ?
- (isAscending() ? ASC_BOOLEAN_TRUE : DESC_BOOLEAN_TRUE) :
- (isAscending() ? ASC_BOOLEAN_FALSE : DESC_BOOLEAN_FALSE));
+ return false;
}
-
+
@Override
protected void writeNonNullValue(Object value, ByteArrayOutputStream bout)
throws IOException
{
- throw new UnsupportedOperationException("should not be called");
+ bout.write(
+ Column.toBooleanValue(value) ?
+ (isAscending() ? ASC_BOOLEAN_TRUE : DESC_BOOLEAN_TRUE) :
+ (isAscending() ? ASC_BOOLEAN_FALSE : DESC_BOOLEAN_FALSE));
}
}
super(column, flags);
}
- @Override
- protected void writeValue(Object value, ByteArrayOutputStream bout)
- throws IOException
- {
- throw new UnsupportedOperationException(
- "FIXME cannot write indexes of this type yet");
- }
-
@Override
protected void writeNonNullValue(Object value, ByteArrayOutputStream bout)
throws IOException
* @param rowId rowId in which the row is stored
* @param columns map of columns for this index
*/
- private Entry(Object[] values, RowId rowId, List<ColumnDescriptor> columns,
- int maxTableColumnCount)
+ private Entry(Object[] values, RowId rowId, Index parent)
throws IOException
{
_rowId = rowId;
if(values != null) {
- ByteArrayOutputStream bout = new ByteArrayOutputStream();
- // annoyingly, the values array could come from different sources, one
- // of which will make it a different size than the other. we need to
- // handle both situations.
- boolean useColNumber = (values.length >= maxTableColumnCount);
- for(ColumnDescriptor col : columns) {
- Object value = values[
- useColNumber ? col.getColumnNumber() : col.getColumnIndex()];
- col.writeValue(value, bout);
- }
- _entryBytes = bout.toByteArray();
+ _entryBytes = parent.createEntryBytes(values);
_type = ((_rowId.getType() == RowId.Type.NORMAL) ?
EntryType.NORMAL :
((_rowId.getType() == RowId.Type.ALWAYS_FIRST) ?
((o != null) && (getClass() == o.getClass()) &&
(compareTo((Entry)o) == 0)));
}
+
+ /**
+ * @return {@code true} iff the entryBytes are equal between this
+ * Entry and the given Entry
+ */
+ public boolean equalsEntryBytes(Entry o) {
+ return(BYTE_CODE_COMPARATOR.compare(_entryBytes, o._entryBytes) == 0);
+ }
public int compareTo(Entry other) {
if (this == other) {
public void beforeEntry(Object[] row)
throws IOException
{
- restorePosition(new Entry(row, RowId.FIRST_ROW_ID, _columns,
- _table.getMaxColumnCount()));
+ restorePosition(new Entry(row, RowId.FIRST_ROW_ID, Index.this));
}
/**
public void afterEntry(Object[] row)
throws IOException
{
- restorePosition(new Entry(row, RowId.LAST_ROW_ID, _columns,
- _table.getMaxColumnCount()));
+ restorePosition(new Entry(row, RowId.LAST_ROW_ID, Index.this));
}
/**