aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2016-07-19 03:45:53 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2016-07-19 03:45:53 +0000
commit060172ce30489655622702db9f3c6ce119636735 (patch)
tree638c511e967525b90b39e7dad4d2c130f5a50c8e /src/main
parentf2578a5a48195e58c2ce37b274c05f4514276bcc (diff)
downloadjackcess-060172ce30489655622702db9f3c6ce119636735.tar.gz
jackcess-060172ce30489655622702db9f3c6ce119636735.zip
reorg to prep for RelationshipBuilder; move remaining table creation logic from TableBuilder to TableCreator
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/mutateops@1002 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java7
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java7
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Relationship.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/TableBuilder.java55
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java8
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java211
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java20
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java10
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java4
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/RelationshipImpl.java23
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java56
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java26
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java342
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java281
14 files changed, 571 insertions, 485 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
index 69b4b33..9be67d2 100644
--- a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
@@ -26,7 +26,7 @@ import com.healthmarketscience.jackcess.impl.DatabaseImpl;
import com.healthmarketscience.jackcess.impl.JetFormat;
import com.healthmarketscience.jackcess.impl.PropertyMapImpl;
import com.healthmarketscience.jackcess.impl.TableImpl;
-import com.healthmarketscience.jackcess.impl.TableMutator;
+import com.healthmarketscience.jackcess.impl.TableUpdater;
/**
* Builder style class for constructing a {@link Column}. See {@link
@@ -479,9 +479,8 @@ public class ColumnBuilder {
* Adds a new Column to the given Table with the currently configured
* attributes.
*/
- public Column addToTable(Table table) throws IOException
- {
- return new TableMutator((TableImpl)table).addColumn(this);
+ public Column addToTable(Table table) throws IOException {
+ return new TableUpdater((TableImpl)table).addColumn(this);
}
private String withErrorContext(String msg) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java b/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java
index 36a64dc..7822b2d 100644
--- a/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java
@@ -27,7 +27,7 @@ import com.healthmarketscience.jackcess.impl.IndexData;
import com.healthmarketscience.jackcess.impl.IndexImpl;
import com.healthmarketscience.jackcess.impl.JetFormat;
import com.healthmarketscience.jackcess.impl.TableImpl;
-import com.healthmarketscience.jackcess.impl.TableMutator;
+import com.healthmarketscience.jackcess.impl.TableUpdater;
/**
* Builder style class for constructing an {@link Index}. See {@link
@@ -199,9 +199,8 @@ public class IndexBuilder
* Adds a new Index to the given Table with the currently configured
* attributes.
*/
- public Index addToTable(Table table) throws IOException
- {
- return new TableMutator((TableImpl)table).addIndex(this);
+ public Index addToTable(Table table) throws IOException {
+ return new TableUpdater((TableImpl)table).addIndex(this);
}
private String withErrorContext(String msg) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/Relationship.java b/src/main/java/com/healthmarketscience/jackcess/Relationship.java
index 2474b52..4200e57 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Relationship.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Relationship.java
@@ -27,6 +27,10 @@ import java.util.List;
*/
public interface Relationship
{
+ public enum JoinType {
+ INNER, LEFT_OUTER, RIGHT_OUTER;
+ }
+
public String getName();
public Table getFromTable();
@@ -50,4 +54,6 @@ public interface Relationship
public boolean isLeftOuterJoin();
public boolean isRightOuterJoin();
+
+ public JoinType getJoinType();
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/TableBuilder.java b/src/main/java/com/healthmarketscience/jackcess/TableBuilder.java
index 8e5272e..8f0b233 100644
--- a/src/main/java/com/healthmarketscience/jackcess/TableBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/TableBuilder.java
@@ -28,6 +28,7 @@ import java.util.Set;
import com.healthmarketscience.jackcess.impl.DatabaseImpl;
import com.healthmarketscience.jackcess.impl.PropertyMapImpl;
+import com.healthmarketscience.jackcess.impl.TableCreator;
/**
* Builder style class for constructing a {@link Table}.
@@ -116,6 +117,9 @@ public class TableBuilder {
}
}
+ public String getName() {
+ return _name;
+ }
/**
* Adds a Column to the new table.
@@ -140,6 +144,10 @@ public class TableBuilder {
return this;
}
+ public List<ColumnBuilder> getColumns() {
+ return _columns;
+ }
+
/**
* Adds an IndexBuilder to the new table.
*/
@@ -155,6 +163,22 @@ public class TableBuilder {
}
/**
+ * Adds the Indexes to the new table.
+ */
+ public TableBuilder addIndexes(Collection<? extends IndexBuilder> indexes) {
+ if(indexes != null) {
+ for(IndexBuilder col : indexes) {
+ addIndex(col);
+ }
+ }
+ return this;
+ }
+
+ public List<IndexBuilder> getIndexes() {
+ return _indexes;
+ }
+
+ /**
* Sets whether or not subsequently added columns will have their names
* automatically escaped
*/
@@ -202,35 +226,16 @@ public class TableBuilder {
return this;
}
+ public Map<String,PropertyMap.Property> getProperties() {
+ return _props;
+ }
+
/**
* Creates a new Table in the given Database with the currently configured
* attributes.
*/
- public Table toTable(Database db)
- throws IOException
- {
- ((DatabaseImpl)db).createTable(_name, _columns, _indexes);
- Table table = db.getTable(_name);
-
- boolean addedProps = false;
- if(_props != null) {
- table.getProperties().putAll(_props.values());
- addedProps = true;
- }
- for(ColumnBuilder cb : _columns) {
- Map<String,PropertyMap.Property> colProps = cb.getProperties();
- if(colProps != null) {
- table.getColumn(cb.getName()).getProperties().putAll(colProps.values());
- addedProps = true;
- }
- }
-
- // all table and column props are saved together
- if(addedProps) {
- table.getProperties().save();
- }
-
- return table;
+ public Table toTable(Database db) throws IOException {
+ return new TableCreator(((DatabaseImpl)db)).createTable(this);
}
/**
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
index 8950248..998e80a 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
@@ -1590,10 +1590,10 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
}
protected static void writeDefinition(
- DBMutator mutator, ColumnBuilder col, ByteBuffer buffer)
+ TableMutator mutator, ColumnBuilder col, ByteBuffer buffer)
throws IOException
{
- DBMutator.ColumnOffsets colOffsets = mutator.getColumnOffsets();
+ TableMutator.ColumnOffsets colOffsets = mutator.getColumnOffsets();
buffer.put(col.getType().getValue());
buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); //constant magic number
@@ -1671,10 +1671,10 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
}
protected static void writeColUsageMapDefinition(
- DBMutator creator, ColumnBuilder lvalCol, ByteBuffer buffer)
+ TableMutator creator, ColumnBuilder lvalCol, ByteBuffer buffer)
throws IOException
{
- DBMutator.ColumnState colState = creator.getColumnState(lvalCol);
+ TableMutator.ColumnState colState = creator.getColumnState(lvalCol);
buffer.putShort(lvalCol.getColumnNumber());
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java b/src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java
index 374af2b..b16a058 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/DBMutator.java
@@ -18,23 +18,16 @@ package com.healthmarketscience.jackcess.impl;
import java.io.IOException;
import java.nio.charset.Charset;
-import java.util.ArrayList;
-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 database mutation.
+ * Common helper class used to maintain state during database mutation.
*
* @author James Ahlborn
*/
abstract class DBMutator
{
private final DatabaseImpl _database;
- private ColumnOffsets _colOffsets;
protected DBMutator(DatabaseImpl database) {
_database = database;
@@ -60,75 +53,11 @@ abstract class DBMutator
return getPageChannel().allocateNewPage();
}
- public void setColumnOffsets(
- int fixedOffset, int varOffset, int longVarOffset) {
- if(_colOffsets == null) {
- _colOffsets = new ColumnOffsets();
- }
- _colOffsets.set(fixedOffset, varOffset, longVarOffset);
- }
-
- public ColumnOffsets getColumnOffsets() {
- return _colOffsets;
- }
-
public static int calculateNameLength(String name) {
return (name.length() * JetFormat.TEXT_FIELD_UNIT_SIZE) + 2;
}
- protected void validateColumn(Set<String> colNames, ColumnBuilder column) {
-
- // FIXME for now, we can't create complex columns
- if(column.getType() == DataType.COMPLEX_TYPE) {
- throw new UnsupportedOperationException(
- "Complex column creation is not yet implemented");
- }
-
- column.validate(getFormat());
- if(!colNames.add(column.getName().toUpperCase())) {
- throw new IllegalArgumentException("duplicate column name: " +
- column.getName());
- }
-
- setColumnSortOrder(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())) {
- throw new IllegalArgumentException(
- "Can have at most one AutoNumber column of type " + column.getType() +
- " per table");
- }
- }
-
- private void setColumnSortOrder(ColumnBuilder column) {
- // set the sort order to the db default (if unspecified)
- if(column.getType().isTextual() && (column.getTextSortOrder() == null)) {
- column.setTextSortOrder(getDbSortOrder());
- }
- }
-
- private ColumnImpl.SortOrder getDbSortOrder() {
+ protected ColumnImpl.SortOrder getDbSortOrder() {
try {
return _database.getDefaultSortOrder();
} catch(IOException e) {
@@ -136,140 +65,4 @@ abstract class DBMutator
}
return null;
}
-
- abstract String getTableName();
-
- public abstract int getTdefPageNumber();
-
- abstract short getColumnNumber(String colName);
-
- public abstract ColumnState getColumnState(ColumnBuilder col);
-
- public abstract IndexDataState getIndexDataState(IndexBuilder idx);
-
- /**
- * Maintains additional state used during column writing.
- * @usage _advanced_class_
- */
- static final class ColumnOffsets
- {
- private short _fixedOffset;
- private short _varOffset;
- private short _longVarOffset;
-
- public void set(int fixedOffset, int varOffset, int longVarOffset) {
- _fixedOffset = (short)fixedOffset;
- _varOffset = (short)varOffset;
- _longVarOffset = (short)longVarOffset;
- }
-
- public short getNextVariableOffset(ColumnBuilder col) {
- if(!col.getType().isLongValue()) {
- return _varOffset++;
- }
- return _longVarOffset++;
- }
-
- public short getNextFixedOffset(ColumnBuilder col) {
- short offset = _fixedOffset;
- _fixedOffset += col.getType().getFixedSize(col.getLength());
- return offset;
- }
- }
-
- /**
- * 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 byte getUmapFreeRowNumber() {
- return _umapFreeRowNumber;
- }
-
- 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
- {
- private final List<IndexBuilder> _indexes = new ArrayList<IndexBuilder>();
- private int _indexDataNumber;
- private byte _umapRowNumber;
- private int _umapPageNumber;
- private int _rootPageNumber;
-
- 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 addIndex(IndexBuilder idx) {
- _indexes.add(idx);
- }
-
- public int getIndexDataNumber() {
- return _indexDataNumber;
- }
-
- public void setIndexDataNumber(int newIndexDataNumber) {
- _indexDataNumber = newIndexDataNumber;
- }
-
- public byte getUmapRowNumber() {
- return _umapRowNumber;
- }
-
- public void setUmapRowNumber(byte newUmapRowNumber) {
- _umapRowNumber = newUmapRowNumber;
- }
-
- public int getUmapPageNumber() {
- return _umapPageNumber;
- }
-
- public void setUmapPageNumber(int newUmapPageNumber) {
- _umapPageNumber = newUmapPageNumber;
- }
-
- public int getRootPageNumber() {
- return _rootPageNumber;
- }
-
- public void setRootPageNumber(int newRootPageNumber) {
- _rootPageNumber = newRootPageNumber;
- }
- }
-
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
index 99500dd..26cdc59 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
@@ -60,6 +60,7 @@ import com.healthmarketscience.jackcess.Relationship;
import com.healthmarketscience.jackcess.Row;
import com.healthmarketscience.jackcess.RuntimeIOException;
import com.healthmarketscience.jackcess.Table;
+import com.healthmarketscience.jackcess.TableBuilder;
import com.healthmarketscience.jackcess.TableMetaData;
import com.healthmarketscience.jackcess.impl.query.QueryImpl;
import com.healthmarketscience.jackcess.query.Query;
@@ -1035,12 +1036,10 @@ public class DatabaseImpl implements Database
List<IndexBuilder> indexes)
throws IOException
{
- if(lookupTable(name) != null) {
- throw new IllegalArgumentException(withErrorContext(
- "Cannot create table with name of existing table '" + name + "'"));
- }
-
- new TableCreator(this, name, columns, indexes).createTable();
+ new TableBuilder(name)
+ .addColumns(columns)
+ .addIndexes(indexes)
+ .toTable(this);
}
public void createLinkedTable(String name, String linkedDbName,
@@ -1582,6 +1581,15 @@ public class DatabaseImpl implements Database
}
_pageChannel.close();
}
+
+ public void validateNewTableName(String name) throws IOException {
+ if(lookupTable(name) != null) {
+ throw new IllegalArgumentException(withErrorContext(
+ "Cannot create table with name of existing table '" + name + "'"));
+ }
+
+ validateIdentifierName(name, getFormat().MAX_TABLE_NAME_LENGTH, "table");
+ }
/**
* Validates an identifier name.
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
index ea6708d..a55259e 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java
@@ -492,7 +492,7 @@ public class IndexData {
* @param idxCount num indexes to write
*/
protected static void writeRowCountDefinitions(
- DBMutator creator, ByteBuffer buffer, int idxCount)
+ TableMutator creator, ByteBuffer buffer, int idxCount)
{
// index row counts (empty data)
ByteUtil.forward(buffer, (idxCount *
@@ -510,7 +510,7 @@ public class IndexData {
{
ByteBuffer rootPageBuffer = createRootPageBuffer(creator);
- for(DBMutator.IndexDataState idxDataState : creator.getIndexDataStates()) {
+ for(TableMutator.IndexDataState idxDataState : creator.getIndexDataStates()) {
writeDefinition(creator, buffer, idxDataState, rootPageBuffer);
}
}
@@ -521,8 +521,8 @@ public class IndexData {
* @param buffer Buffer to write to
*/
protected static void writeDefinition(
- DBMutator creator, ByteBuffer buffer,
- DBMutator.IndexDataState idxDataState, ByteBuffer rootPageBuffer)
+ TableMutator creator, ByteBuffer buffer,
+ TableMutator.IndexDataState idxDataState, ByteBuffer rootPageBuffer)
throws IOException
{
if(rootPageBuffer == null) {
@@ -573,7 +573,7 @@ public class IndexData {
ByteUtil.forward(buffer, 5); // unknown
}
- private static ByteBuffer createRootPageBuffer(DBMutator creator)
+ private static ByteBuffer createRootPageBuffer(TableMutator creator)
throws IOException
{
ByteBuffer rootPageBuffer = creator.getPageChannel().createPageBuffer();
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
index 43b6c44..833ee98 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java
@@ -351,10 +351,10 @@ public class IndexImpl implements Index, Comparable<IndexImpl>
}
protected static void writeDefinition(
- DBMutator mutator, IndexBuilder idx, ByteBuffer buffer)
+ TableMutator mutator, IndexBuilder idx, ByteBuffer buffer)
throws IOException
{
- DBMutator.IndexDataState idxDataState = mutator.getIndexDataState(idx);
+ TableMutator.IndexDataState idxDataState = mutator.getIndexDataState(idx);
// write logical index information
buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); // seemingly constant magic value which matches the table def
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipImpl.java
index 8775448..4563992 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipImpl.java
@@ -33,20 +33,20 @@ public class RelationshipImpl implements Relationship
{
/** flag indicating one-to-one relationship */
- private static final int ONE_TO_ONE_FLAG = 0x00000001;
+ public static final int ONE_TO_ONE_FLAG = 0x00000001;
/** flag indicating no referential integrity */
- private static final int NO_REFERENTIAL_INTEGRITY_FLAG = 0x00000002;
+ public static final int NO_REFERENTIAL_INTEGRITY_FLAG = 0x00000002;
/** flag indicating cascading updates (requires referential integrity) */
- private static final int CASCADE_UPDATES_FLAG = 0x00000100;
+ public static final int CASCADE_UPDATES_FLAG = 0x00000100;
/** flag indicating cascading deletes (requires referential integrity) */
- private static final int CASCADE_DELETES_FLAG = 0x00001000;
+ public static final int CASCADE_DELETES_FLAG = 0x00001000;
/** flag indicating cascading null on delete (requires referential
integrity) */
- private static final int CASCADE_NULL_FLAG = 0x00002000;
+ public static final int CASCADE_NULL_FLAG = 0x00002000;
/** flag indicating left outer join */
- private static final int LEFT_OUTER_JOIN_FLAG = 0x01000000;
+ public static final int LEFT_OUTER_JOIN_FLAG = 0x01000000;
/** flag indicating right outer join */
- private static final int RIGHT_OUTER_JOIN_FLAG = 0x02000000;
+ public static final int RIGHT_OUTER_JOIN_FLAG = 0x02000000;
/** the name of this relationship */
private final String _name;
@@ -127,6 +127,15 @@ public class RelationshipImpl implements Relationship
public boolean isRightOuterJoin() {
return hasFlag(RIGHT_OUTER_JOIN_FLAG);
}
+
+ public JoinType getJoinType() {
+ if(isLeftOuterJoin()) {
+ return JoinType.LEFT_OUTER;
+ } else if(isRightOuterJoin()) {
+ return JoinType.RIGHT_OUTER;
+ }
+ return JoinType.INNER;
+ }
private boolean hasFlag(int flagMask) {
return((getFlags() & flagMask) != 0);
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
index 34765e8..5fb3337 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
@@ -29,6 +29,8 @@ import java.util.Set;
import com.healthmarketscience.jackcess.ColumnBuilder;
import com.healthmarketscience.jackcess.DataType;
import com.healthmarketscience.jackcess.IndexBuilder;
+import com.healthmarketscience.jackcess.PropertyMap;
+import com.healthmarketscience.jackcess.TableBuilder;
/**
* Helper class used to maintain state during table creation.
@@ -36,11 +38,11 @@ import com.healthmarketscience.jackcess.IndexBuilder;
* @author James Ahlborn
* @usage _advanced_class_
*/
-class TableCreator extends DBMutator
+public class TableCreator extends TableMutator
{
- private final String _name;
- private final List<ColumnBuilder> _columns;
- private final List<IndexBuilder> _indexes;
+ private String _name;
+ private List<ColumnBuilder> _columns;
+ private List<IndexBuilder> _indexes;
private final List<IndexDataState> _indexDataStates =
new ArrayList<IndexDataState>();
private final Map<ColumnBuilder,ColumnState> _columnStates =
@@ -51,13 +53,8 @@ class TableCreator extends DBMutator
private int _indexCount;
private int _logicalIndexCount;
- public TableCreator(DatabaseImpl database, String name,
- List<ColumnBuilder> columns, List<IndexBuilder> indexes) {
+ public TableCreator(DatabaseImpl database) {
super(database);
- _name = name;
- _columns = columns;
- _indexes = ((indexes != null) ? indexes :
- Collections.<IndexBuilder>emptyList());
}
public String getName() {
@@ -153,7 +150,14 @@ class TableCreator extends DBMutator
* Creates the table in the database.
* @usage _advanced_method_
*/
- public void createTable() throws IOException {
+ public TableImpl createTable(TableBuilder table) throws IOException {
+
+ _name = table.getName();
+ _columns = table.getColumns();
+ _indexes = table.getIndexes();
+ if(_indexes == null) {
+ _indexes = Collections.<IndexBuilder>emptyList();
+ }
validate();
@@ -190,6 +194,31 @@ class TableCreator extends DBMutator
getDatabase().addNewTable(_name, _tdefPageNumber, DatabaseImpl.TYPE_TABLE,
null, null);
+ TableImpl newTable = getDatabase().getTable(_name);
+
+ // add any table properties
+ boolean addedProps = false;
+ Map<String,PropertyMap.Property> props = table.getProperties();
+ if(props != null) {
+ newTable.getProperties().putAll(props.values());
+ addedProps = true;
+ }
+ for(ColumnBuilder cb : _columns) {
+ Map<String,PropertyMap.Property> colProps = cb.getProperties();
+ if(colProps != null) {
+ newTable.getColumn(cb.getName()).getProperties()
+ .putAll(colProps.values());
+ addedProps = true;
+ }
+ }
+
+ // all table and column props are saved together
+ if(addedProps) {
+ newTable.getProperties().save();
+ }
+
+ return newTable;
+
} finally {
getPageChannel().finishWrite();
}
@@ -217,10 +246,9 @@ class TableCreator extends DBMutator
/**
* Validates the new table information before attempting creation.
*/
- private void validate() {
+ private void validate() throws IOException {
- DatabaseImpl.validateIdentifierName(
- _name, getFormat().MAX_TABLE_NAME_LENGTH, "table");
+ getDatabase().validateNewTableName(_name);
if((_columns == null) || _columns.isEmpty()) {
throw new IllegalArgumentException(
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
index cdd1a93..a4dfd6d 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java
@@ -1017,7 +1017,7 @@ public class TableImpl implements Table
private static void writeTableDefinitionBuffer(
ByteBuffer buffer, int tdefPageNumber,
- DBMutator mutator, List<Integer> reservedPages)
+ TableMutator mutator, List<Integer> reservedPages)
throws IOException
{
buffer.rewind();
@@ -1099,10 +1099,10 @@ public class TableImpl implements Table
}
/**
- * Writes a column defined by the given TableMutator to this table.
+ * Writes a column defined by the given TableUpdater to this table.
* @usage _advanced_method_
*/
- protected ColumnImpl mutateAddColumn(TableMutator mutator) throws IOException
+ protected ColumnImpl mutateAddColumn(TableUpdater mutator) throws IOException
{
ColumnBuilder column = mutator.getColumn();
JetFormat format = mutator.getFormat();
@@ -1183,7 +1183,7 @@ public class TableImpl implements Table
// allocate usage maps for the long value col
Map.Entry<Integer,Integer> umapInfo = addUsageMaps(2, null);
System.out.println("FOO created umap " + umapInfo);
- DBMutator.ColumnState colState = mutator.getColumnState(column);
+ TableMutator.ColumnState colState = mutator.getColumnState(column);
colState.setUmapPageNumber(umapInfo.getKey());
byte rowNum = umapInfo.getValue().byteValue();
colState.setUmapOwnedRowNumber(rowNum);
@@ -1281,10 +1281,10 @@ public class TableImpl implements Table
}
/**
- * Writes a index defined by the given TableMutator to this table.
+ * Writes a index defined by the given TableUpdater to this table.
* @usage _advanced_method_
*/
- protected IndexData mutateAddIndexData(TableMutator mutator) throws IOException
+ protected IndexData mutateAddIndexData(TableUpdater mutator) throws IOException
{
IndexBuilder index = mutator.getIndex();
JetFormat format = mutator.getFormat();
@@ -1326,7 +1326,7 @@ public class TableImpl implements Table
format.SIZE_INDEX_COLUMN_BLOCK));
// allocate usage maps and root page
- DBMutator.IndexDataState idxDataState = mutator.getIndexDataState(index);
+ TableMutator.IndexDataState idxDataState = mutator.getIndexDataState(index);
int rootPageNumber = getPageChannel().allocateNewPage();
Map.Entry<Integer,Integer> umapInfo = addUsageMaps(1, rootPageNumber);
System.out.println("FOO created umap " + umapInfo);
@@ -1404,10 +1404,10 @@ public class TableImpl implements Table
}
/**
- * Writes a index defined by the given TableMutator to this table.
+ * Writes a index defined by the given TableUpdater to this table.
* @usage _advanced_method_
*/
- protected IndexImpl mutateAddIndex(TableMutator mutator) throws IOException
+ protected IndexImpl mutateAddIndex(TableUpdater mutator) throws IOException
{
IndexBuilder index = mutator.getIndex();
JetFormat format = mutator.getFormat();
@@ -1490,7 +1490,7 @@ public class TableImpl implements Table
return newIdx;
}
- private void validateTableDefUpdate(TableMutator mutator, ByteBuffer tableBuffer)
+ private void validateTableDefUpdate(TableUpdater mutator, ByteBuffer tableBuffer)
throws IOException
{
if(!mutator.validateUpdatedTdef(tableBuffer)) {
@@ -1522,7 +1522,7 @@ public class TableImpl implements Table
}
private ByteBuffer loadCompleteTableDefinitionBufferForUpdate(
- TableMutator mutator)
+ TableUpdater mutator)
throws IOException
{
// load complete table definition
@@ -1750,7 +1750,7 @@ public class TableImpl implements Table
// index umap
int indexIdx = i - 2;
- DBMutator.IndexDataState idxDataState =
+ TableMutator.IndexDataState idxDataState =
creator.getIndexDataStates().get(indexIdx);
// allocate root page for the index
@@ -1774,7 +1774,7 @@ public class TableImpl implements Table
lvalColIdx /= 2;
ColumnBuilder lvalCol = lvalCols.get(lvalColIdx);
- DBMutator.ColumnState colState =
+ TableMutator.ColumnState colState =
creator.getColumnState(lvalCol);
umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java
index 8c9ba28..494bfb7 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java
@@ -16,11 +16,7 @@ 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;
@@ -29,253 +25,215 @@ import com.healthmarketscience.jackcess.DataType;
import com.healthmarketscience.jackcess.IndexBuilder;
/**
- * Helper class used to maintain state during table mutation.
+ * Common helper class used to maintain state during table mutation.
*
* @author James Ahlborn
* @usage _advanced_class_
*/
-public class TableMutator extends DBMutator
+public abstract 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);
- private ColumnState _colState;
- private IndexDataState _idxDataState;
-
- public TableMutator(TableImpl table) {
- super(table.getDatabase());
- _table = table;
- }
+ private ColumnOffsets _colOffsets;
- public ColumnBuilder getColumn() {
- return _column;
+ protected TableMutator(DatabaseImpl database) {
+ super(database);
}
- public IndexBuilder getIndex() {
- return _index;
+ public void setColumnOffsets(
+ int fixedOffset, int varOffset, int longVarOffset) {
+ if(_colOffsets == null) {
+ _colOffsets = new ColumnOffsets();
+ }
+ _colOffsets.set(fixedOffset, varOffset, longVarOffset);
}
- @Override
- String getTableName() {
- return _table.getName();
- }
-
- @Override
- public int getTdefPageNumber() {
- return _table.getTableDefPageNumber();
+ public ColumnOffsets getColumnOffsets() {
+ return _colOffsets;
}
- @Override
- short getColumnNumber(String colName) {
- for(ColumnImpl col : _table.getColumns()) {
- if(col.getName().equalsIgnoreCase(colName)) {
- return col.getColumnNumber();
+ protected void validateColumn(Set<String> colNames, ColumnBuilder column) {
+
+ // FIXME for now, we can't create complex columns
+ if(column.getType() == DataType.COMPLEX_TYPE) {
+ throw new UnsupportedOperationException(
+ "Complex column creation is not yet implemented");
+ }
+
+ column.validate(getFormat());
+ if(!colNames.add(column.getName().toUpperCase())) {
+ throw new IllegalArgumentException("duplicate column name: " +
+ column.getName());
}
- }
- return IndexData.COLUMN_UNUSED;
- }
- @Override
- public ColumnState getColumnState(ColumnBuilder col) {
- return ((col == _column) ? _colState : null);
+ setColumnSortOrder(column);
}
- @Override
- public IndexDataState getIndexDataState(IndexBuilder idx) {
- return ((idx == _index) ? _idxDataState : null);
+ 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;
+ }
}
- int getAddedTdefLen() {
- return _addedTdefLen;
+ protected static void validateAutoNumberColumn(Set<DataType> autoTypes,
+ ColumnBuilder column)
+ {
+ if(!column.getType().isMultipleAutoNumberAllowed() &&
+ !autoTypes.add(column.getType())) {
+ throw new IllegalArgumentException(
+ "Can have at most one AutoNumber column of type " + column.getType() +
+ " per table");
+ }
}
- void addTdefLen(int add) {
- _addedTdefLen += add;
+ private void setColumnSortOrder(ColumnBuilder column) {
+ // set the sort order to the db default (if unspecified)
+ if(column.getType().isTextual() && (column.getTextSortOrder() == null)) {
+ column.setTextSortOrder(getDbSortOrder());
+ }
}
- void setOrigTdefLen(int len) {
- _origTdefLen = len;
- }
+ abstract String getTableName();
- List<Integer> getNextPages() {
- return _nextPages;
- }
+ public abstract int getTdefPageNumber();
- void resetTdefInfo() {
- _addedTdefLen = 0;
- _origTdefLen = 0;
- _nextPages.clear();
- }
+ abstract short getColumnNumber(String colName);
+
+ public abstract ColumnState getColumnState(ColumnBuilder col);
- public ColumnImpl addColumn(ColumnBuilder column) throws IOException {
+ public abstract IndexDataState getIndexDataState(IndexBuilder idx);
- _column = column;
+ /**
+ * Maintains additional state used during column writing.
+ * @usage _advanced_class_
+ */
+ static final class ColumnOffsets
+ {
+ private short _fixedOffset;
+ private short _varOffset;
+ private short _longVarOffset;
- validateAddColumn();
+ public void set(int fixedOffset, int varOffset, int longVarOffset) {
+ _fixedOffset = (short)fixedOffset;
+ _varOffset = (short)varOffset;
+ _longVarOffset = (short)longVarOffset;
+ }
- // assign column number and do some assorted column bookkeeping
- short columnNumber = (short)_table.getMaxColumnCount();
- _column.setColumnNumber(columnNumber);
- if(_column.getType().isLongValue()) {
- _colState = new ColumnState();
+ public short getNextVariableOffset(ColumnBuilder col) {
+ if(!col.getType().isLongValue()) {
+ return _varOffset++;
+ }
+ return _longVarOffset++;
}
- getPageChannel().startExclusiveWrite();
- try {
-
- return _table.mutateAddColumn(this);
-
- } finally {
- getPageChannel().finishWrite();
+ public short getNextFixedOffset(ColumnBuilder col) {
+ short offset = _fixedOffset;
+ _fixedOffset += col.getType().getFixedSize(col.getLength());
+ return offset;
}
}
- public IndexImpl addIndex(IndexBuilder index) throws IOException {
-
- _index = index;
-
- validateAddIndex();
-
- // assign index number and do some assorted index bookkeeping
- int indexNumber = _table.getLogicalIndexCount();
- _index.setIndexNumber(indexNumber);
-
- // find backing index state
- findIndexDataState();
-
- getPageChannel().startExclusiveWrite();
- try {
-
- if(_idxDataState.getIndexDataNumber() == _table.getIndexCount()) {
- // we need a new backing index data
- _table.mutateAddIndexData(this);
-
- // we need to modify the table def again when adding the Index, so reset
- resetTdefInfo();
- }
-
- return _table.mutateAddIndex(this);
+ /**
+ * 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;
- } finally {
- getPageChannel().finishWrite();
+ public byte getUmapOwnedRowNumber() {
+ return _umapOwnedRowNumber;
}
- }
- boolean validateUpdatedTdef(ByteBuffer tableBuffer) {
- // sanity check the updates
- return((_origTdefLen + _addedTdefLen) == tableBuffer.limit());
- }
+ public void setUmapOwnedRowNumber(byte newUmapOwnedRowNumber) {
+ _umapOwnedRowNumber = newUmapOwnedRowNumber;
+ }
- private void validateAddColumn() {
+ public byte getUmapFreeRowNumber() {
+ return _umapFreeRowNumber;
+ }
- if(_column == null) {
- throw new IllegalArgumentException("Cannot add column with no column");
+ public void setUmapFreeRowNumber(byte newUmapFreeRowNumber) {
+ _umapFreeRowNumber = newUmapFreeRowNumber;
}
- if((_table.getColumnCount() + 1) > getFormat().MAX_COLUMNS_PER_TABLE) {
- throw new IllegalArgumentException(
- "Cannot add column to table with " +
- getFormat().MAX_COLUMNS_PER_TABLE + " columns");
+
+ public int getUmapPageNumber() {
+ return _umapPageNumber;
}
-
- Set<String> colNames = getColumnNames();
- // next, validate the column definition
- validateColumn(colNames, _column);
-
- if(_column.isAutoNumber()) {
- // for most autonumber types, we can only have one of each type
- Set<DataType> autoTypes = EnumSet.noneOf(DataType.class);
- for(ColumnImpl column : _table.getAutoNumberColumns()) {
- autoTypes.add(column.getType());
- }
- validateAutoNumberColumn(autoTypes, _column);
+ public void setUmapPageNumber(int newUmapPageNumber) {
+ _umapPageNumber = newUmapPageNumber;
}
}
- private void validateAddIndex() {
-
- if(_index == null) {
- throw new IllegalArgumentException("Cannot add index with no index");
+ /**
+ * Maintains additional state used during index data creation.
+ * @usage _advanced_class_
+ */
+ static final class IndexDataState
+ {
+ private final List<IndexBuilder> _indexes = new ArrayList<IndexBuilder>();
+ private int _indexDataNumber;
+ private byte _umapRowNumber;
+ private int _umapPageNumber;
+ private int _rootPageNumber;
+
+ public IndexBuilder getFirstIndex() {
+ // all indexes which have the same backing IndexDataState will have
+ // equivalent columns and flags.
+ return _indexes.get(0);
}
- if((_table.getLogicalIndexCount() + 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());
+ public List<IndexBuilder> getIndexes() {
+ return _indexes;
}
- 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;
- }
+ public void addIndex(IndexBuilder idx) {
+ _indexes.add(idx);
}
- return idxNames;
- }
- private void findIndexDataState() {
+ public int getIndexDataNumber() {
+ return _indexDataNumber;
+ }
- _idxDataState = new IndexDataState();
- _idxDataState.addIndex(_index);
-
- // search for an existing index which matches the given index (in terms of
- // the backing data)
- for(IndexData idxData : _table.getIndexDatas()) {
- if(sameIndexData(_index, idxData)) {
- _idxDataState.setIndexDataNumber(idxData.getIndexDataNumber());
- return;
- }
+ public void setIndexDataNumber(int newIndexDataNumber) {
+ _indexDataNumber = newIndexDataNumber;
}
- // no matches found, need new index data state
- _idxDataState.setIndexDataNumber(_table.getIndexCount());
- }
+ public byte getUmapRowNumber() {
+ return _umapRowNumber;
+ }
- private static boolean sameIndexData(IndexBuilder idx1, IndexData idx2) {
- // index data can be combined if flags match and columns (and col flags)
- // match
- if(idx1.getFlags() != idx2.getIndexFlags()) {
- return false;
+ public void setUmapRowNumber(byte newUmapRowNumber) {
+ _umapRowNumber = newUmapRowNumber;
}
- if(idx1.getColumns().size() != idx2.getColumns().size()) {
- return false;
+ public int getUmapPageNumber() {
+ return _umapPageNumber;
}
-
- for(int i = 0; i < idx1.getColumns().size(); ++i) {
- IndexBuilder.Column col1 = idx1.getColumns().get(i);
- IndexData.ColumnDescriptor col2 = idx2.getColumns().get(i);
- if(!sameIndexData(col1, col2)) {
- return false;
- }
+ public void setUmapPageNumber(int newUmapPageNumber) {
+ _umapPageNumber = newUmapPageNumber;
}
- return true;
- }
+ public int getRootPageNumber() {
+ return _rootPageNumber;
+ }
- private static boolean sameIndexData(
- IndexBuilder.Column col1, IndexData.ColumnDescriptor col2) {
- return (col1.getName().equals(col2.getName()) &&
- (col1.getFlags() == col2.getFlags()));
- }
+ public void setRootPageNumber(int newRootPageNumber) {
+ _rootPageNumber = newRootPageNumber;
+ }
+ }
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java
new file mode 100644
index 0000000..7ed52f5
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java
@@ -0,0 +1,281 @@
+/*
+Copyright (c) 2016 James Ahlborn
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+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.
+ *
+ * @author James Ahlborn
+ * @usage _advanced_class_
+ */
+public class TableUpdater extends TableMutator
+{
+ private final TableImpl _table;
+
+ private ColumnBuilder _column;
+ private IndexBuilder _index;
+ private int _origTdefLen;
+ private int _addedTdefLen;
+ private List<Integer> _nextPages = new ArrayList<Integer>(1);
+ private ColumnState _colState;
+ private IndexDataState _idxDataState;
+
+ public TableUpdater(TableImpl table) {
+ super(table.getDatabase());
+ _table = table;
+ }
+
+ public ColumnBuilder getColumn() {
+ return _column;
+ }
+
+ public IndexBuilder getIndex() {
+ return _index;
+ }
+
+ @Override
+ String getTableName() {
+ return _table.getName();
+ }
+
+ @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;
+ }
+
+ @Override
+ public ColumnState getColumnState(ColumnBuilder col) {
+ return ((col == _column) ? _colState : null);
+ }
+
+ @Override
+ public IndexDataState getIndexDataState(IndexBuilder idx) {
+ return ((idx == _index) ? _idxDataState : null);
+ }
+
+ int getAddedTdefLen() {
+ return _addedTdefLen;
+ }
+
+ void addTdefLen(int add) {
+ _addedTdefLen += add;
+ }
+
+ void setOrigTdefLen(int len) {
+ _origTdefLen = len;
+ }
+
+ List<Integer> getNextPages() {
+ return _nextPages;
+ }
+
+ void resetTdefInfo() {
+ _addedTdefLen = 0;
+ _origTdefLen = 0;
+ _nextPages.clear();
+ }
+
+ public ColumnImpl addColumn(ColumnBuilder column) throws IOException {
+
+ _column = column;
+
+ validateAddColumn();
+
+ // assign column number and do some assorted column bookkeeping
+ short columnNumber = (short)_table.getMaxColumnCount();
+ _column.setColumnNumber(columnNumber);
+ if(_column.getType().isLongValue()) {
+ _colState = new ColumnState();
+ }
+
+ getPageChannel().startExclusiveWrite();
+ try {
+
+ return _table.mutateAddColumn(this);
+
+ } finally {
+ getPageChannel().finishWrite();
+ }
+ }
+
+ public IndexImpl addIndex(IndexBuilder index) throws IOException {
+
+ _index = index;
+
+ validateAddIndex();
+
+ // assign index number and do some assorted index bookkeeping
+ int indexNumber = _table.getLogicalIndexCount();
+ _index.setIndexNumber(indexNumber);
+
+ // find backing index state
+ findIndexDataState();
+
+ getPageChannel().startExclusiveWrite();
+ try {
+
+ if(_idxDataState.getIndexDataNumber() == _table.getIndexCount()) {
+ // we need a new backing index data
+ _table.mutateAddIndexData(this);
+
+ // we need to modify the table def again when adding the Index, so reset
+ resetTdefInfo();
+ }
+
+ 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");
+ }
+ if((_table.getColumnCount() + 1) > getFormat().MAX_COLUMNS_PER_TABLE) {
+ throw new IllegalArgumentException(
+ "Cannot add column to table with " +
+ getFormat().MAX_COLUMNS_PER_TABLE + " columns");
+ }
+
+ Set<String> colNames = getColumnNames();
+ // next, validate the column definition
+ validateColumn(colNames, _column);
+
+ if(_column.isAutoNumber()) {
+ // for most autonumber types, we can only have one of each type
+ Set<DataType> autoTypes = EnumSet.noneOf(DataType.class);
+ for(ColumnImpl column : _table.getAutoNumberColumns()) {
+ autoTypes.add(column.getType());
+ }
+
+ validateAutoNumberColumn(autoTypes, _column);
+ }
+ }
+
+ private void validateAddIndex() {
+
+ if(_index == null) {
+ throw new IllegalArgumentException("Cannot add index with no index");
+ }
+ if((_table.getLogicalIndexCount() + 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;
+ }
+
+ private void findIndexDataState() {
+
+ _idxDataState = new IndexDataState();
+ _idxDataState.addIndex(_index);
+
+ // search for an existing index which matches the given index (in terms of
+ // the backing data)
+ for(IndexData idxData : _table.getIndexDatas()) {
+ if(sameIndexData(_index, idxData)) {
+ _idxDataState.setIndexDataNumber(idxData.getIndexDataNumber());
+ return;
+ }
+ }
+
+ // no matches found, need new index data state
+ _idxDataState.setIndexDataNumber(_table.getIndexCount());
+ }
+
+ private static boolean sameIndexData(IndexBuilder idx1, IndexData idx2) {
+ // index data can be combined if flags match and columns (and col flags)
+ // match
+ if(idx1.getFlags() != idx2.getIndexFlags()) {
+ 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);
+ IndexData.ColumnDescriptor col2 = idx2.getColumns().get(i);
+
+ if(!sameIndexData(col1, col2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean sameIndexData(
+ IndexBuilder.Column col1, IndexData.ColumnDescriptor col2) {
+ return (col1.getName().equals(col2.getName()) &&
+ (col1.getFlags() == col2.getFlags()));
+ }
+}