]> source.dussan.org Git - jackcess.git/commitdiff
add index primary key info; possibly fix some bugs around reading indexes (or possibl...
authorJames Ahlborn <jtahlborn@yahoo.com>
Mon, 24 Jul 2006 15:17:13 +0000 (15:17 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Mon, 24 Jul 2006 15:17:13 +0000 (15:17 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@66 f203690c-595d-4dc9-a70b-905162fa7fd2

src/java/com/healthmarketscience/jackcess/Index.java
src/java/com/healthmarketscience/jackcess/JetFormat.java
src/java/com/healthmarketscience/jackcess/Table.java
test/data/test.mdb
test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java

index 294d450efac8fdcd0d1ce1600d726e4c19c82160..d1a832172b07830ca694d3846b58965e7e3a7e3f 100644 (file)
@@ -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<Index> {
   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<Index> {
   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<Column> 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<Index> {
     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");
index 1ed2134123cdde00006790a90745c0631d0789a2..9ddf4a235c8c6384eb815e15150fcf33bc52361f 100644 (file)
@@ -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; }
index 3927465ae383446883a7fceac467533730a0887f..35b66a9f4bf966b2277ff68ec8e120f51966e65d 100644 (file)
@@ -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<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);
-    _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);
   }
   
   /**
index f1e47ae11a9d3cc6c11888dfe35c69538b805a02..caae8af06833d024685e105b0cbef71d551b5fae 100644 (file)
Binary files a/test/data/test.mdb and b/test/data/test.mdb differ
index 84f5a22adda5ad2c49b0ab184ef95bce35b65f5b..2dba4485c843f74a70d41aa60823f5b70ca7a9c8 100644 (file)
@@ -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<String, Boolean> foundPKs = new HashMap<String, Boolean>();
+    for(Index index : table.getIndexes()) {
+      System.out.println(index);
+      foundPKs.put(index.getColumns().iterator().next().getName(),
+                   index.isPrimaryKey());
+    }
+    Map<String, Boolean> expectedPKs = new HashMap<String, Boolean>();
+    expectedPKs.put("A", Boolean.TRUE);
+    expectedPKs.put("B", Boolean.FALSE);
+    assertEquals(expectedPKs, foundPKs);
+  }
   
   private int countRows(Table table) throws Exception {
     table.reset();