* @param columns List of Columns to write definitions for
*/
protected static void writeDefinitions(
- ByteBuffer buffer, List<Column> columns, JetFormat format,
- Charset charset)
+ TableCreator creator, ByteBuffer buffer)
throws IOException
{
+ List<Column> columns = creator.getColumns();
short columnNumber = (short) 0;
short fixedOffset = (short) 0;
short variableOffset = (short) 0;
if(col.getType().isTextual()) {
// this will write 4 bytes (note we don't support writing dbs which
// use the text code page)
- writeSortOrder(buffer, col.getTextSortOrder(), format);
+ writeSortOrder(buffer, col.getTextSortOrder(), creator.getFormat());
} else {
if(col.getType().getHasScalePrecision()) {
buffer.put(col.getPrecision()); // numeric precision
columnNumber++;
if (LOG.isDebugEnabled()) {
LOG.debug("Creating new column def block\n" + ByteUtil.toHexString(
- buffer, position, format.SIZE_COLUMN_DEF_BLOCK));
+ buffer, position, creator.getFormat().SIZE_COLUMN_DEF_BLOCK));
}
}
for (Column col : columns) {
- Table.writeName(buffer, col.getName(), charset);
+ Table.writeName(buffer, col.getName(), creator.getCharset());
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Date;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
List<IndexBuilder> indexes)
throws IOException
{
- validateIdentifierName(name, _format.MAX_TABLE_NAME_LENGTH, "table");
-
- if(getTable(name) != null) {
+ if(lookupTable(name) != null) {
throw new IllegalArgumentException(
"Cannot create table with name of existing table");
}
-
- if(columns.isEmpty()) {
- throw new IllegalArgumentException(
- "Cannot create table with no columns");
- }
- if(columns.size() > _format.MAX_COLUMNS_PER_TABLE) {
- throw new IllegalArgumentException(
- "Cannot create table with more than " +
- _format.MAX_COLUMNS_PER_TABLE + " columns");
- }
-
- Column.SortOrder dbSortOrder = null;
- try {
- dbSortOrder = getDefaultSortOrder();
- } catch(IOException e) {
- // ignored, just use the jet format default
- }
-
- Set<String> colNames = new HashSet<String>();
- // next, validate the column definitions
- for(Column column : columns) {
-
- // 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(_format);
- if(!colNames.add(column.getName().toUpperCase())) {
- throw new IllegalArgumentException("duplicate column name: " +
- column.getName());
- }
- // set the sort order to the db default (if unspecified)
- if(column.getType().isTextual() && (column.getTextSortOrder() == null)) {
- column.setTextSortOrder(dbSortOrder);
- }
- }
-
- List<Column> autoCols = Table.getAutoNumberColumns(columns);
- if(autoCols.size() > 1) {
- // for most autonumber types, we can only have one of each type
- Set<DataType> autoTypes = EnumSet.noneOf(DataType.class);
- for(Column c : autoCols) {
- if(!c.getType().isMultipleAutoNumberAllowed() &&
- !autoTypes.add(c.getType())) {
- throw new IllegalArgumentException(
- "Can have at most one AutoNumber column of type " + c.getType() +
- " per table");
- }
- }
- }
+ new TableCreator(this, name, columns, indexes).createTable();
+ }
- if(indexes == null) {
- indexes = Collections.emptyList();
- }
- if(!indexes.isEmpty()) {
- // now, validate the indexes
- Set<String> idxNames = new HashSet<String>();
- boolean foundPk = false;
- for(IndexBuilder index : indexes) {
- index.validate(colNames);
- 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;
- }
- }
- }
-
- //Write the tdef page to disk.
- int tdefPageNumber = Table.writeTableDefinition(columns, indexes,
- _pageChannel, _format,
- getCharset());
-
+ /**
+ * Adds a newly created table to the relevant internal database structures.
+ */
+ void addNewTable(String name, int tdefPageNumber) throws IOException {
//Add this table to our internal list.
addTable(name, Integer.valueOf(tdefPageNumber));
//Add this table to system tables
addToSystemCatalog(name, tdefPageNumber);
- addToAccessControlEntries(tdefPageNumber);
+ addToAccessControlEntries(tdefPageNumber);
}
/**
/**
* Interface which allows customization of the behavior of the
- * <code>Database<</code> import/copy methods.
+ * <code>Database</code> import/copy methods.
*
* @author James Ahlborn
*/
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import java.util.Map;
* @param indexes List of IndexBuilders to write definitions for
*/
protected static void writeDefinitions(
- ByteBuffer buffer, List<IndexBuilder> indexes, Charset charset)
+ TableCreator creator, ByteBuffer buffer)
throws IOException
{
// write logical index information
- for(IndexBuilder idx : indexes) {
+ for(IndexBuilder idx : creator.getIndexes()) {
+ TableCreator.IndexState idxState = creator.getIndexState(idx);
buffer.putInt(Table.MAGIC_TABLE_NUMBER); // seemingly constant magic value which matches the table def
- buffer.putInt(idx.getIndexNumber()); // index num
- buffer.putInt(idx.getIndexDataNumber()); // index data num
+ buffer.putInt(idxState.getIndexNumber()); // index num
+ buffer.putInt(idxState.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
}
// write index names
- for(IndexBuilder idx : indexes) {
- Table.writeName(buffer, idx.getName(), charset);
+ for(IndexBuilder idx : creator.getIndexes()) {
+ Table.writeName(buffer, idx.getName(), creator.getCharset());
}
}
/** the names and orderings of the indexed columns */
private final List<Column> _columns = new ArrayList<Column>();
- // used by table definition writing code
- private int _indexNumber;
- private int _indexDataNumber;
- private byte _umapRowNumber;
- private int _umapPageNumber;
- private int _rootPageNumber;
-
public IndexBuilder(String name) {
_name = name;
}
}
}
- protected int getIndexNumber() {
- return _indexNumber;
- }
-
- protected void setIndexNumber(int newIndexNumber) {
- _indexNumber = newIndexNumber;
- }
-
- protected int getIndexDataNumber() {
- return _indexDataNumber;
- }
-
- protected void setIndexDataNumber(int newIndexDataNumber) {
- _indexDataNumber = newIndexDataNumber;
- }
-
- protected byte getUmapRowNumber() {
- return _umapRowNumber;
- }
-
- protected void setUmapRowNumber(byte newUmapRowNumber) {
- _umapRowNumber = newUmapRowNumber;
- }
-
- protected int getUmapPageNumber() {
- return _umapPageNumber;
- }
-
- protected void setUmapPageNumber(int newUmapPageNumber) {
- _umapPageNumber = newUmapPageNumber;
- }
-
- protected int getRootPageNumber() {
- return _rootPageNumber;
- }
-
- protected void setRootPageNumber(int newRootPageNumber) {
- _rootPageNumber = newRootPageNumber;
- }
-
/**
* Information about a column in this index (name and ordering).
*/
* @param indexes List of IndexBuilders to write definitions for
*/
protected static void writeRowCountDefinitions(
- ByteBuffer buffer, int indexCount, JetFormat format)
+ TableCreator creator, ByteBuffer buffer)
{
// index row counts (empty data)
- ByteUtil.forward(buffer, (indexCount * format.SIZE_INDEX_DEFINITION));
+ ByteUtil.forward(buffer, (creator.getIndexCount() *
+ creator.getFormat().SIZE_INDEX_DEFINITION));
}
/**
* @param indexes List of IndexBuilders to write definitions for
*/
protected static void writeDefinitions(
- ByteBuffer buffer, List<Column> columns, List<IndexBuilder> indexes,
- int tdefPageNumber, PageChannel pageChannel, JetFormat format)
+ TableCreator creator, ByteBuffer buffer)
throws IOException
{
- ByteBuffer rootPageBuffer = pageChannel.createPageBuffer();
+ ByteBuffer rootPageBuffer = creator.getPageChannel().createPageBuffer();
writeDataPage(rootPageBuffer, SimpleIndexData.NEW_ROOT_DATA_PAGE,
- tdefPageNumber, format);
+ creator.getTdefPageNumber(), creator.getFormat());
- for(IndexBuilder idx : indexes) {
+ for(IndexBuilder idx : creator.getIndexes()) {
buffer.putInt(MAGIC_INDEX_NUMBER); // seemingly constant magic value
// write column information (always MAX_COLUMNS entries)
flags = idxCol.getFlags();
// find actual table column number
- for(Column col : columns) {
+ for(Column col : creator.getColumns()) {
if(col.getName().equalsIgnoreCase(idxCol.getName())) {
columnNumber = col.getColumnNumber();
break;
buffer.put(flags); // column flags (e.g. ordering)
}
- buffer.put(idx.getUmapRowNumber()); // umap row
- ByteUtil.put3ByteInt(buffer, idx.getUmapPageNumber()); // umap page
+ TableCreator.IndexState idxState = creator.getIndexState(idx);
+
+ buffer.put(idxState.getUmapRowNumber()); // umap row
+ ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); // umap page
// write empty root index page
- pageChannel.writePage(rootPageBuffer, idx.getRootPageNumber());
+ creator.getPageChannel().writePage(rootPageBuffer,
+ idxState.getRootPageNumber());
- buffer.putInt(idx.getRootPageNumber());
+ buffer.putInt(idxState.getRootPageNumber());
buffer.putInt(0); // unknown
buffer.put(idx.getFlags()); // index flags (unique, etc.)
ByteUtil.forward(buffer, 5); // unknown
/**
* Returns a new Entry of the correct type for the given data and page type.
*/
- private Entry newEntry(ByteBuffer buffer, int entryLength, boolean isLeaf)
+ private static Entry newEntry(ByteBuffer buffer, int entryLength,
+ boolean isLeaf)
throws IOException
{
if(isLeaf) {
}
/**
- * Writes a new table defined by the given columns and indexes to the
- * database.
- * @return the first page of the new table's definition
+ * Writes a new table defined by the given TableCreator to the database.
* @usage _advanced_method_
*/
- public static int writeTableDefinition(
- List<Column> columns, List<IndexBuilder> indexes,
- PageChannel pageChannel, JetFormat format, Charset charset)
+ protected static void writeTableDefinition(TableCreator creator)
throws IOException
{
- int indexCount = 0;
- int logicalIndexCount = 0;
- if(!indexes.isEmpty()) {
- // sort out index numbers. for now, these values will always match
- // (until we support writing foreign key indexes)
- for(IndexBuilder idx : indexes) {
- idx.setIndexNumber(logicalIndexCount++);
- idx.setIndexDataNumber(indexCount++);
- }
- }
-
- // allocate first table def page
- int tdefPageNumber = pageChannel.allocateNewPage();
-
// first, create the usage map page
- int usageMapPageNumber =
- createUsageMapDefinitionBuffer(indexes, pageChannel, format);
+ createUsageMapDefinitionBuffer(creator);
// next, determine how big the table def will be (in case it will be more
// than one page)
- int idxDataLen = (indexCount * (format.SIZE_INDEX_DEFINITION +
- format.SIZE_INDEX_COLUMN_BLOCK)) +
- (logicalIndexCount * format.SIZE_INDEX_INFO_BLOCK);
+ JetFormat format = creator.getFormat();
+ int idxDataLen = (creator.getIndexCount() *
+ (format.SIZE_INDEX_DEFINITION +
+ format.SIZE_INDEX_COLUMN_BLOCK)) +
+ (creator.getLogicalIndexCount() * format.SIZE_INDEX_INFO_BLOCK);
int totalTableDefSize = format.SIZE_TDEF_HEADER +
- (format.SIZE_COLUMN_DEF_BLOCK * columns.size()) + idxDataLen +
- format.SIZE_TDEF_TRAILER;
+ (format.SIZE_COLUMN_DEF_BLOCK * creator.getColumns().size()) +
+ idxDataLen + format.SIZE_TDEF_TRAILER;
// total up the amount of space used by the column and index names (2
// bytes per char + 2 bytes for the length)
- for(Column col : columns) {
+ for(Column col : creator.getColumns()) {
int nameByteLen = (col.getName().length() *
JetFormat.TEXT_FIELD_UNIT_SIZE);
totalTableDefSize += nameByteLen + 2;
}
- for(IndexBuilder idx : indexes) {
+ for(IndexBuilder idx : creator.getIndexes()) {
int nameByteLen = (idx.getName().length() *
JetFormat.TEXT_FIELD_UNIT_SIZE);
totalTableDefSize += nameByteLen + 2;
// now, create the table definition
- ByteBuffer buffer = pageChannel.createBuffer(Math.max(totalTableDefSize,
- format.PAGE_SIZE));
- writeTableDefinitionHeader(buffer, columns, usageMapPageNumber,
- totalTableDefSize, indexCount,
- logicalIndexCount, format);
+ PageChannel pageChannel = creator.getPageChannel();
+ ByteBuffer buffer = pageChannel .createBuffer(Math.max(totalTableDefSize,
+ format.PAGE_SIZE));
+ writeTableDefinitionHeader(creator, buffer, totalTableDefSize);
- if(indexCount > 0) {
+ if(creator.hasIndexes()) {
// index row counts
- IndexData.writeRowCountDefinitions(buffer, indexCount, format);
+ IndexData.writeRowCountDefinitions(creator, buffer);
}
// column definitions
- Column.writeDefinitions(buffer, columns, format, charset);
+ Column.writeDefinitions(creator, buffer);
- if(indexCount > 0) {
+ if(creator.hasIndexes()) {
// index and index data definitions
- IndexData.writeDefinitions(buffer, columns, indexes, tdefPageNumber,
- pageChannel, format);
- Index.writeDefinitions(buffer, indexes, charset);
+ IndexData.writeDefinitions(creator, buffer);
+ Index.writeDefinitions(creator, buffer);
}
//End of tabledef
buffer.putShort(format.OFFSET_FREE_SPACE,
(short)(buffer.remaining() - 8)); // overwrite page free space
// Write the tdef page to disk.
- pageChannel.writePage(buffer, tdefPageNumber);
+ pageChannel.writePage(buffer, creator.getTdefPageNumber());
} else {
// this is the first page. note, the first page already has the
// page header, so no need to write it here
- nextTdefPageNumber = tdefPageNumber;
+ nextTdefPageNumber = creator.getTdefPageNumber();
} else {
}
}
-
- return tdefPageNumber;
}
/**
* @param columns List of Columns in the table
*/
private static void writeTableDefinitionHeader(
- ByteBuffer buffer, List<Column> columns,
- int usageMapPageNumber, int totalTableDefSize,
- int indexCount, int logicalIndexCount, JetFormat format)
+ TableCreator creator, ByteBuffer buffer, int totalTableDefSize)
throws IOException
{
+ List<Column> columns = creator.getColumns();
+
//Start writing the tdef
writeTablePageHeader(buffer);
buffer.putInt(totalTableDefSize); //Length of table def
buffer.putShort((short) columns.size()); //Max columns a row will have
buffer.putShort(Column.countVariableLength(columns)); //Number of variable columns in table
buffer.putShort((short) columns.size()); //Number of columns in table
- buffer.putInt(logicalIndexCount); //Number of logical indexes in table
- buffer.putInt(indexCount); //Number of indexes in table
+ buffer.putInt(creator.getLogicalIndexCount()); //Number of logical indexes in table
+ buffer.putInt(creator.getIndexCount()); //Number of indexes in table
buffer.put((byte) 0); //Usage map row number
- ByteUtil.put3ByteInt(buffer, usageMapPageNumber); //Usage map page number
+ ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); //Usage map page number
buffer.put((byte) 1); //Free map row number
- ByteUtil.put3ByteInt(buffer, usageMapPageNumber); //Free map page number
+ ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); //Free map page number
if (LOG.isDebugEnabled()) {
int position = buffer.position();
buffer.rewind();
LOG.debug("Creating new table def block:\n" + ByteUtil.toHexString(
- buffer, format.SIZE_TDEF_HEADER));
+ buffer, creator.getFormat().SIZE_TDEF_HEADER));
buffer.position(position);
}
}
* row 0, the "pages with free space" map is in row 1. Index usage maps are
* in subsequent rows.
*/
- private static int createUsageMapDefinitionBuffer(
- List<IndexBuilder> indexes, PageChannel pageChannel, JetFormat format)
+ private static void createUsageMapDefinitionBuffer(TableCreator creator)
throws IOException
{
// 2 table usage maps plus 1 for each index
- int umapNum = 2 + indexes.size();
+ int umapNum = 2 + creator.getIndexCount();
+ JetFormat format = creator.getFormat();
int usageMapRowLength = format.OFFSET_USAGE_MAP_START +
format.USAGE_MAP_TABLE_BYTE_LENGTH;
int freeSpace = format.DATA_PAGE_INITIAL_FREE_SPACE
throw new IOException("FIXME attempting to write too many indexes");
}
- int umapPageNumber = pageChannel.allocateNewPage();
+ int umapPageNumber = creator.getUmapPageNumber();
+ PageChannel pageChannel = creator.getPageChannel();
ByteBuffer rtn = pageChannel.createPageBuffer();
rtn.put(PageTypes.DATA);
rtn.put((byte) 0x1); //Unknown
rowStart -= usageMapRowLength;
}
- if(!indexes.isEmpty()) {
+ if(creator.hasIndexes()) {
- for(int i = 0; i < indexes.size(); ++i) {
- IndexBuilder idx = indexes.get(i);
+ for(int i = 0; i < creator.getIndexes().size(); ++i) {
+ IndexBuilder idx = creator.getIndexes().get(i);
// allocate root page for the index
int rootPageNumber = pageChannel.allocateNewPage();
int umapRowNum = i + 2;
// stash info for later use
- idx.setRootPageNumber(rootPageNumber);
- idx.setUmapRowNumber((byte)umapRowNum);
- idx.setUmapPageNumber(umapPageNumber);
+ TableCreator.IndexState idxState = creator.getIndexState(idx);
+ idxState.setRootPageNumber(rootPageNumber);
+ idxState.setUmapRowNumber((byte)umapRowNum);
+ idxState.setUmapPageNumber(umapPageNumber);
// index map definition, including initial root page
rtn.putShort(getRowStartOffset(umapRowNum, format), (short)rowStart);
rtn.put(rowStart + 5, (byte)1);
rowStart -= usageMapRowLength;
- }
+ }
}
pageChannel.writePage(rtn, umapPageNumber);
-
- return umapPageNumber;
}
/**
--- /dev/null
+/*
+Copyright (c) 2011 James Ahlborn
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA
+*/
+
+package com.healthmarketscience.jackcess;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper class used to maintain state during table creation.
+ *
+ * @author James Ahlborn
+ * @usage _advanced_class_
+ */
+class TableCreator
+{
+ private final Database _database;
+ private final String _name;
+ private final List<Column> _columns;
+ private final List<IndexBuilder> _indexes;
+ private final Map<IndexBuilder,IndexState> _indexStates =
+ new HashMap<IndexBuilder,IndexState>();
+ private int _tdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
+ private int _umapPageNumber = PageChannel.INVALID_PAGE_NUMBER;
+ private int _indexCount;
+ private int _logicalIndexCount;
+
+ public TableCreator(Database database, String name, List<Column> columns,
+ List<IndexBuilder> indexes) {
+ _database = database;
+ _name = name;
+ _columns = columns;
+ _indexes = ((indexes != null) ? indexes :
+ Collections.<IndexBuilder>emptyList());
+ }
+
+ public JetFormat getFormat() {
+ return _database.getFormat();
+ }
+
+ public PageChannel getPageChannel() {
+ return _database.getPageChannel();
+ }
+
+ public Charset getCharset() {
+ return _database.getCharset();
+ }
+
+ public int getTdefPageNumber() {
+ return _tdefPageNumber;
+ }
+
+ public int getUmapPageNumber() {
+ return _umapPageNumber;
+ }
+
+ public List<Column> getColumns() {
+ return _columns;
+ }
+
+ public List<IndexBuilder> getIndexes() {
+ return _indexes;
+ }
+
+ public boolean hasIndexes() {
+ return !_indexes.isEmpty();
+ }
+
+ public int getIndexCount() {
+ return _indexCount;
+ }
+
+ public int getLogicalIndexCount() {
+ return _logicalIndexCount;
+ }
+
+ public IndexState getIndexState(IndexBuilder idx) {
+ return _indexStates.get(idx);
+ }
+
+ public int reservePageNumber() throws IOException {
+ return getPageChannel().allocateNewPage();
+ }
+
+ /**
+ * Creates the table in the database.
+ * @usage _advanced_method_
+ */
+ public void createTable() throws IOException {
+
+ validate();
+
+ if(hasIndexes()) {
+ // sort out index numbers. for now, these values will always match
+ // (until we support writing foreign key indexes)
+ for(IndexBuilder idx : _indexes) {
+ IndexState idxState = new IndexState();
+ idxState.setIndexNumber(_logicalIndexCount++);
+ idxState.setIndexDataNumber(_indexCount++);
+ _indexStates.put(idx, idxState);
+ }
+ }
+
+ // reserve some pages
+ _tdefPageNumber = reservePageNumber();
+ _umapPageNumber = reservePageNumber();
+
+ //Write the tdef page to disk.
+ Table.writeTableDefinition(this);
+
+ // update the database with the new table info
+ _database.addNewTable(_name, _tdefPageNumber);
+ }
+
+ /**
+ * Validates the new table information before attempting creation.
+ */
+ private void validate() {
+
+ Database.validateIdentifierName(
+ _name, getFormat().MAX_TABLE_NAME_LENGTH, "table");
+
+ if((_columns == null) || _columns.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Cannot create table with no columns");
+ }
+ if(_columns.size() > getFormat().MAX_COLUMNS_PER_TABLE) {
+ throw new IllegalArgumentException(
+ "Cannot create table with more than " +
+ getFormat().MAX_COLUMNS_PER_TABLE + " columns");
+ }
+
+ Column.SortOrder dbSortOrder = null;
+ try {
+ dbSortOrder = _database.getDefaultSortOrder();
+ } catch(IOException e) {
+ // ignored, just use the jet format default
+ }
+
+ Set<String> colNames = new HashSet<String>();
+ // next, validate the column definitions
+ for(Column column : _columns) {
+
+ // 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());
+ }
+
+ // set the sort order to the db default (if unspecified)
+ if(column.getType().isTextual() && (column.getTextSortOrder() == null)) {
+ column.setTextSortOrder(dbSortOrder);
+ }
+ }
+
+ List<Column> autoCols = Table.getAutoNumberColumns(_columns);
+ if(autoCols.size() > 1) {
+ // for most autonumber types, we can only have one of each type
+ Set<DataType> autoTypes = EnumSet.noneOf(DataType.class);
+ for(Column c : autoCols) {
+ if(!c.getType().isMultipleAutoNumberAllowed() &&
+ !autoTypes.add(c.getType())) {
+ throw new IllegalArgumentException(
+ "Can have at most one AutoNumber column of type " + c.getType() +
+ " per table");
+ }
+ }
+ }
+
+ if(hasIndexes()) {
+ // now, validate the indexes
+ Set<String> idxNames = new HashSet<String>();
+ boolean foundPk = false;
+ for(IndexBuilder index : _indexes) {
+ index.validate(colNames);
+ 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;
+ }
+ }
+ }
+ }
+
+ /**
+ * Maintains additional state used during index creation.
+ * @usage _advanced_class_
+ */
+ static final class IndexState
+ {
+ private int _indexNumber;
+ private int _indexDataNumber;
+ private byte _umapRowNumber;
+ private int _umapPageNumber;
+ private int _rootPageNumber;
+
+ public int getIndexNumber() {
+ return _indexNumber;
+ }
+
+ public void setIndexNumber(int newIndexNumber) {
+ _indexNumber = newIndexNumber;
+ }
+
+ 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;
+ }
+
+ }
+}