aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/com/healthmarketscience/jackcess
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2008-03-11 00:49:42 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2008-03-11 00:49:42 +0000
commit5c1b068826d0e897b0ea3c4b77c2b8c08503f112 (patch)
tree02353204177fc2739df01bdfce3525d7f64948d4 /src/java/com/healthmarketscience/jackcess
parentc7d0f58880d7abd80b8f572903f29636eb6c1181 (diff)
downloadjackcess-5c1b068826d0e897b0ea3c4b77c2b8c08503f112.tar.gz
jackcess-5c1b068826d0e897b0ea3c4b77c2b8c08503f112.zip
add unit tests (and fix some bugs) for ignoreNull and unique index handling
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@264 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src/java/com/healthmarketscience/jackcess')
-rw-r--r--src/java/com/healthmarketscience/jackcess/Column.java2
-rw-r--r--src/java/com/healthmarketscience/jackcess/Index.java106
-rw-r--r--src/java/com/healthmarketscience/jackcess/Table.java16
-rw-r--r--src/java/com/healthmarketscience/jackcess/UsageMap.java3
4 files changed, 67 insertions, 60 deletions
diff --git a/src/java/com/healthmarketscience/jackcess/Column.java b/src/java/com/healthmarketscience/jackcess/Column.java
index 9d3583a..6f2ac35 100644
--- a/src/java/com/healthmarketscience/jackcess/Column.java
+++ b/src/java/com/healthmarketscience/jackcess/Column.java
@@ -1112,7 +1112,7 @@ public class Column implements Comparable<Column> {
@Override
public String toString() {
StringBuilder rtn = new StringBuilder();
- rtn.append("\tName: " + _name);
+ rtn.append("\tName: (" + _table.getName() + ") " + _name);
rtn.append("\n\tType: 0x" + Integer.toHexString(_type.getValue()) +
" (" + _type + ")");
rtn.append("\n\tNumber: " + _columnNumber);
diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java
index c87f766..0146c2b 100644
--- a/src/java/com/healthmarketscience/jackcess/Index.java
+++ b/src/java/com/healthmarketscience/jackcess/Index.java
@@ -498,10 +498,9 @@ public class Index implements Comparable<Index> {
}
if(isLeaf) {
- entries.add(new Entry(curEntryBuffer, curEntryLen, _columns));
+ entries.add(new Entry(curEntryBuffer, curEntryLen));
} else {
- nodeEntries.add(new NodeEntry(
- curEntryBuffer, curEntryLen, _columns));
+ nodeEntries.add(new NodeEntry(curEntryBuffer, curEntryLen));
}
// read any shared "compressed" bytes
@@ -558,16 +557,22 @@ public class Index implements Comparable<Index> {
public void addRow(Object[] row, RowId rowId)
throws IOException
{
- if(shouldIgnoreNulls() && isNullEntry(row)) {
+ int nullCount = countNullValues(row);
+ boolean isNullEntry = (nullCount == _columns.size());
+ if(shouldIgnoreNulls() && isNullEntry) {
// nothing to do
return;
}
+ if(isPrimaryKey() && (nullCount > 0)) {
+ throw new IOException("Null value found in row " + Arrays.asList(row)
+ + " for primary key index " + this);
+ }
// make sure we've parsed the entries
initialize();
- Entry newEntry = new Entry(row, rowId, this);
- if(addEntry(newEntry)) {
+ Entry newEntry = new Entry(createEntryBytes(row), rowId);
+ if(addEntry(newEntry, isNullEntry, row)) {
++_rowCount;
++_modCount;
} else {
@@ -587,7 +592,8 @@ public class Index implements Comparable<Index> {
public void deleteRow(Object[] row, RowId rowId)
throws IOException
{
- if(shouldIgnoreNulls() && isNullEntry(row)) {
+ int nullCount = countNullValues(row);
+ if(shouldIgnoreNulls() && (nullCount == _columns.size())) {
// nothing to do
return;
}
@@ -595,7 +601,7 @@ public class Index implements Comparable<Index> {
// make sure we've parsed the entries
initialize();
- Entry oldEntry = new Entry(row, rowId, this);
+ Entry oldEntry = new Entry(createEntryBytes(row), rowId);
if(removeEntry(oldEntry)) {
--_rowCount;
++_modCount;
@@ -638,18 +644,16 @@ public class Index implements Comparable<Index> {
initialize();
Position startPos = FIRST_POSITION;
if(startRow != null) {
- Entry startEntry = new Entry(startRow,
+ Entry startEntry = new Entry(createEntryBytes(startRow),
(startInclusive ?
- RowId.FIRST_ROW_ID : RowId.LAST_ROW_ID),
- this);
+ RowId.FIRST_ROW_ID : RowId.LAST_ROW_ID));
startPos = new Position(FIRST_ENTRY_IDX, startEntry);
}
Position endPos = LAST_POSITION;
if(endRow != null) {
- Entry endEntry = new Entry(endRow,
+ Entry endEntry = new Entry(createEntryBytes(endRow),
(endInclusive ?
- RowId.LAST_ROW_ID : RowId.FIRST_ROW_ID),
- this);
+ RowId.LAST_ROW_ID : RowId.FIRST_ROW_ID));
endPos = new Position(LAST_ENTRY_IDX, endEntry);
}
return new EntryCursor(startPos, endPos);
@@ -674,7 +678,7 @@ public class Index implements Comparable<Index> {
/**
* Adds an entry to the _entries list, maintaining the order.
*/
- private boolean addEntry(Entry newEntry)
+ private boolean addEntry(Entry newEntry, boolean isNullEntry, Object[] row)
throws IOException
{
int idx = findEntry(newEntry);
@@ -683,16 +687,17 @@ public class Index implements Comparable<Index> {
idx = missingIndexToInsertionPoint(idx);
// determine if the addition of this entry would break the uniqueness
- // constraint
- if(isUnique() &&
+ // constraint (note, access does not seem to consider multiple null
+ // entries invalid for a "unique" index)
+ if(isUnique() && !isNullEntry &&
(((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);
+ "New row " + Arrays.asList(row) +
+ " violates uniqueness constraint for index " + this);
}
_entries.add(idx, newEntry);
@@ -787,7 +792,7 @@ public class Index implements Comparable<Index> {
@Override
public String toString() {
StringBuilder rtn = new StringBuilder();
- rtn.append("\tName: " + _name);
+ rtn.append("\tName: (" + _table.getName() + ") " + _name);
rtn.append("\n\tNumber: " + _indexNumber);
rtn.append("\n\tPage number: " + _pageNumber);
rtn.append("\n\tIs Primary Key: " + isPrimaryKey());
@@ -809,27 +814,29 @@ public class Index implements Comparable<Index> {
}
/**
- * Determines if all values for this index from the given row are
- * {@code null}.
+ * Determines the number of {@code null} values for this index from the
+ * given row.
*/
- private boolean isNullEntry(Object[] values)
+ private int countNullValues(Object[] values)
{
if(values == null) {
- return true;
+ return _columns.size();
}
// 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());
+ int nullCount = 0;
for(ColumnDescriptor col : _columns) {
Object value = values[
useColNumber ? col.getColumnNumber() : col.getColumnIndex()];
- if(!col.isNullValue(value)) {
- return false;
+ if(col.isNullValue(value)) {
+ ++nullCount;
}
}
- return true;
+
+ return nullCount;
}
/**
@@ -1039,7 +1046,7 @@ public class Index implements Comparable<Index> {
*/
private static Entry createSpecialEntry(RowId rowId) {
try {
- return new Entry(null, rowId, null);
+ return new Entry((byte[])null, rowId);
} catch(IOException e) {
// should never happen
throw new IllegalStateException(e);
@@ -1369,47 +1376,41 @@ public class Index implements Comparable<Index> {
/**
* Create a new entry
- * @param values Indexed row values
+ * @param entryBytes encoded bytes for this index entry
* @param rowId rowId in which the row is stored
- * @param columns map of columns for this index
*/
- private Entry(Object[] values, RowId rowId, Index parent)
+ private Entry(byte[] entryBytes, RowId rowId)
throws IOException
{
_rowId = rowId;
- if(values != null) {
- _entryBytes = parent.createEntryBytes(values);
+ _entryBytes = entryBytes;
+ if(_entryBytes != null) {
_type = ((_rowId.getType() == RowId.Type.NORMAL) ?
EntryType.NORMAL :
((_rowId.getType() == RowId.Type.ALWAYS_FIRST) ?
EntryType.FIRST_VALID : EntryType.LAST_VALID));
+ } else if(!_rowId.isValid()) {
+ // this is a "special" entry (first/last)
+ _type = ((_rowId.getType() == RowId.Type.ALWAYS_FIRST) ?
+ EntryType.ALWAYS_FIRST : EntryType.ALWAYS_LAST);
} else {
- if(!_rowId.isValid()) {
- // this is a "special" entry (first/last)
- _entryBytes = null;
- _type = ((_rowId.getType() == RowId.Type.ALWAYS_FIRST) ?
- EntryType.ALWAYS_FIRST : EntryType.ALWAYS_LAST);
- } else {
- throw new IllegalArgumentException("Values was null");
- }
+ throw new IllegalArgumentException("Values was null for valid entry");
}
}
/**
* Read an existing entry in from a buffer
*/
- private Entry(ByteBuffer buffer, int entryLen,
- List<ColumnDescriptor> columns)
+ private Entry(ByteBuffer buffer, int entryLen)
throws IOException
{
- this(buffer, entryLen, columns, 0);
+ this(buffer, entryLen, 0);
}
/**
* Read an existing entry in from a buffer
*/
- private Entry(ByteBuffer buffer, int entryLen,
- List<ColumnDescriptor> columns, int extraTrailingLen)
+ private Entry(ByteBuffer buffer, int entryLen, int extraTrailingLen)
throws IOException
{
// we need 4 trailing bytes for the rowId, plus whatever the caller
@@ -1439,7 +1440,7 @@ public class Index implements Comparable<Index> {
public boolean isValid() {
return(_entryBytes != null);
}
-
+
protected final byte[] getEntryBytes() {
return _entryBytes;
}
@@ -1534,12 +1535,11 @@ public class Index implements Comparable<Index> {
/**
* Read an existing node entry in from a buffer
*/
- private NodeEntry(ByteBuffer buffer, int entryLen,
- List<ColumnDescriptor> columns)
+ private NodeEntry(ByteBuffer buffer, int entryLen)
throws IOException
{
// we need 4 trailing bytes for the sub-page number
- super(buffer, entryLen, columns, 4);
+ super(buffer, entryLen, 4);
_subPageNumber = ByteUtil.getInt(buffer, ByteOrder.BIG_ENDIAN);
}
@@ -1665,7 +1665,8 @@ public class Index implements Comparable<Index> {
public void beforeEntry(Object[] row)
throws IOException
{
- restorePosition(new Entry(row, RowId.FIRST_ROW_ID, Index.this));
+ restorePosition(
+ new Entry(Index.this.createEntryBytes(row), RowId.FIRST_ROW_ID));
}
/**
@@ -1675,7 +1676,8 @@ public class Index implements Comparable<Index> {
public void afterEntry(Object[] row)
throws IOException
{
- restorePosition(new Entry(row, RowId.LAST_ROW_ID, Index.this));
+ restorePosition(
+ new Entry(Index.this.createEntryBytes(row), RowId.LAST_ROW_ID));
}
/**
diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java
index eeea925..311f0ad 100644
--- a/src/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/java/com/healthmarketscience/jackcess/Table.java
@@ -1094,14 +1094,17 @@ public class Table
}
getPageChannel().readPage(dataPage, tmpPageNumber);
if(dataPage.get() == PageTypes.DATA) {
- // found last data page
- pageNumber = tmpPageNumber;
+ // found last data page, only use if actually listed in free space
+ // pages
+ if(_freeSpacePages.containsPageNumber(tmpPageNumber)) {
+ pageNumber = tmpPageNumber;
+ }
break;
}
}
if(pageNumber == PageChannel.INVALID_PAGE_NUMBER) {
- //No data pages exist. Create a new one.
+ // No data pages exist (with free space). Create a new one.
pageNumber = newDataPage(dataPage);
}
@@ -1111,12 +1114,13 @@ public class Table
short freeSpaceInPage = dataPage.getShort(getFormat().OFFSET_FREE_SPACE);
if (freeSpaceInPage < rowSpaceUsage) {
- //Last data page is full. Create a new one.
+ // Last data page is full. Create a new one.
writeDataPage(dataPage, pageNumber);
- dataPage.clear();
_freeSpacePages.removePageNumber(pageNumber);
+ dataPage.clear();
pageNumber = newDataPage(dataPage);
+
freeSpaceInPage = dataPage.getShort(getFormat().OFFSET_FREE_SPACE);
}
@@ -1131,7 +1135,7 @@ public class Table
}
writeDataPage(dataPage, pageNumber);
- //Update tdef page
+ // Update tdef page
updateTableDefinition(rows.size());
}
diff --git a/src/java/com/healthmarketscience/jackcess/UsageMap.java b/src/java/com/healthmarketscience/jackcess/UsageMap.java
index 551c8a5..9484ffb 100644
--- a/src/java/com/healthmarketscience/jackcess/UsageMap.java
+++ b/src/java/com/healthmarketscience/jackcess/UsageMap.java
@@ -384,7 +384,8 @@ public class UsageMap
@Override
public String toString() {
- StringBuilder builder = new StringBuilder("page numbers: [");
+ StringBuilder builder = new StringBuilder(
+ "page numbers (range " + _startPage + " " + _endPage + "): [");
PageCursor pCursor = cursor();
while(true) {
int nextPage = pCursor.getNextPage();