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);
& 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() {
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
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;
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();
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();
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; }
/** 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;
_buffer.limit() - _buffer.position()));
}
short columnCount = _buffer.getShort(); //Number of columns in this row
-
+
Map<String, Object> rtn = new LinkedHashMap<String, Object>(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);
{
//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())))
_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);
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);
return rtn;
}
+ public void testReadWithDeletedCols() throws Exception {
+ Table table = Database.open(new File("test/data/delColTest.mdb")).getTable("Table1");
+
+ Map<String, Object> expectedRow0 = new HashMap<String, Object>();
+ expectedRow0.put("id", 0);
+ expectedRow0.put("id2", 2);
+ expectedRow0.put("data", "foo");
+ expectedRow0.put("data2", "foo2");
+
+ Map<String, Object> expectedRow1 = new HashMap<String, Object>();
+ expectedRow1.put("id", 3);
+ expectedRow1.put("id2", 5);
+ expectedRow1.put("data", "bar");
+ expectedRow1.put("data2", "bar2");
+
+ int rowNum = 0;
+ Map<String, Object> 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()};