diff options
author | James Ahlborn <jtahlborn@yahoo.com> | 2016-06-01 01:51:55 +0000 |
---|---|---|
committer | James Ahlborn <jtahlborn@yahoo.com> | 2016-06-01 01:51:55 +0000 |
commit | a45ac7fc22c133a110ae54a6261857f96491bff8 (patch) | |
tree | dcb8c42bfff20e03ba32a0c997ad4a730fed76af /src | |
parent | aa9555c6679d41a7c6f9d05c7fa2178d2882dd6e (diff) | |
download | jackcess-a45ac7fc22c133a110ae54a6261857f96491bff8.tar.gz jackcess-a45ac7fc22c133a110ae54a6261857f96491bff8.zip |
checkpointing some progress on add index
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/mutateops@995 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src')
8 files changed, 428 insertions, 119 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java b/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java index 9c9f584..52a4550 100644 --- a/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java @@ -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( diff --git a/src/main/java/com/healthmarketscience/jackcess/TableModBuilder.java b/src/main/java/com/healthmarketscience/jackcess/TableModBuilder.java index e7a654a..e776c3c 100644 --- a/src/main/java/com/healthmarketscience/jackcess/TableModBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/TableModBuilder.java @@ -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); + } + } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java b/src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java index e3825d2..8abc390 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java @@ -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. diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java index 090baf0..10deea6 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java @@ -483,8 +483,20 @@ 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) { @@ -553,6 +563,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. * <p> diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java index 41eb669..a927071 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java @@ -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 diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java index a58622f..2039d63 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java @@ -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() { diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java index 1a3d19f..5c704b6 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java @@ -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; } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java index 2dd2fa5..72a1481 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java @@ -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; + } } |