From 2097e47024fc0703960ec347e17113c4968031d2 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Mon, 24 Jul 2006 15:17:13 +0000 Subject: [PATCH] add index primary key info; possibly fix some bugs around reading indexes (or possibly introduce) git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@66 f203690c-595d-4dc9-a70b-905162fa7fd2 --- .../healthmarketscience/jackcess/Index.java | 25 +++++- .../jackcess/JetFormat.java | 16 ++++ .../healthmarketscience/jackcess/Table.java | 73 +++++++++++++----- test/data/test.mdb | Bin 118784 -> 135168 bytes .../jackcess/DatabaseTest.java | 15 ++++ 5 files changed, 108 insertions(+), 21 deletions(-) diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java index 294d450..d1a8321 100644 --- a/src/java/com/healthmarketscience/jackcess/Index.java +++ b/src/java/com/healthmarketscience/jackcess/Index.java @@ -31,14 +31,17 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; -import org.apache.commons.collections.bidimap.DualHashBidiMap; + import org.apache.commons.collections.BidiMap; +import org.apache.commons.collections.bidimap.DualHashBidiMap; import org.apache.commons.lang.builder.CompareToBuilder; /** @@ -137,6 +140,8 @@ public class Index implements Comparable { private int _indexNumber; /** Index name */ private String _name; + /** is this index a primary key */ + private boolean _primaryKey; public Index(int parentPageNumber, PageChannel channel, JetFormat format) { _parentPageNumber = parentPageNumber; @@ -158,11 +163,26 @@ public class Index implements Comparable { public void setName(String name) { _name = name; } + + public boolean isPrimaryKey() { + return _primaryKey; + } + + public void setPrimaryKey(boolean newPrimaryKey) { + _primaryKey = newPrimaryKey; + } + + /** + * Returns the Columns for this index (unmodifiable) + */ + public Collection getColumns() { + return Collections.unmodifiableCollection(_columns.keySet()); + } public void update() throws IOException { _pageChannel.writePage(write(), _pageNumber); } - + /** * Write this index out to a buffer */ @@ -249,6 +269,7 @@ public class Index implements Comparable { rtn.append("\tName: " + _name); rtn.append("\n\tNumber: " + _indexNumber); rtn.append("\n\tPage number: " + _pageNumber); + rtn.append("\n\tIs Primary Key: " + _primaryKey); rtn.append("\n\tColumns: " + _columns); rtn.append("\n\tEntries: " + _entries); rtn.append("\n\n"); diff --git a/src/java/com/healthmarketscience/jackcess/JetFormat.java b/src/java/com/healthmarketscience/jackcess/JetFormat.java index 1ed2134..9ddf4a2 100644 --- a/src/java/com/healthmarketscience/jackcess/JetFormat.java +++ b/src/java/com/healthmarketscience/jackcess/JetFormat.java @@ -65,12 +65,16 @@ public abstract class JetFormat { public final int OFFSET_NEXT_TABLE_DEF_PAGE; public final int OFFSET_NUM_ROWS; public final int OFFSET_TABLE_TYPE; + public final int OFFSET_MAX_COLS; + public final int OFFSET_NUM_VAR_COLS; public final int OFFSET_NUM_COLS; public final int OFFSET_NUM_INDEXES; public final int OFFSET_OWNED_PAGES; public final int OFFSET_FREE_SPACE_PAGES; public final int OFFSET_INDEX_DEF_BLOCK; + public final int OFFSET_INDEX_NUMBER_BLOCK; + public final int OFFSET_COLUMN_TYPE; public final int OFFSET_COLUMN_NUMBER; public final int OFFSET_COLUMN_PRECISION; @@ -142,12 +146,16 @@ public abstract class JetFormat { OFFSET_NEXT_TABLE_DEF_PAGE = defineOffsetNextTableDefPage(); OFFSET_NUM_ROWS = defineOffsetNumRows(); OFFSET_TABLE_TYPE = defineOffsetTableType(); + OFFSET_MAX_COLS = defineOffsetMaxCols(); + OFFSET_NUM_VAR_COLS = defineOffsetNumVarCols(); OFFSET_NUM_COLS = defineOffsetNumCols(); OFFSET_NUM_INDEXES = defineOffsetNumIndexes(); OFFSET_OWNED_PAGES = defineOffsetOwnedPages(); OFFSET_FREE_SPACE_PAGES = defineOffsetFreeSpacePages(); OFFSET_INDEX_DEF_BLOCK = defineOffsetIndexDefBlock(); + OFFSET_INDEX_NUMBER_BLOCK = defineOffsetIndexNumberBlock(); + OFFSET_COLUMN_TYPE = defineOffsetColumnType(); OFFSET_COLUMN_NUMBER = defineOffsetColumnNumber(); OFFSET_COLUMN_PRECISION = defineOffsetColumnPrecision(); @@ -198,12 +206,16 @@ public abstract class JetFormat { protected abstract int defineOffsetNextTableDefPage(); protected abstract int defineOffsetNumRows(); protected abstract int defineOffsetTableType(); + protected abstract int defineOffsetMaxCols(); + protected abstract int defineOffsetNumVarCols(); protected abstract int defineOffsetNumCols(); protected abstract int defineOffsetNumIndexes(); protected abstract int defineOffsetOwnedPages(); protected abstract int defineOffsetFreeSpacePages(); protected abstract int defineOffsetIndexDefBlock(); + protected abstract int defineOffsetIndexNumberBlock(); + protected abstract int defineOffsetColumnType(); protected abstract int defineOffsetColumnNumber(); protected abstract int defineOffsetColumnPrecision(); @@ -255,11 +267,15 @@ public abstract class JetFormat { protected int defineOffsetNextTableDefPage() { return 4; } protected int defineOffsetNumRows() { return 16; } protected int defineOffsetTableType() { return 40; } + protected int defineOffsetMaxCols() { return 41; } + protected int defineOffsetNumVarCols() { return 43; } protected int defineOffsetNumCols() { return 45; } protected int defineOffsetNumIndexes() { return 51; } protected int defineOffsetOwnedPages() { return 55; } protected int defineOffsetFreeSpacePages() { return 59; } protected int defineOffsetIndexDefBlock() { return 63; } + + protected int defineOffsetIndexNumberBlock() { return 52; } protected int defineOffsetColumnType() { return 0; } protected int defineOffsetColumnNumber() { return 5; } diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java index 3927465..35b66a9 100644 --- a/src/java/com/healthmarketscience/jackcess/Table.java +++ b/src/java/com/healthmarketscience/jackcess/Table.java @@ -72,6 +72,12 @@ public class Table { private short _rowsLeftOnPage = 0; /** Offset index in the buffer of the start of the current row */ private short _rowStart; + /** max Number of columns in the table (includes previous deletions) */ + private short _maxColumnCount; + /** max Number of variable columns in the table */ + private short _varColumnCount; + /** Number of fixed columns in the table */ + private short _fixedColumnCount; /** Number of columns in the table */ private short _columnCount; /** Format of the database that contains this table */ @@ -201,22 +207,33 @@ public class Table { ":\n" + ByteUtil.toHexString(_buffer, _buffer.position(), _buffer.limit() - _buffer.position())); } - short columnCount = _buffer.getShort(); //Number of columns in this table + 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); - _buffer.position(_buffer.limit() - nullMask.byteSize() - 2); - short varColumnCount = _buffer.getShort(); //Number of variable length columns - byte[][] varColumnData = new byte[varColumnCount][]; //Holds variable length column data - - //Read in the offsets of each of the variable length columns - short[] varColumnOffsets = new short[varColumnCount]; - _buffer.position(_buffer.position() - 2 - (varColumnCount * 2) - 2); - short lastVarColumnStart = _buffer.getShort(); - for (short i = 0; i < varColumnCount; i++) { - varColumnOffsets[i] = _buffer.getShort(); + + short varColumnCount = 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) { + _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 + + //Read in the offsets of each of the variable length columns + varColumnOffsets = new short[varColumnCount]; + _buffer.position(_buffer.position() - 2 - (varColumnCount * 2) - 2); + lastVarColumnStart = _buffer.getShort(); + for (short i = 0; i < varColumnCount; i++) { + varColumnOffsets[i] = _buffer.getShort(); + } } + //Read in the actual data for each of the variable length columns for (short i = 0; i < varColumnCount; i++) { @@ -316,6 +333,8 @@ 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); _columnCount = _buffer.getShort(_format.OFFSET_NUM_COLS); _indexCount = _buffer.getInt(_format.OFFSET_NUM_INDEXES); @@ -339,6 +358,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()) { + _fixedColumnCount++; + } _columns.add(column); } offset += _columnCount * _format.SIZE_COLUMN_HEADER; @@ -353,24 +375,37 @@ public class Table { offset += nameLength; } Collections.sort(_columns); - - for (int i = 0; i < _indexCount; i++) { - _buffer.getInt(); //Forward past Unknown - ((Index) _indexes.get(i)).read(_buffer, _columns); - } + + 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); _buffer.getInt(); //Forward past Unknown - ((Index) _indexes.get(i)).setIndexNumber(_buffer.getInt()); - _buffer.position(_buffer.position() + 20); + index.setIndexNumber(_buffer.getInt()); + _buffer.position(_buffer.position() + 15); + index.setPrimaryKey(_buffer.get() == 1); + _buffer.position(_buffer.position() + 4); } - Collections.sort(_indexes); for (int i = 0; i < _indexCount; i++) { byte[] nameBytes = new byte[_buffer.getShort()]; _buffer.get(nameBytes); ((Index) _indexes.get(i)).setName(_format.CHARSET.decode(ByteBuffer.wrap( nameBytes)).toString()); } + int idxEndOffset = _buffer.position(); + Collections.sort(_indexes); + + // go back to index column info after sorting + _buffer.position(idxOffset); + for (int i = 0; i < _indexCount; i++) { + _buffer.getInt(); //Forward past Unknown + ((Index) _indexes.get(i)).read(_buffer, _columns); + } + + // reset to end of index info + _buffer.position(idxEndOffset); } /** diff --git a/test/data/test.mdb b/test/data/test.mdb index f1e47ae11a9d3cc6c11888dfe35c69538b805a02..caae8af06833d024685e105b0cbef71d551b5fae 100644 GIT binary patch delta 1950 zcmeH|ZAep57{{OI&fJ{s&X;`K)TU8jeW}1^s5z(8)D2V9D9g;$UQ7jFGo-Do4@N=j z7MH-vm00w~*1`!kXwGSOQhvz)c?|<%d&Up^k z&?z)@ip&&^{?A0Hp!%I86!9_5YrzMu zNt>igq#tQ(pm~^P6Xv3Z8QpS#iQk$$%LhetxX~1bXfy1@0Swb?xlhPZzh-Um`jXPJ z@=DK~Nd4$;GX@Zam{&0UwJ8?!J`B-k^O|g}E;p~Ba9ZI{2OyRRwMD5~E*XRxp~X}p z^ws4r1vO<$DaF^gAr#U{>Xl?0jpU#kpona$0-Aw_EYN|5^jat_&E0(0?d1KX6C~Uvc z1@gq9thVQH$^KM!(;J%xp!eW+b3eq6fja%L^?c7pXGgp@IGtbEt~5RwS?*AsaLYw6 zmVG1|eiI*`$Wo2ip+E-$dlnaQ)p zl(?oKQF=z(id)Z;hTa-yF6?ry&^P`IE8_1Zixg(?Y@(QY zKD-ow&FezB*pK)_NKsL{+3GH=3ibn}LdhIBBa9e=-Rz?(tO}YLVBO_7#71}H5SFq1 z)u?0_pT%%5pl#wZ3NXMAN2Ncj7#{w!RSYqaV_JOmzlzDb{$It@@8FMBY)9LQJGc;S pb%Q7-e}KEF^0Dn2L>k$?2dJ`~EA^Ek+VN-Y58L+a#0P(ueFuGd6dC{k delta 328 zcmZozz|pXPeFGC8BjaX4zW0oi75Mlz2^{8Ud@%i?5u?o}g&p3LKcu^Y@Wc(BOb#p) z7divUjSI7wHrM3;tz;L`X8ZVk-lNG4FXPz`u`@U@$V`6v(p(`iZ_h=bumb}SgfJvB zBr)VLq%s&XJY9SisE9>sv(2kxQ#m&Af#ls7r*kzhs)E@dK7%9!1Eb{j&;~|!MyCJ& zHw$jq#mCCXn8MGnnN^^Yf3j(YD-hdCFfvMW_=Gw7fV8kOFkFB@V~&Xn)3#eCFfL%7 ze()~iEyjT92Xh(Cr(eFuC^7x-J;v8KHLCU=$EwWYmKj%*bHJv2mdu$M$<4 i8MPRvAD+)>J8{F=?FwHR9ay$YePQHdoSyKL(E$K@-foQm diff --git a/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java b/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java index 84f5a22..2dba448 100644 --- a/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java @@ -7,6 +7,7 @@ import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -233,6 +234,20 @@ public class DatabaseTest extends TestCase { } assertTrue(!bogusFile.exists()); } + + public void testPrimaryKey() throws Exception { + Table table = open().getTable("Table1"); + Map foundPKs = new HashMap(); + for(Index index : table.getIndexes()) { + System.out.println(index); + foundPKs.put(index.getColumns().iterator().next().getName(), + index.isPrimaryKey()); + } + Map expectedPKs = new HashMap(); + expectedPKs.put("A", Boolean.TRUE); + expectedPKs.put("B", Boolean.FALSE); + assertEquals(expectedPKs, foundPKs); + } private int countRows(Table table) throws Exception { table.reset(); -- 2.39.5