From 805cc8cf030fc2ebe36c1b9e051dec156e2241b2 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Sun, 30 Jul 2006 03:22:36 +0000 Subject: [PATCH] handle more tables with more idnex slots than actual indexes (fix bug 1530312) git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@78 f203690c-595d-4dc9-a70b-905162fa7fd2 --- .../jackcess/JetFormat.java | 4 ++ .../healthmarketscience/jackcess/Table.java | 38 ++++++++++++++++-- test/data/indexTest.mdb | Bin 0 -> 196608 bytes .../jackcess/DatabaseTest.java | 17 ++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 test/data/indexTest.mdb diff --git a/src/java/com/healthmarketscience/jackcess/JetFormat.java b/src/java/com/healthmarketscience/jackcess/JetFormat.java index dc127b1..ec5c73e 100644 --- a/src/java/com/healthmarketscience/jackcess/JetFormat.java +++ b/src/java/com/healthmarketscience/jackcess/JetFormat.java @@ -68,6 +68,7 @@ public abstract class JetFormat { public final int OFFSET_MAX_COLS; public final int OFFSET_NUM_VAR_COLS; public final int OFFSET_NUM_COLS; + public final int OFFSET_NUM_INDEX_SLOTS; public final int OFFSET_NUM_INDEXES; public final int OFFSET_OWNED_PAGES; public final int OFFSET_FREE_SPACE_PAGES; @@ -151,6 +152,7 @@ public abstract class JetFormat { OFFSET_MAX_COLS = defineOffsetMaxCols(); OFFSET_NUM_VAR_COLS = defineOffsetNumVarCols(); OFFSET_NUM_COLS = defineOffsetNumCols(); + OFFSET_NUM_INDEX_SLOTS = defineOffsetNumIndexSlots(); OFFSET_NUM_INDEXES = defineOffsetNumIndexes(); OFFSET_OWNED_PAGES = defineOffsetOwnedPages(); OFFSET_FREE_SPACE_PAGES = defineOffsetFreeSpacePages(); @@ -213,6 +215,7 @@ public abstract class JetFormat { protected abstract int defineOffsetMaxCols(); protected abstract int defineOffsetNumVarCols(); protected abstract int defineOffsetNumCols(); + protected abstract int defineOffsetNumIndexSlots(); protected abstract int defineOffsetNumIndexes(); protected abstract int defineOffsetOwnedPages(); protected abstract int defineOffsetFreeSpacePages(); @@ -276,6 +279,7 @@ public abstract class JetFormat { protected int defineOffsetMaxCols() { return 41; } protected int defineOffsetNumVarCols() { return 43; } protected int defineOffsetNumCols() { return 45; } + protected int defineOffsetNumIndexSlots() { return 47; } protected int defineOffsetNumIndexes() { return 51; } protected int defineOffsetOwnedPages() { return 55; } protected int defineOffsetFreeSpacePages() { return 59; } diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java index c1bf455..9637c4e 100644 --- a/src/java/com/healthmarketscience/jackcess/Table.java +++ b/src/java/com/healthmarketscience/jackcess/Table.java @@ -63,6 +63,8 @@ public class Table { private int _currentRowInPage; /** Number of indexes on the table */ private int _indexCount; + /** Number of index slots for the table */ + private int _indexSlotCount; /** Offset index in the buffer where the last row read started */ private short _lastRowStart; /** Number of rows in the table */ @@ -164,6 +166,13 @@ public class Table { public List getIndexes() { return Collections.unmodifiableList(_indexes); } + + /** + * Only called by unit tests + */ + int getIndexSlotCount() { + return _indexSlotCount; + } /** * After calling this method, getNextRow will return the first row in the table @@ -334,6 +343,7 @@ public class Table { _maxColumnCount = _buffer.getShort(_format.OFFSET_MAX_COLS); _maxVarColumnCount = _buffer.getShort(_format.OFFSET_NUM_VAR_COLS); _columnCount = _buffer.getShort(_format.OFFSET_NUM_COLS); + _indexSlotCount = _buffer.getInt(_format.OFFSET_NUM_INDEX_SLOTS); _indexCount = _buffer.getInt(_format.OFFSET_NUM_INDEXES); byte rowNum = _buffer.get(_format.OFFSET_OWNED_PAGES); @@ -379,14 +389,34 @@ public class Table { int idxOffset = _buffer.position(); _buffer.position(idxOffset + (_format.OFFSET_INDEX_NUMBER_BLOCK * _indexCount)); - for (int i = 0; i < _indexCount; i++) { - Index index = _indexes.get(i); + + // there are _indexSlotCount blocks here, we ignore any slot with an index + // number greater than the number of actual indexes + int curIndex = 0; + for (int i = 0; i < _indexSlotCount; i++) { + _buffer.getInt(); //Forward past Unknown - index.setIndexNumber(_buffer.getInt()); + int indexNumber = _buffer.getInt(); _buffer.position(_buffer.position() + 15); - index.setPrimaryKey(_buffer.get() == 1); + byte indexType = _buffer.get(); _buffer.position(_buffer.position() + 4); + + if(indexNumber < _indexCount) { + Index index = _indexes.get(curIndex++); + index.setIndexNumber(indexNumber); + index.setPrimaryKey(indexType == 1); + } } + + // for each empty index slot, there is some weird sort of name + for(int i = 0; i < (_indexSlotCount - _indexCount); ++i) { + int skipBytes = _buffer.getShort(); + _buffer.position(_buffer.position() + skipBytes); + } + + // read actual index names + // FIXME, we still are not always getting the names matched correctly with + // the index info, some weird indexing we are not figuring out yet for (int i = 0; i < _indexCount; i++) { byte[] nameBytes = new byte[_buffer.getShort()]; _buffer.get(nameBytes); diff --git a/test/data/indexTest.mdb b/test/data/indexTest.mdb new file mode 100644 index 0000000000000000000000000000000000000000..7e8bd98da8ca1bb8fd2df11a13202c5bc6896479 GIT binary patch literal 196608 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