aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2016-05-19 02:43:54 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2016-05-19 02:43:54 +0000
commitaa9555c6679d41a7c6f9d05c7fa2178d2882dd6e (patch)
tree8f9db0983f9bc2da8678bf01a07cae1157106780
parent627e22dee739fb737df8fb1443554e243d903222 (diff)
downloadjackcess-aa9555c6679d41a7c6f9d05c7fa2178d2882dd6e.tar.gz
jackcess-aa9555c6679d41a7c6f9d05c7fa2178d2882dd6e.zip
add support for sharing backing index data across indexes during table creation
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/mutateops@994 f203690c-595d-4dc9-a70b-905162fa7fd2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java11
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java2
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java92
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java10
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/IndexTest.java51
5 files changed, 149 insertions, 17 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
index e5e6dc7..090baf0 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
@@ -501,10 +501,11 @@ public class IndexData {
writeDataPage(rootPageBuffer, NEW_ROOT_DATA_PAGE,
creator.getTdefPageNumber(), creator.getFormat());
- for(IndexBuilder idx : creator.getIndexes()) {
+ 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();
List<IndexBuilder.Column> idxColumns = idx.getColumns();
for(int i = 0; i < MAX_COLUMNS; ++i) {
@@ -537,16 +538,14 @@ public class IndexData {
buffer.put(flags); // column flags (e.g. ordering)
}
- TableCreator.IndexState idxState = creator.getIndexState(idx);
-
- buffer.put(idxState.getUmapRowNumber()); // umap row
+ buffer.put(idxDataState.getUmapRowNumber()); // umap row
ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); // umap page
// write empty root index page
creator.getPageChannel().writePage(rootPageBuffer,
- idxState.getRootPageNumber());
+ idxDataState.getRootPageNumber());
- buffer.putInt(idxState.getRootPageNumber());
+ buffer.putInt(idxDataState.getRootPageNumber());
buffer.putInt(0); // unknown
buffer.put(idx.getFlags()); // index flags (unique, etc.)
ByteUtil.forward(buffer, 5); // unknown
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
index f3fe868..41eb669 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
@@ -345,7 +345,7 @@ public class IndexImpl implements Index, Comparable<IndexImpl>
TableCreator.IndexState idxState = creator.getIndexState(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.getIndexDataNumber()); // index data num
+ buffer.putInt(idxState.getIndexDataState().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 89da310..a58622f 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
@@ -43,6 +43,8 @@ class TableCreator extends DBMutator
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 =
new IdentityHashMap<ColumnBuilder,ColumnState>();
private final List<ColumnBuilder> _lvalCols = new ArrayList<ColumnBuilder>();
@@ -96,6 +98,10 @@ class TableCreator extends DBMutator
return _indexStates.get(idx);
}
+ public List<IndexDataState> getIndexDataStates() {
+ return _indexDataStates;
+ }
+
public ColumnState getColumnState(ColumnBuilder col) {
return _columnStates.get(col);
}
@@ -145,7 +151,7 @@ class TableCreator extends DBMutator
for(IndexBuilder idx : _indexes) {
IndexState idxState = new IndexState();
idxState.setIndexNumber(_logicalIndexCount++);
- idxState.setIndexDataNumber(_indexCount++);
+ idxState.setIndexDataState(findIndexDataState(idx));
_indexStates.put(idx, idxState);
}
}
@@ -169,6 +175,24 @@ class TableCreator extends DBMutator
}
}
+ private IndexDataState findIndexDataState(IndexBuilder idx) {
+
+ // search for an index which matches the given index (in terms of the
+ // backing data)
+ for(IndexDataState idxDataState : _indexDataStates) {
+ if(sameIndexData(idxDataState.getIndex(), idx)) {
+ return idxDataState;
+ }
+ }
+
+ // no matches found, need new index data state
+ IndexDataState idxDataState = new IndexDataState();
+ idxDataState.setIndex(idx);
+ idxDataState.setIndexDataNumber(_indexCount++);
+ _indexDataStates.add(idxDataState);
+ return idxDataState;
+ }
+
/**
* Validates the new table information before attempting creation.
*/
@@ -241,6 +265,35 @@ class TableCreator extends DBMutator
return autoCols;
}
+ private static boolean sameIndexData(IndexBuilder idx1, IndexBuilder idx2) {
+ // index data can be combined if flags match and columns (and col flags)
+ // match
+ if(idx1.getFlags() != idx2.getFlags()) {
+ return false;
+ }
+
+ if(idx1.getColumns().size() != idx2.getColumns().size()) {
+ return false;
+ }
+
+ for(int i = 0; i < idx1.getColumns().size(); ++i) {
+ IndexBuilder.Column col1 = idx1.getColumns().get(i);
+ IndexBuilder.Column col2 = idx2.getColumns().get(i);
+
+ if(!sameIndexData(col1, col2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean sameIndexData(
+ IndexBuilder.Column col1, IndexBuilder.Column col2) {
+ return (col1.getName().equals(col2.getName()) &&
+ (col1.getFlags() == col2.getFlags()));
+ }
+
/**
* Maintains additional state used during index creation.
* @usage _advanced_class_
@@ -248,10 +301,7 @@ class TableCreator extends DBMutator
static final class IndexState
{
private int _indexNumber;
- private int _indexDataNumber;
- private byte _umapRowNumber;
- private int _umapPageNumber;
- private int _rootPageNumber;
+ private IndexDataState _dataState;
public int getIndexNumber() {
return _indexNumber;
@@ -261,6 +311,38 @@ class TableCreator extends DBMutator
_indexNumber = newIndexNumber;
}
+ public IndexDataState getIndexDataState() {
+ return _dataState;
+ }
+
+ public void setIndexDataState(IndexDataState dataState) {
+ _dataState = dataState;
+ }
+ }
+
+ /**
+ * 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 int _indexDataNumber;
+ private byte _umapRowNumber;
+ private int _umapPageNumber;
+ private int _rootPageNumber;
+
+ public IndexBuilder getIndex() {
+ return _idx;
+ }
+
+ public void setIndex(IndexBuilder idx) {
+ _idx = idx;
+ }
+
public int getIndexDataNumber() {
return _indexDataNumber;
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
index 18fee24..1a3d19f 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
@@ -1503,16 +1503,16 @@ public class TableImpl implements Table
// index umap
int indexIdx = i - 2;
- IndexBuilder idx = creator.getIndexes().get(indexIdx);
+ TableCreator.IndexDataState idxDataState =
+ creator.getIndexDataStates().get(indexIdx);
// allocate root page for the index
int rootPageNumber = pageChannel.allocateNewPage();
// stash info for later use
- TableCreator.IndexState idxState = creator.getIndexState(idx);
- idxState.setRootPageNumber(rootPageNumber);
- idxState.setUmapRowNumber((byte)umapRowNum);
- idxState.setUmapPageNumber(umapPageNumber);
+ idxDataState.setRootPageNumber(rootPageNumber);
+ idxDataState.setUmapRowNumber((byte)umapRowNum);
+ idxDataState.setUmapPageNumber(umapPageNumber);
// index map definition, including initial root page
umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
diff --git a/src/test/java/com/healthmarketscience/jackcess/IndexTest.java b/src/test/java/com/healthmarketscience/jackcess/IndexTest.java
index 28e2ff9..47f7b89 100644
--- a/src/test/java/com/healthmarketscience/jackcess/IndexTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/IndexTest.java
@@ -462,6 +462,57 @@ public class IndexTest extends TestCase {
}
}
+ public void testIndexCreationSharedData() throws Exception
+ {
+ for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
+ Database db = create(fileFormat);
+
+ Table t = new TableBuilder("TestTable")
+ .addColumn(new ColumnBuilder("id", DataType.LONG))
+ .addColumn(new ColumnBuilder("data", DataType.TEXT))
+ .addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
+ .addColumns("id").setPrimaryKey())
+ .addIndex(new IndexBuilder("Index1").addColumns("id"))
+ .addIndex(new IndexBuilder("Index2").addColumns("id"))
+ .addIndex(new IndexBuilder("Index3").addColumns(false, "id"))
+ .toTable(db);
+
+ assertEquals(4, t.getIndexes().size());
+ IndexImpl idx = (IndexImpl)t.getIndexes().get(0);
+
+ assertEquals(IndexBuilder.PRIMARY_KEY_NAME, idx.getName());
+ assertEquals(1, idx.getColumns().size());
+ assertEquals("id", idx.getColumns().get(0).getName());
+ assertTrue(idx.getColumns().get(0).isAscending());
+ assertTrue(idx.isPrimaryKey());
+ assertTrue(idx.isUnique());
+ assertFalse(idx.shouldIgnoreNulls());
+ assertNull(idx.getReference());
+
+ IndexImpl idx1 = (IndexImpl)t.getIndexes().get(1);
+ IndexImpl idx2 = (IndexImpl)t.getIndexes().get(2);
+ IndexImpl idx3 = (IndexImpl)t.getIndexes().get(3);
+
+ assertNotSame(idx.getIndexData(), idx1.getIndexData());
+ assertSame(idx1.getIndexData(), idx2.getIndexData());
+ assertNotSame(idx2.getIndexData(), idx3.getIndexData());
+
+ t.addRow(2, "row2");
+ t.addRow(1, "row1");
+ t.addRow(3, "row3");
+
+ Cursor c = t.newCursor()
+ .setIndexByName(IndexBuilder.PRIMARY_KEY_NAME).toCursor();
+
+ for(int i = 1; i <= 3; ++i) {
+ Map<String,Object> row = c.getNextRow();
+ assertEquals(i, row.get("id"));
+ assertEquals("row" + i, row.get("data"));
+ }
+ assertFalse(c.moveToNextRow());
+ }
+ }
+
public void testGetForeignKeyIndex() throws Exception
{
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX, true)) {