]> source.dussan.org Git - jackcess.git/commitdiff
checkpointing some progress on add index
authorJames Ahlborn <jtahlborn@yahoo.com>
Wed, 1 Jun 2016 01:51:55 +0000 (01:51 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Wed, 1 Jun 2016 01:51:55 +0000 (01:51 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/mutateops@995 f203690c-595d-4dc9-a70b-905162fa7fd2

src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java
src/main/java/com/healthmarketscience/jackcess/TableModBuilder.java
src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java
src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java

index 9c9f5849c463369b3a57919a94a0ecacbc78881f..52a4550acb128919049d3ec7f29735fd285d47d0 100644 (file)
@@ -47,6 +47,8 @@ public class IndexBuilder
   private byte _flags = IndexData.UNKNOWN_INDEX_FLAG;
   /** the names and orderings of the indexed columns */
   private final List<Column> _columns = new ArrayList<Column>();
+  /** 0-based index number */
+  private int _indexNumber;
 
   public IndexBuilder(String name) {
     _name = name;
@@ -141,6 +143,20 @@ public class IndexBuilder
     return this;
   }    
 
+  /**
+   * @usage _advanced_method_
+   */
+  public int getIndexNumber() {
+    return _indexNumber;
+  }
+
+  /**
+   * @usage _advanced_method_
+   */
+  public void setIndexNumber(int newIndexNumber) {
+    _indexNumber = newIndexNumber;
+  }
+
   public void validate(Set<String> tableColNames, JetFormat format) {
 
     DatabaseImpl.validateIdentifierName(
index e7a654a5cf37219d92dec4d53d9b695616c67c65..e776c3c2f74e51c3c4a280b4d2b771c9b6e43ec1 100644 (file)
@@ -37,6 +37,10 @@ public class TableModBuilder
     return new AddColumn(column);
   }
 
+  public AddIndex addIndex(IndexBuilder index) {
+    return new AddIndex(index);
+  }
+
   public class AddColumn
   {
     private ColumnBuilder _column;
@@ -50,4 +54,18 @@ public class TableModBuilder
       return new TableMutator((TableImpl)_table).addColumn(_column);
     }
   }
+
+  public class AddIndex
+  {
+    private IndexBuilder _index;
+
+    private AddIndex(IndexBuilder index) {
+      _index = index;
+    }
+
+    public Index add() throws IOException
+    {
+      return new TableMutator((TableImpl)_table).addIndex(_index);
+    }
+  }
 }
index e3825d277b29e853be1d5e713bc0cc1ba3901f2f..8abc390493ee92a39e98987f7887f62ff31abb7b 100644 (file)
@@ -22,6 +22,7 @@ import java.util.Set;
 
 import com.healthmarketscience.jackcess.ColumnBuilder;
 import com.healthmarketscience.jackcess.DataType;
+import com.healthmarketscience.jackcess.IndexBuilder;
 
 /**
  * Helper class used to maintain state during database mutation.
@@ -90,8 +91,25 @@ abstract class DBMutator
       setColumnSortOrder(column);
   }
 
-  protected void validateAutoNumberColumn(Set<DataType> autoTypes, 
-                                          ColumnBuilder column) 
+  protected void validateIndex(Set<String> colNames, Set<String> idxNames, 
+                               boolean[] foundPk, IndexBuilder index) {
+    
+    index.validate(colNames, getFormat());
+    if(!idxNames.add(index.getName().toUpperCase())) {
+      throw new IllegalArgumentException("duplicate index name: " +
+                                         index.getName());
+    }
+    if(index.isPrimaryKey()) {
+      if(foundPk[0]) {
+        throw new IllegalArgumentException(
+            "found second primary key index: " + index.getName());
+      }
+      foundPk[0] = true;
+    }
+  }
+
+  protected static void validateAutoNumberColumn(Set<DataType> autoTypes, 
+                                                 ColumnBuilder column) 
   {
     if(!column.getType().isMultipleAutoNumberAllowed() &&
        !autoTypes.add(column.getType())) {
@@ -117,41 +135,9 @@ abstract class DBMutator
     return null;
   }
 
-  /**
-   * Maintains additional state used during column creation.
-   * @usage _advanced_class_
-   */
-  static final class ColumnState
-  {
-    private byte _umapOwnedRowNumber;
-    private byte _umapFreeRowNumber;
-    // we always put both usage maps on the same page
-    private int _umapPageNumber;
-
-    public byte getUmapOwnedRowNumber() {
-      return _umapOwnedRowNumber;
-    }
-
-    public void setUmapOwnedRowNumber(byte newUmapOwnedRowNumber) {
-      _umapOwnedRowNumber = newUmapOwnedRowNumber;
-    }
+  public abstract int getTdefPageNumber();
 
-    public byte getUmapFreeRowNumber() {
-      return _umapFreeRowNumber;
-    }
-
-    public void setUmapFreeRowNumber(byte newUmapFreeRowNumber) {
-      _umapFreeRowNumber = newUmapFreeRowNumber;
-    }
-
-    public int getUmapPageNumber() {
-      return _umapPageNumber;
-    }
-
-    public void setUmapPageNumber(int newUmapPageNumber) {
-      _umapPageNumber = newUmapPageNumber;
-    }
-  }
+  abstract short getColumnNumber(String colName);
 
   /**
    * Maintains additional state used during column writing.
index 090baf0dd0502c2bf5e7b0669ee08cdfdfeaab57..10deea614f760f830f048e3a735e5b615136b24c 100644 (file)
@@ -482,9 +482,21 @@ public class IndexData {
    */
   protected static void writeRowCountDefinitions(
       TableCreator creator, ByteBuffer buffer)
+  {
+    writeRowCountDefinitions(creator, buffer, creator.getIndexCount());
+  }
+
+  /**
+   * Writes the index row count definitions into a table definition buffer.
+   * @param creator description of the indexes to write
+   * @param buffer Buffer to write to
+   * @param idxCount num indexes to write
+   */
+  protected static void writeRowCountDefinitions(
+      DBMutator creator, ByteBuffer buffer, int idxCount)
   {
     // index row counts (empty data)
-    ByteUtil.forward(buffer, (creator.getIndexCount() *
+    ByteUtil.forward(buffer, (idxCount *
                               creator.getFormat().SIZE_INDEX_DEFINITION));
   }
 
@@ -497,15 +509,13 @@ public class IndexData {
       TableCreator creator, ByteBuffer buffer)
     throws IOException
   {
-    ByteBuffer rootPageBuffer = creator.getPageChannel().createPageBuffer();
-    writeDataPage(rootPageBuffer, NEW_ROOT_DATA_PAGE, 
-                  creator.getTdefPageNumber(), creator.getFormat());
+    ByteBuffer rootPageBuffer = createRootPageBuffer(creator);
 
     for(TableCreator.IndexDataState idxDataState : creator.getIndexDataStates()) {
       buffer.putInt(MAGIC_INDEX_NUMBER); // seemingly constant magic value
 
       // write column information (always MAX_COLUMNS entries)
-      IndexBuilder idx = idxDataState.getIndex();
+      IndexBuilder idx = idxDataState.getFirstIndex();
       List<IndexBuilder.Column> idxColumns = idx.getColumns();
       for(int i = 0; i < MAX_COLUMNS; ++i) {
 
@@ -552,6 +562,76 @@ public class IndexData {
     }
   }
 
+  /**
+   * Writes the index definitions into a table definition buffer.
+   * @param creator description of the indexes to write
+   * @param buffer Buffer to write to
+   */
+  protected static void writeDefinition(
+      DBMutator creator, ByteBuffer buffer, 
+      TableCreator.IndexDataState idxDataState, ByteBuffer rootPageBuffer)
+    throws IOException
+  {
+    if(rootPageBuffer == null) {
+      rootPageBuffer = createRootPageBuffer(creator);
+    }
+
+    // FIXME
+    buffer.putInt(MAGIC_INDEX_NUMBER); // seemingly constant magic value
+
+    // write column information (always MAX_COLUMNS entries)
+    IndexBuilder idx = idxDataState.getFirstIndex();
+    List<IndexBuilder.Column> idxColumns = idx.getColumns();
+    for(int i = 0; i < MAX_COLUMNS; ++i) {
+
+      short columnNumber = COLUMN_UNUSED;
+      byte flags = 0;
+
+      if(i < idxColumns.size()) {
+
+        // determine column info
+        IndexBuilder.Column idxCol = idxColumns.get(i);
+        flags = idxCol.getFlags();
+
+        // find actual table column number
+        columnNumber = creator.getColumnNumber(idxCol.getName());
+        if(columnNumber == COLUMN_UNUSED) {
+          // should never happen as this is validated before
+          // FIXME
+          // throw new IllegalArgumentException(
+          //     withErrorContext(
+          //         "Column with name " + idxCol.getName() + " not found",
+          //         creator.getDatabase(), creator.getName(), idx.getName()));
+        }
+      }
+         
+      buffer.putShort(columnNumber); // table column number
+      buffer.put(flags); // column flags (e.g. ordering)
+    }
+
+    // FIXME
+    // buffer.put(idxDataState.getUmapRowNumber()); // umap row
+    // ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); // umap page
+
+    // // write empty root index page
+    // creator.getPageChannel().writePage(rootPageBuffer, 
+    //                                    idxDataState.getRootPageNumber());
+
+    // buffer.putInt(idxDataState.getRootPageNumber());
+    // buffer.putInt(0); // unknown
+    // buffer.put(idx.getFlags()); // index flags (unique, etc.)
+    // ByteUtil.forward(buffer, 5); // unknown
+  }
+
+  private static ByteBuffer createRootPageBuffer(DBMutator creator) 
+    throws IOException
+  {
+    ByteBuffer rootPageBuffer = creator.getPageChannel().createPageBuffer();
+    writeDataPage(rootPageBuffer, NEW_ROOT_DATA_PAGE, 
+                  creator.getTdefPageNumber(), creator.getFormat());
+    return rootPageBuffer;
+  }
+
   /**
    * Prepares to add a row to this index.  All constraints are checked before
    * this method returns.
index 41eb66933696f20f83c15b4ee2d705656cfed7ec..a92707143a9e7d40311ac19376efd7f2d559f0c4 100644 (file)
@@ -342,10 +342,10 @@ public class IndexImpl implements Index, Comparable<IndexImpl>
   {
     // write logical index information
     for(IndexBuilder idx : creator.getIndexes()) {
-      TableCreator.IndexState idxState = creator.getIndexState(idx);
+      TableCreator.IndexDataState idxDataState = creator.getIndexDataState(idx);
       buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); // seemingly constant magic value which matches the table def
-      buffer.putInt(idxState.getIndexNumber()); // index num
-      buffer.putInt(idxState.getIndexDataState().getIndexDataNumber()); // index data num
+      buffer.putInt(idx.getIndexNumber()); // index num
+      buffer.putInt(idxDataState.getIndexDataNumber()); // index data num
       buffer.put((byte)0); // related table type
       buffer.putInt(INVALID_INDEX_NUMBER); // related index num
       buffer.putInt(0); // related table definition page number
index a58622f30bf80824ff6d26b4e00d7bbc2a42e534..2039d630a144ea4a73dd89e19e0b9863b5f0e59f 100644 (file)
@@ -41,8 +41,6 @@ class TableCreator extends DBMutator
   private final String _name;
   private final List<ColumnBuilder> _columns;
   private final List<IndexBuilder> _indexes;
-  private final Map<IndexBuilder,IndexState> _indexStates = 
-    new IdentityHashMap<IndexBuilder,IndexState>();
   private final List<IndexDataState> _indexDataStates = 
     new ArrayList<IndexDataState>();
   private final Map<ColumnBuilder,ColumnState> _columnStates = 
@@ -66,6 +64,7 @@ class TableCreator extends DBMutator
     return _name;
   }
 
+  @Override
   public int getTdefPageNumber() {
     return _tdefPageNumber;
   }
@@ -94,8 +93,15 @@ class TableCreator extends DBMutator
     return _logicalIndexCount;
   }
 
-  public IndexState getIndexState(IndexBuilder idx) {
-    return _indexStates.get(idx);
+  public IndexDataState getIndexDataState(IndexBuilder idx) {
+    for(IndexDataState idxDataState : _indexDataStates) {
+      for(IndexBuilder curIdx : idxDataState.getIndexes()) {
+        if(idx == curIdx) {
+          return idxDataState;
+        }
+      }
+    }
+    throw new IllegalStateException("could not find state for index");
   }
 
   public List<IndexDataState> getIndexDataStates() {
@@ -110,6 +116,16 @@ class TableCreator extends DBMutator
     return _lvalCols;
   }
 
+  @Override
+  short getColumnNumber(String colName) {
+    for(ColumnBuilder col : _columns) {
+      if(col.getName().equalsIgnoreCase(colName)) {
+        return col.getColumnNumber();
+      }
+    }
+    return IndexData.COLUMN_UNUSED;
+  }  
+
   /**
    * @return The number of variable length columns which are not long values
    *         found in the list
@@ -146,13 +162,10 @@ class TableCreator extends DBMutator
     }
 
     if(hasIndexes()) {
-      // sort out index numbers.  for now, these values will always match
-      // (until we support writing foreign key indexes)
+      // sort out index numbers (and backing index data).  
       for(IndexBuilder idx : _indexes) {
-        IndexState idxState = new IndexState();
-        idxState.setIndexNumber(_logicalIndexCount++);
-        idxState.setIndexDataState(findIndexDataState(idx));
-        _indexStates.put(idx, idxState);
+        idx.setIndexNumber(_logicalIndexCount++);
+        findIndexDataState(idx);
       }
     }
 
@@ -180,15 +193,16 @@ class TableCreator extends DBMutator
     // search for an index which matches the given index (in terms of the
     // backing data)
     for(IndexDataState idxDataState : _indexDataStates) {
-      if(sameIndexData(idxDataState.getIndex(), idx)) {
+      if(sameIndexData(idxDataState.getFirstIndex(), idx)) {
+        idxDataState.addIndex(idx);
         return idxDataState;
       }
     }
 
     // no matches found, need new index data state
     IndexDataState idxDataState = new IndexDataState();
-    idxDataState.setIndex(idx);
     idxDataState.setIndexDataNumber(_indexCount++);
+    idxDataState.addIndex(idx);
     _indexDataStates.add(idxDataState);
     return idxDataState;
   }
@@ -236,20 +250,9 @@ class TableCreator extends DBMutator
 
       // now, validate the indexes
       Set<String> idxNames = new HashSet<String>();
-      boolean foundPk = false;
+      boolean foundPk[] = new boolean[1];
       for(IndexBuilder index : _indexes) {
-        index.validate(colNames, getFormat());
-        if(!idxNames.add(index.getName().toUpperCase())) {
-          throw new IllegalArgumentException("duplicate index name: " +
-                                             index.getName());
-        }
-        if(index.isPrimaryKey()) {
-          if(foundPk) {
-            throw new IllegalArgumentException(
-                "found second primary key index: " + index.getName());
-          }
-          foundPk = true;
-        }
+        validateIndex(colNames, idxNames, foundPk, index);
       }
     }
   }
@@ -295,52 +298,65 @@ class TableCreator extends DBMutator
   }
 
   /**
-   * Maintains additional state used during index creation.
+   * Maintains additional state used during column creation.
    * @usage _advanced_class_
    */
-  static final class IndexState
+  static final class ColumnState
   {
-    private int _indexNumber;
-    private IndexDataState _dataState;
+    private byte _umapOwnedRowNumber;
+    private byte _umapFreeRowNumber;
+    // we always put both usage maps on the same page
+    private int _umapPageNumber;
 
-    public int getIndexNumber() {
-      return _indexNumber;
+    public byte getUmapOwnedRowNumber() {
+      return _umapOwnedRowNumber;
     }
 
-    public void setIndexNumber(int newIndexNumber) {
-      _indexNumber = newIndexNumber;
+    public void setUmapOwnedRowNumber(byte newUmapOwnedRowNumber) {
+      _umapOwnedRowNumber = newUmapOwnedRowNumber;
     }
 
-    public IndexDataState getIndexDataState() {
-      return _dataState;
+    public byte getUmapFreeRowNumber() {
+      return _umapFreeRowNumber;
     }
 
-    public void setIndexDataState(IndexDataState dataState) {
-      _dataState = dataState;
+    public void setUmapFreeRowNumber(byte newUmapFreeRowNumber) {
+      _umapFreeRowNumber = newUmapFreeRowNumber;
+    }
+
+    public int getUmapPageNumber() {
+      return _umapPageNumber;
+    }
+
+    public void setUmapPageNumber(int newUmapPageNumber) {
+      _umapPageNumber = newUmapPageNumber;
     }
   }
-    
+
   /**
    * Maintains additional state used during index data creation.
    * @usage _advanced_class_
    */
   static final class IndexDataState
   {
-    // all indexes which have the same backing IndexDataState will have
-    // equivalent columns and flags.  we keep a reference to the first index
-    // which uses this backing index data.
-    private IndexBuilder _idx;
+    private final List<IndexBuilder> _indexes = new ArrayList<IndexBuilder>();
     private int _indexDataNumber;
     private byte _umapRowNumber;
     private int _umapPageNumber;
     private int _rootPageNumber;
 
-    public IndexBuilder getIndex() {
-      return _idx;
+    public IndexBuilder getFirstIndex() {
+      // all indexes which have the same backing IndexDataState will have
+      // equivalent columns and flags.
+      return _indexes.get(0);
+    }
+
+    public List<IndexBuilder> getIndexes() {
+      return _indexes;
     }
 
-    public void setIndex(IndexBuilder idx) {
-      _idx = idx;
+    public void addIndex(IndexBuilder idx) {
+      _indexes.add(idx);
     }
 
     public int getIndexDataNumber() {
index 1a3d19f43c662684562a5f4a5181375200b811c8..5c704b607fa90a8a02776db53e59cf3f94ddbb63 100644 (file)
@@ -1117,22 +1117,18 @@ public class TableImpl implements Table
     boolean isLongVal = column.getType().isLongValue();
 
     // calculate how much more space we need in the table def
-    int addedLen = 0;
-
     if(isLongVal) {
-      addedLen += 10;
+      mutator.addTdefLen(10);
     }
 
-    addedLen += format.SIZE_COLUMN_DEF_BLOCK;
+    mutator.addTdefLen(format.SIZE_COLUMN_DEF_BLOCK);
 
     int nameByteLen = DBMutator.calculateNameLength(column.getName());
-    addedLen += nameByteLen;
+    mutator.addTdefLen(nameByteLen);
 
     // load current table definition and add space for new info
-    List<Integer> nextPages = new ArrayList<Integer>(1);
     ByteBuffer tableBuffer = loadCompleteTableDefinitionBufferForUpdate(
-        nextPages, addedLen);
-    int origTdefLen = tableBuffer.limit();
+        mutator);
 
     // update various bits of the table def
     ByteUtil.forward(tableBuffer, 29);
@@ -1195,13 +1191,13 @@ public class TableImpl implements Table
 
       // skip past index defs
       System.out.println("FOO pre move " + tableBuffer.position());
-      ByteUtil.forward(tableBuffer, (_indexDatas.size() * 
+      ByteUtil.forward(tableBuffer, (_indexCount * 
                                      format.SIZE_INDEX_COLUMN_BLOCK));
       System.out.println("FOO moved to " + tableBuffer.position());
       ByteUtil.forward(tableBuffer,
-                       (_indexes.size() * format.SIZE_INDEX_INFO_BLOCK));
+                       (_logicalIndexCount * format.SIZE_INDEX_INFO_BLOCK));
       System.out.println("FOO moved to " + tableBuffer.position());
-      for(int i = 0; i < _indexes.size(); ++i) {
+      for(int i = 0; i < _logicalIndexCount; ++i) {
         short len = tableBuffer.getShort();
         System.out.println("FOO skipping " + len);
         ByteUtil.forward(tableBuffer, len);
@@ -1235,7 +1231,7 @@ public class TableImpl implements Table
     }
 
     // sanity check the updates
-    if((origTdefLen + addedLen) != tableBuffer.limit()) {
+    if(!mutator.validateUpdatedTdef(tableBuffer)) {
       throw new IllegalStateException(
           withErrorContext("Failed update table definition (unexpected length)"));
     }
@@ -1247,7 +1243,7 @@ public class TableImpl implements Table
 
     // write updated table def back to the database
     writeTableDefinitionBuffer(tableBuffer, _tableDefPageNumber, mutator, 
-                               nextPages);
+                               mutator.getNextPages());
 
 
     // now, update current TableImpl
@@ -1276,27 +1272,119 @@ public class TableImpl implements Table
       newCol.setColumnValidator(null);
     }
 
+    // save any column properties
+    Map<String,PropertyMap.Property> colProps = column.getProperties();
+    if(colProps != null) {
+      newCol.getProperties().putAll(colProps.values());
+      getProperties().save();
+    }
+
+    completeTableMutation(tableBuffer);
+
+    return newCol;
+  }
+
+  /**
+   * Writes a index defined by the given TableMutator to this table.
+   * @usage _advanced_method_
+   */
+  protected IndexImpl mutateAddIndex(TableMutator mutator) throws IOException
+  {
+    IndexBuilder index = mutator.getIndex();
+    JetFormat format = mutator.getFormat();
+
+    // calculate how much more space we need in the table def
+    mutator.addTdefLen(format.SIZE_INDEX_INFO_BLOCK);
+
+    // FIXME
+    int indexDataNumber = 0;
+    boolean addingIndexData = (indexDataNumber >= _indexCount);
+    if(addingIndexData) {
+
+      // we are adding an index data as well
+      mutator.addTdefLen(format.SIZE_INDEX_DEFINITION + 
+                         format.SIZE_INDEX_COLUMN_BLOCK);
+    }
+
+    int nameByteLen = DBMutator.calculateNameLength(index.getName());
+    mutator.addTdefLen(nameByteLen);
+
+    // load current table definition and add space for new info
+    ByteBuffer tableBuffer = loadCompleteTableDefinitionBufferForUpdate(
+        mutator);
+
+    // update various bits of the table def
+    ByteUtil.forward(tableBuffer, 35);
+    tableBuffer.putInt(_logicalIndexCount + 1);
+    int numIdxData = _indexCount + (addingIndexData ? 1 : 0);
+    tableBuffer.putInt(numIdxData);
+
+    // move to end of index data def blocks
+    tableBuffer.position(format.SIZE_TDEF_HEADER + 
+                         (_indexCount * format.SIZE_INDEX_DEFINITION));
+
+    if(addingIndexData) {
+      // write index row count definition (empty initially)
+      ByteUtil.insertEmptyData(tableBuffer, format.SIZE_INDEX_DEFINITION);
+      IndexData.writeRowCountDefinitions(mutator, tableBuffer, 1);
+    }
+
+    // skip columns and column names
+    ByteUtil.forward(tableBuffer, 
+                     (_columns.size() * format.SIZE_COLUMN_DEF_BLOCK));
+    for(int i = 0; i < _columns.size(); ++i) {
+      ByteUtil.forward(tableBuffer, tableBuffer.getShort());
+    }
+
+    // move to end of current index datas
+    ByteUtil.forward(tableBuffer, (_indexCount * 
+                                   format.SIZE_INDEX_COLUMN_BLOCK));
+
+    if(addingIndexData) {
+      // write index data def
+      ByteUtil.insertEmptyData(tableBuffer, format.SIZE_INDEX_COLUMN_BLOCK);
+      
+      // FIXME
+    }
+
+    // FIXME
+
+    IndexImpl newIdx = null;
+
+    // FIXME
+
+
+    // FIXME, need to reset fkenforcer?
+
+    completeTableMutation(tableBuffer);
+
+    return newIdx;
+  }
+
+  private void completeTableMutation(ByteBuffer tableBuffer) throws IOException
+  {
     // lastly, may need to clear table def buffer
     _tableDefBufferH.possiblyInvalidate(_tableDefPageNumber, tableBuffer);
 
     // update modification count so any active RowStates can keep themselves
     // up-to-date
     ++_modCount;
-
-    return newCol;
   }
 
   private ByteBuffer loadCompleteTableDefinitionBufferForUpdate(
-      List<Integer> nextPages, int addedLen)
+      TableMutator mutator)
     throws IOException
   {
     // load complete table definition
     ByteBuffer tableBuffer = _tableDefBufferH.setPage(getPageChannel(),
                                                       _tableDefPageNumber);
-    tableBuffer = loadCompleteTableDefinitionBuffer(tableBuffer, nextPages);
+    tableBuffer = loadCompleteTableDefinitionBuffer(
+        tableBuffer, mutator.getNextPages());
 
     // make sure the table buffer has enough room for the new info
+    int addedLen = mutator.getAddedTdefLen();
     int origTdefLen = tableBuffer.getInt(8);
+    mutator.setOrigTdefLen(origTdefLen);
     int newTdefLen = origTdefLen + addedLen;
     System.out.println("FOO new " + newTdefLen + " add " + addedLen);
     while(newTdefLen > tableBuffer.capacity()) {
@@ -1308,7 +1396,7 @@ public class TableImpl implements Table
 
     // set new tdef length
     tableBuffer.position(8);
-    tableBuffer.putInt(newTdefLen);    
+    tableBuffer.putInt(newTdefLen);
 
     return tableBuffer;
   }
index 2dd2fa5ec8ca8f82ad659d5858364fefb1bb279c..72a14819aac75ebcac6645099f57b6a38cbebdb7 100644 (file)
@@ -17,12 +17,16 @@ limitations under the License.
 package com.healthmarketscience.jackcess.impl;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import com.healthmarketscience.jackcess.ColumnBuilder;
 import com.healthmarketscience.jackcess.DataType;
+import com.healthmarketscience.jackcess.IndexBuilder;
 
 /**
  * Helper class used to maintain state during table mutation.
@@ -35,6 +39,10 @@ public class TableMutator extends DBMutator
   private final TableImpl _table;
 
   private ColumnBuilder _column;
+  private IndexBuilder _index;
+  private int _origTdefLen;
+  private int _addedTdefLen;
+  private List<Integer> _nextPages = new ArrayList<Integer>(1);
 
   public TableMutator(TableImpl table) {
     super(table.getDatabase());
@@ -45,13 +53,48 @@ public class TableMutator extends DBMutator
     return _column;
   }
 
-  public ColumnImpl addColumn(ColumnBuilder column) throws IOException
-  {
+  public IndexBuilder getIndex() {
+    return _index;
+  }
+
+  @Override
+  public int getTdefPageNumber() {
+    return _table.getTableDefPageNumber();
+  }
+
+  @Override
+  short getColumnNumber(String colName) {
+    for(ColumnImpl col : _table.getColumns()) {
+      if(col.getName().equalsIgnoreCase(colName)) {
+        return col.getColumnNumber();
+      }
+    }
+    return IndexData.COLUMN_UNUSED;
+  }
+
+  int getAddedTdefLen() {
+    return _addedTdefLen;
+  }
+
+  void addTdefLen(int add) {
+    _addedTdefLen += add;
+  }
+
+  void setOrigTdefLen(int len) {
+    _origTdefLen = len;
+  }
+
+  List<Integer> getNextPages() {
+    return _nextPages;
+  }
+
+  public ColumnImpl addColumn(ColumnBuilder column) throws IOException {
+
     _column = column;
 
     validateAddColumn();
     
-    // assign column numbers and do some assorted column bookkeeping
+    // assign column number and do some assorted column bookkeeping
     short columnNumber = (short)_table.getMaxColumnCount();
     _column.setColumnNumber(columnNumber);
 
@@ -65,8 +108,38 @@ public class TableMutator extends DBMutator
     }
   }
 
-  private void validateAddColumn()
-  {
+  public IndexImpl addIndex(IndexBuilder index) throws IOException {
+
+    _index = index;
+
+    validateAddIndex();
+
+    // assign index number and do some assorted index bookkeeping
+    int indexNumber = _table.getIndexes().size();
+    _index.setIndexNumber(indexNumber);
+
+    // find backing index state
+
+    // FIXME, writeme!
+
+
+    getPageChannel().startExclusiveWrite();
+    try {
+
+      return _table.mutateAddIndex(this);
+
+    } finally {
+      getPageChannel().finishWrite();
+    }
+  }
+
+  boolean validateUpdatedTdef(ByteBuffer tableBuffer) {
+    // sanity check the updates
+    return((_origTdefLen + _addedTdefLen) == tableBuffer.limit());
+  }
+
+  private void validateAddColumn() {
+
     if(_column == null) {
       throw new IllegalArgumentException("Cannot add column with no column");
     }
@@ -76,12 +149,8 @@ public class TableMutator extends DBMutator
           getFormat().MAX_COLUMNS_PER_TABLE + " columns");
     }
     
-    Set<String> colNames = new HashSet<String>();
-    // next, validate the column definitions
-    for(ColumnImpl column : _table.getColumns()) {
-      colNames.add(column.getName().toUpperCase());
-    }
-
+    Set<String> colNames = getColumnNames();
+    // next, validate the column definition
     validateColumn(colNames, _column);
     
     if(_column.isAutoNumber()) {
@@ -94,4 +163,40 @@ public class TableMutator extends DBMutator
       validateAutoNumberColumn(autoTypes, _column);
     }
   }
+
+  private void validateAddIndex() {
+    
+    if(_index == null) {
+      throw new IllegalArgumentException("Cannot add index with no index");
+    }
+    if((_table.getIndexes().size() + 1) > getFormat().MAX_INDEXES_PER_TABLE) {
+      throw new IllegalArgumentException(
+          "Cannot add index to table with " +
+          getFormat().MAX_INDEXES_PER_TABLE + " indexes");
+    }
+    
+    boolean foundPk[] = new boolean[1];
+    Set<String> idxNames = getIndexNames(foundPk);
+    // next, validate the index definition
+    validateIndex(getColumnNames(), idxNames, foundPk, _index);    
+  }
+
+  private Set<String> getColumnNames() {
+    Set<String> colNames = new HashSet<String>();
+    for(ColumnImpl column : _table.getColumns()) {
+      colNames.add(column.getName().toUpperCase());
+    }
+    return colNames;
+  }
+
+  private Set<String> getIndexNames(boolean[] foundPk) {
+    Set<String> idxNames = new HashSet<String>();
+    for(IndexImpl index : _table.getIndexes()) {
+      idxNames.add(index.getName().toUpperCase());
+      if(index.isPrimaryKey()) {
+        foundPk[0] = true;
+      }
+    }
+    return idxNames;
+  }
 }