From: James Ahlborn Date: Mon, 24 Jul 2006 18:57:11 +0000 (+0000) Subject: first attempt at reading with deleted columns X-Git-Tag: rel_1_1_5~4 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=f495acf15344bf72147b6c7c62f6a076093e6014;p=jackcess.git first attempt at reading with deleted columns git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@67 f203690c-595d-4dc9-a70b-905162fa7fd2 --- diff --git a/src/java/com/healthmarketscience/jackcess/Column.java b/src/java/com/healthmarketscience/jackcess/Column.java index 4db8b80..cb6619c 100644 --- a/src/java/com/healthmarketscience/jackcess/Column.java +++ b/src/java/com/healthmarketscience/jackcess/Column.java @@ -97,6 +97,10 @@ public class Column implements Comparable { private short _columnNumber; /** Column name */ private String _name; + /** the offset of the fixed data in the row */ + private int _fixedDataOffset; + /** the index of the variable length data in the var len offset table */ + private int _varLenTableIndex; public Column() { this(JetFormat.VERSION_4); @@ -131,6 +135,12 @@ public class Column implements Comparable { & 1) != 1); _compressedUnicode = ((buffer.get(offset + format.OFFSET_COLUMN_COMPRESSED_UNICODE) & 1) == 1); + + if(_variableLength) { + _varLenTableIndex = buffer.getShort(offset + format.OFFSET_COLUMN_VARIABLE_TABLE_INDEX); + } else { + _fixedDataOffset = buffer.getShort(offset + format.OFFSET_COLUMN_FIXED_DATA_OFFSET); + } } public String getName() { @@ -189,6 +199,14 @@ public class Column implements Comparable { public short getLength() { return _columnLength; } + + public int getVarLenTableIndex() { + return _varLenTableIndex; + } + + public int getFixedDataOffset() { + return _fixedDataOffset; + } /** * Deserialize a raw byte value for this column into an Object diff --git a/src/java/com/healthmarketscience/jackcess/JetFormat.java b/src/java/com/healthmarketscience/jackcess/JetFormat.java index 9ddf4a2..dc127b1 100644 --- a/src/java/com/healthmarketscience/jackcess/JetFormat.java +++ b/src/java/com/healthmarketscience/jackcess/JetFormat.java @@ -82,6 +82,8 @@ public abstract class JetFormat { public final int OFFSET_COLUMN_VARIABLE; public final int OFFSET_COLUMN_COMPRESSED_UNICODE; public final int OFFSET_COLUMN_LENGTH; + public final int OFFSET_COLUMN_VARIABLE_TABLE_INDEX; + public final int OFFSET_COLUMN_FIXED_DATA_OFFSET; public final int OFFSET_TABLE_DEF_LOCATION; public final int OFFSET_NUM_ROWS_ON_PAGE; @@ -163,6 +165,8 @@ public abstract class JetFormat { OFFSET_COLUMN_VARIABLE = defineOffsetColumnVariable(); OFFSET_COLUMN_COMPRESSED_UNICODE = defineOffsetColumnCompressedUnicode(); OFFSET_COLUMN_LENGTH = defineOffsetColumnLength(); + OFFSET_COLUMN_VARIABLE_TABLE_INDEX = defineOffsetColumnVariableTableIndex(); + OFFSET_COLUMN_FIXED_DATA_OFFSET = defineOffsetColumnFixedDataOffset(); OFFSET_TABLE_DEF_LOCATION = defineOffsetTableDefLocation(); OFFSET_NUM_ROWS_ON_PAGE = defineOffsetNumRowsOnPage(); @@ -223,7 +227,9 @@ public abstract class JetFormat { protected abstract int defineOffsetColumnVariable(); protected abstract int defineOffsetColumnCompressedUnicode(); protected abstract int defineOffsetColumnLength(); - + protected abstract int defineOffsetColumnVariableTableIndex(); + protected abstract int defineOffsetColumnFixedDataOffset(); + protected abstract int defineOffsetTableDefLocation(); protected abstract int defineOffsetNumRowsOnPage(); protected abstract int defineOffsetRowLocationBlock(); @@ -284,6 +290,8 @@ public abstract class JetFormat { protected int defineOffsetColumnVariable() { return 15; } protected int defineOffsetColumnCompressedUnicode() { return 16; } protected int defineOffsetColumnLength() { return 23; } + protected int defineOffsetColumnVariableTableIndex() { return 7; } + protected int defineOffsetColumnFixedDataOffset() { return 21; } protected int defineOffsetTableDefLocation() { return 4; } protected int defineOffsetNumRowsOnPage() { return 12; } diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java index 35b66a9..b141862 100644 --- a/src/java/com/healthmarketscience/jackcess/Table.java +++ b/src/java/com/healthmarketscience/jackcess/Table.java @@ -75,6 +75,8 @@ public class Table { /** max Number of columns in the table (includes previous deletions) */ private short _maxColumnCount; /** max Number of variable columns in the table */ + private short _maxVarColumnCount; + /** Number of variable columns in the table */ private short _varColumnCount; /** Number of fixed columns in the table */ private short _fixedColumnCount; @@ -208,48 +210,47 @@ public class Table { _buffer.limit() - _buffer.position())); } short columnCount = _buffer.getShort(); //Number of columns in this row - + Map rtn = new LinkedHashMap(columnCount); NullMask nullMask = new NullMask(columnCount); _buffer.position(_buffer.limit() - nullMask.byteSize()); //Null mask at end nullMask.read(_buffer); - short varColumnCount = 0; + short rowVarColumnCount = 0; byte[][] varColumnData = null; short[] varColumnOffsets = null; short lastVarColumnStart = 0; - // if table varColumnCount is 0, then row info does not include varcol - // info - if(_varColumnCount > 0) { + // if _maxVarColumnCount is 0, then row info does not include varcol info + if(_maxVarColumnCount > 0) { _buffer.position(_buffer.limit() - nullMask.byteSize() - 2); - varColumnCount = _buffer.getShort(); // actual number of variable length columns in this row - varColumnData = new byte[varColumnCount][]; //Holds variable length column data + rowVarColumnCount = _buffer.getShort(); // number of variable length columns in this row + varColumnData = new byte[rowVarColumnCount][]; //Holds variable length column data //Read in the offsets of each of the variable length columns - varColumnOffsets = new short[varColumnCount]; - _buffer.position(_buffer.position() - 2 - (varColumnCount * 2) - 2); + varColumnOffsets = new short[rowVarColumnCount]; + _buffer.position(_buffer.position() - 2 - (rowVarColumnCount * 2) - 2); lastVarColumnStart = _buffer.getShort(); - for (short i = 0; i < varColumnCount; i++) { + for (short i = 0; i < rowVarColumnCount; i++) { varColumnOffsets[i] = _buffer.getShort(); } } //Read in the actual data for each of the variable length columns - for (short i = 0; i < varColumnCount; i++) { + for (short i = 0; i < rowVarColumnCount; i++) { _buffer.position(_rowStart + varColumnOffsets[i]); varColumnData[i] = new byte[lastVarColumnStart - varColumnOffsets[i]]; _buffer.get(varColumnData[i]); lastVarColumnStart = varColumnOffsets[i]; } - int columnNumber = 0; - int varColumnDataIndex = varColumnCount - 1; - - _buffer.position(_rowStart + 2); //Move back to the front of the buffer + + // compute start of fixed data + int dataStart = _rowStart + 2; //Now read in the fixed length columns and populate the columnData array //with the combination of fixed length and variable length data. byte[] columnData = null; + int columnNumber = 0; for (Iterator iter = _columns.iterator(); iter.hasNext(); columnNumber++) { Column column = (Column) iter.next(); boolean isNull = nullMask.isNull(columnNumber); @@ -261,16 +262,18 @@ public class Table { { //Read in fixed length column data columnData = new byte[column.getLength()]; + _buffer.position(dataStart + column.getFixedDataOffset()); _buffer.get(columnData); } else { if (!isNull) { - //Refer to already-read-in variable length data - columnData = varColumnData[varColumnDataIndex]; + // Refer to already-read-in variable length data. note, + // varLenTableIndex is from the *end* of the array... + columnData = varColumnData[rowVarColumnCount - + column.getVarLenTableIndex() - 1]; } - --varColumnDataIndex; } if (!isNull && columnData != null && (columnNames == null || columnNames.contains(column.getName()))) @@ -334,7 +337,7 @@ public class Table { _rowCount = _buffer.getInt(_format.OFFSET_NUM_ROWS); _tableType = _buffer.get(_format.OFFSET_TABLE_TYPE); _maxColumnCount = _buffer.getShort(_format.OFFSET_MAX_COLS); - _varColumnCount = _buffer.getShort(_format.OFFSET_NUM_VAR_COLS); + _maxVarColumnCount = _buffer.getShort(_format.OFFSET_NUM_VAR_COLS); _columnCount = _buffer.getShort(_format.OFFSET_NUM_COLS); _indexCount = _buffer.getInt(_format.OFFSET_NUM_INDEXES); @@ -358,7 +361,9 @@ public class Table { for (int i = 0; i < _columnCount; i++) { column = new Column(_buffer, offset + i * _format.SIZE_COLUMN_HEADER, _pageChannel, _format); - if(!column.isVariableLength()) { + if(column.isVariableLength()) { + _varColumnCount++; + } else { _fixedColumnCount++; } _columns.add(column); diff --git a/test/data/delColTest.mdb b/test/data/delColTest.mdb new file mode 100644 index 0000000..b931b0d Binary files /dev/null and b/test/data/delColTest.mdb differ diff --git a/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java b/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java index 2dba448..86f1bdd 100644 --- a/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java @@ -258,6 +258,35 @@ public class DatabaseTest extends TestCase { return rtn; } + public void testReadWithDeletedCols() throws Exception { + Table table = Database.open(new File("test/data/delColTest.mdb")).getTable("Table1"); + + Map expectedRow0 = new HashMap(); + expectedRow0.put("id", 0); + expectedRow0.put("id2", 2); + expectedRow0.put("data", "foo"); + expectedRow0.put("data2", "foo2"); + + Map expectedRow1 = new HashMap(); + expectedRow1.put("id", 3); + expectedRow1.put("id2", 5); + expectedRow1.put("data", "bar"); + expectedRow1.put("data2", "bar2"); + + int rowNum = 0; + Map row = null; + while ((row = table.getNextRow()) != null) { + if(rowNum == 0) { + assertEquals(expectedRow0, row); + } else if(rowNum == 1) { + assertEquals(expectedRow1, row); + } else if(rowNum >= 2) { + fail("should only have 2 rows"); + } + rowNum++; + } + } + private Object[] createTestRow() { return new Object[] {"Tim", "R", "McCune", 1234, (byte) 0xad, 555.66d, 777.88f, (short) 999, new Date()};