* @param index index for the table which will define traversal order as
* well as enhance certain lookups
*/
- public static Cursor createIndexCursor(TableImpl table, Index index)
+ public static Cursor createIndexCursor(TableImpl table, IndexImpl index)
throws IOException
{
return IndexCursor.createCursor(table, index);
* @param endRow the last row of data for the cursor (inclusive), or
* {@code null} for the last entry
*/
- public static Cursor createIndexCursor(TableImpl table, Index index,
+ public static Cursor createIndexCursor(TableImpl table, IndexImpl index,
Object[] startRow, Object[] endRow)
throws IOException
{
* the last entry
* @param endInclusive whether or not endRow is inclusive or exclusive
*/
- public static Cursor createIndexCursor(TableImpl table, Index index,
+ public static Cursor createIndexCursor(TableImpl table, IndexImpl index,
Object[] startRow,
boolean startInclusive,
Object[] endRow,
* @param rowPattern pattern to be used to find the row
* @return the matching row or {@code null} if a match could not be found.
*/
- public static Map<String,Object> findRow(TableImpl table, Index index,
+ public static Map<String,Object> findRow(TableImpl table, IndexImpl index,
Map<String,?> rowPattern)
throws IOException
{
* desired row
* @return the matching row or {@code null} if a match could not be found.
*/
- public static Object findValue(TableImpl table, Index index, Column column,
+ public static Object findValue(TableImpl table, IndexImpl index, Column column,
Column columnPattern, Object valuePattern)
throws IOException
{
*/
public class CursorBuilder {
/** the table which the cursor will traverse */
- private final Table _table;
+ private final TableImpl _table;
/** optional index to use in traversal */
- private Index _index;
+ private IndexImpl _index;
/** optional start row for an index cursor */
private Object[] _startRow;
/** whether or not start row for an index cursor is inclusive */
private ColumnMatcher _columnMatcher;
public CursorBuilder(Table table) {
- _table = table;
+ _table = (TableImpl)table;
}
/**
* Sets an index to use for the cursor.
*/
public CursorBuilder setIndex(Index index) {
- _index = index;
+ _index = (IndexImpl)index;
return this;
}
*/
private CursorBuilder setIndexByColumns(List<String> searchColumns) {
boolean found = false;
- for(Index index : _table.getIndexes()) {
+ for(IndexImpl index : _table.getIndexes()) {
Collection<IndexData.ColumnDescriptor> indexColumns = index.getColumns();
if(indexColumns.size() != searchColumns.size()) {
{
Cursor cursor = null;
if(_index == null) {
- cursor = Cursor.createCursor((TableImpl)_table);
+ cursor = Cursor.createCursor(_table);
} else {
- cursor = Cursor.createIndexCursor((TableImpl)_table, _index,
+ cursor = Cursor.createIndexCursor(_table, _index,
_startRow, _startRowInclusive,
_endRow, _endRowInclusive);
}
// at this point, only init the index columns
Set<Column> cols = new TreeSet<Column>();
- for(Index idx : _table.getIndexes()) {
- Index.ForeignKeyReference ref = idx.getReference();
+ for(IndexImpl idx : _table.getIndexes()) {
+ IndexImpl.ForeignKeyReference ref = idx.getReference();
if(ref != null) {
// compile an ordered list of all columns in this table which are
// involved in foreign key relationships with other tables
_primaryJoinersDoDel = new ArrayList<Joiner>(1);
_secondaryJoiners = new ArrayList<Joiner>(1);
- for(Index idx : _table.getIndexes()) {
- Index.ForeignKeyReference ref = idx.getReference();
+ for(IndexImpl idx : _table.getIndexes()) {
+ IndexImpl.ForeignKeyReference ref = idx.getReference();
if(ref != null) {
Joiner joiner = Joiner.create(idx);
/*
-Copyright (c) 2005 Health Market Science, Inc.
+Copyright (c) 2013 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 along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
-
-You can contact Health Market Science at info@healthmarketscience.com
-or at the following address:
-
-Health Market Science
-2700 Horizon Drive
-Suite 200
-King of Prussia, PA 19406
*/
package com.healthmarketscience.jackcess;
import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.Collections;
import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
/**
- * Access table (logical) index. Logical indexes are backed for IndexData,
- * where one or more logical indexes could be backed by the same data.
- *
- * @author Tim McCune
+ *
+ * @author James Ahlborn
*/
-public class Index implements Comparable<Index> {
-
- protected static final Log LOG = LogFactory.getLog(Index.class);
-
- /** index type for primary key indexes */
- static final byte PRIMARY_KEY_INDEX_TYPE = (byte)1;
-
- /** index type for foreign key indexes */
- static final byte FOREIGN_KEY_INDEX_TYPE = (byte)2;
-
- /** flag for indicating that updates should cascade in a foreign key index */
- private static final byte CASCADE_UPDATES_FLAG = (byte)1;
- /** flag for indicating that deletes should cascade in a foreign key index */
- private static final byte CASCADE_DELETES_FLAG = (byte)1;
-
- /** index table type for the "primary" table in a foreign key index */
- private static final byte PRIMARY_TABLE_TYPE = (byte)1;
-
- /** indicate an invalid index number for foreign key field */
- private static final int INVALID_INDEX_NUMBER = -1;
-
- /** the actual data backing this index (more than one index may be backed by
- the same data */
- private final IndexData _data;
- /** 0-based index number */
- private final int _indexNumber;
- /** the type of the index */
- private final byte _indexType;
- /** Index name */
- private String _name;
- /** foreign key reference info, if any */
- private final ForeignKeyReference _reference;
-
- protected Index(ByteBuffer tableBuffer, List<IndexData> indexDatas,
- JetFormat format)
- throws IOException
- {
-
- ByteUtil.forward(tableBuffer, format.SKIP_BEFORE_INDEX_SLOT); //Forward past Unknown
- _indexNumber = tableBuffer.getInt();
- int indexDataNumber = tableBuffer.getInt();
-
- // read foreign key reference info
- byte relIndexType = tableBuffer.get();
- int relIndexNumber = tableBuffer.getInt();
- int relTablePageNumber = tableBuffer.getInt();
- byte cascadeUpdatesFlag = tableBuffer.get();
- byte cascadeDeletesFlag = tableBuffer.get();
-
- _indexType = tableBuffer.get();
-
- if((_indexType == FOREIGN_KEY_INDEX_TYPE) &&
- (relIndexNumber != INVALID_INDEX_NUMBER)) {
- _reference = new ForeignKeyReference(
- relIndexType, relIndexNumber, relTablePageNumber,
- (cascadeUpdatesFlag == CASCADE_UPDATES_FLAG),
- (cascadeDeletesFlag == CASCADE_DELETES_FLAG));
- } else {
- _reference = null;
- }
-
- ByteUtil.forward(tableBuffer, format.SKIP_AFTER_INDEX_SLOT); //Skip past Unknown
-
- _data = indexDatas.get(indexDataNumber);
+public abstract class Index
+{
- _data.addIndex(this);
- }
+ public abstract Table getTable();
- public IndexData getIndexData() {
- return _data;
- }
+ public abstract String getName();
- public TableImpl getTable() {
- return getIndexData().getTable();
- }
-
- public JetFormat getFormat() {
- return getTable().getFormat();
- }
+ public abstract boolean isPrimaryKey();
- public PageChannel getPageChannel() {
- return getTable().getPageChannel();
- }
-
- public int getIndexNumber() {
- return _indexNumber;
- }
-
- public byte getIndexFlags() {
- return getIndexData().getIndexFlags();
- }
-
- public int getUniqueEntryCount() {
- return getIndexData().getUniqueEntryCount();
- }
-
- public int getUniqueEntryCountOffset() {
- return getIndexData().getUniqueEntryCountOffset();
- }
-
- public String getName() {
- return _name;
- }
-
- public void setName(String name) {
- _name = name;
- }
-
- public boolean isPrimaryKey() {
- return _indexType == PRIMARY_KEY_INDEX_TYPE;
- }
-
- public boolean isForeignKey() {
- return _indexType == FOREIGN_KEY_INDEX_TYPE;
- }
-
- public ForeignKeyReference getReference() {
- return _reference;
- }
+ public abstract boolean isForeignKey();
/**
- * @return the Index referenced by this Index's ForeignKeyReference (if it
- * has one), otherwise {@code null}.
+ * @return the Columns for this index (unmodifiable)
*/
- public Index getReferencedIndex() throws IOException {
-
- if(_reference == null) {
- return null;
- }
-
- TableImpl refTable = getTable().getDatabase().getTable(
- _reference.getOtherTablePageNumber());
-
- if(refTable == null) {
- throw new IOException("Reference to missing table " +
- _reference.getOtherTablePageNumber());
- }
-
- Index refIndex = null;
- int idxNumber = _reference.getOtherIndexNumber();
- for(Index idx : refTable.getIndexes()) {
- if(idx.getIndexNumber() == idxNumber) {
- refIndex = idx;
- break;
- }
- }
-
- if(refIndex == null) {
- throw new IOException("Reference to missing index " + idxNumber +
- " on table " + refTable.getName());
- }
-
- // finally verify that we found the expected index (should reference this
- // index)
- ForeignKeyReference otherRef = refIndex.getReference();
- if((otherRef == null) ||
- (otherRef.getOtherTablePageNumber() !=
- getTable().getTableDefPageNumber()) ||
- (otherRef.getOtherIndexNumber() != _indexNumber)) {
- throw new IOException("Found unexpected index " + refIndex.getName() +
- " on table " + refTable.getName() +
- " with reference " + otherRef);
- }
-
- return refIndex;
- }
+ public abstract List<? extends ColumnInfo> getColumns();
/**
- * Whether or not {@code null} values are actually recorded in the index.
+ * @return the Index referenced by this Index's ForeignKeyReference (if it
+ * has one), otherwise {@code null}.
*/
- public boolean shouldIgnoreNulls() {
- return getIndexData().shouldIgnoreNulls();
- }
+ public abstract Index getReferencedIndex() throws IOException;
/**
* Whether or not index entries must be unique.
* case <i>will violate</i> the unique constraint</li>
* </ul>
*/
- public boolean isUnique() {
- return getIndexData().isUnique();
- }
-
- /**
- * Returns the Columns for this index (unmodifiable)
- */
- public List<IndexData.ColumnDescriptor> getColumns() {
- return getIndexData().getColumns();
- }
-
- /**
- * Whether or not the complete index state has been read.
- */
- public boolean isInitialized() {
- return getIndexData().isInitialized();
- }
-
- /**
- * Forces initialization of this index (actual parsing of index pages).
- * normally, the index will not be initialized until the entries are
- * actually needed.
- */
- public void initialize() throws IOException {
- getIndexData().initialize();
- }
+ public abstract boolean isUnique();
/**
- * Writes the current index state to the database.
- * <p>
- * Forces index initialization.
+ * Information about a Column in an Index
*/
- public void update() throws IOException {
- getIndexData().update();
- }
+ public interface ColumnInfo {
- /**
- * Adds a row to this index
- * <p>
- * Forces index initialization.
- *
- * @param row Row to add
- * @param rowId rowId of the row to be added
- */
- public void addRow(Object[] row, RowId rowId)
- throws IOException
- {
- getIndexData().addRow(row, rowId);
- }
-
- /**
- * Removes a row from this index
- * <p>
- * Forces index initialization.
- *
- * @param row Row to remove
- * @param rowId rowId of the row to be removed
- */
- public void deleteRow(Object[] row, RowId rowId)
- throws IOException
- {
- getIndexData().deleteRow(row, rowId);
- }
-
- /**
- * Gets a new cursor for this index.
- * <p>
- * Forces index initialization.
- */
- public IndexData.EntryCursor cursor()
- throws IOException
- {
- return cursor(null, true, null, true);
- }
-
- /**
- * Gets a new cursor for this index, narrowed to the range defined by the
- * given startRow and endRow.
- * <p>
- * Forces index initialization.
- *
- * @param startRow the first row of data for the cursor, or {@code null} for
- * the first entry
- * @param startInclusive whether or not startRow is inclusive or exclusive
- * @param endRow the last row of data for the cursor, or {@code null} for
- * the last entry
- * @param endInclusive whether or not endRow is inclusive or exclusive
- */
- public IndexData.EntryCursor cursor(Object[] startRow,
- boolean startInclusive,
- Object[] endRow,
- boolean endInclusive)
- throws IOException
- {
- return getIndexData().cursor(startRow, startInclusive, endRow,
- endInclusive);
- }
+ public Column getColumn();
- /**
- * Constructs an array of values appropriate for this index from the given
- * column values, expected to match the columns for this index.
- * @return the appropriate sparse array of data
- * @throws IllegalArgumentException if the wrong number of values are
- * provided
- */
- public Object[] constructIndexRowFromEntry(Object... values)
- {
- return getIndexData().constructIndexRowFromEntry(values);
- }
-
- /**
- * Constructs an array of values appropriate for this index from the given
- * column value.
- * @return the appropriate sparse array of data or {@code null} if not all
- * columns for this index were provided
- */
- public Object[] constructIndexRow(String colName, Object value)
- {
- return constructIndexRow(Collections.singletonMap(colName, value));
- }
-
- /**
- * Constructs an array of values appropriate for this index from the given
- * column values.
- * @return the appropriate sparse array of data or {@code null} if not all
- * columns for this index were provided
- */
- public Object[] constructIndexRow(Map<String,?> row)
- {
- return getIndexData().constructIndexRow(row);
- }
-
- @Override
- public String toString() {
- StringBuilder rtn = new StringBuilder();
- rtn.append("\tName: (").append(getTable().getName()).append(") ")
- .append(_name);
- rtn.append("\n\tNumber: ").append(_indexNumber);
- rtn.append("\n\tIs Primary Key: ").append(isPrimaryKey());
- rtn.append("\n\tIs Foreign Key: ").append(isForeignKey());
- if(_reference != null) {
- rtn.append("\n\tForeignKeyReference: ").append(_reference);
- }
- rtn.append(_data.toString());
- rtn.append("\n\n");
- return rtn.toString();
- }
-
- public int compareTo(Index other) {
- if (_indexNumber > other.getIndexNumber()) {
- return 1;
- } else if (_indexNumber < other.getIndexNumber()) {
- return -1;
- } else {
- return 0;
- }
- }
+ public boolean isAscending();
- /**
- * Writes the logical index definitions into a table definition buffer.
- * @param buffer Buffer to write to
- * @param indexes List of IndexBuilders to write definitions for
- */
- protected static void writeDefinitions(
- TableCreator creator, ByteBuffer buffer)
- throws IOException
- {
- // write logical index information
- for(IndexBuilder idx : creator.getIndexes()) {
- 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.put((byte)0); // related table type
- buffer.putInt(INVALID_INDEX_NUMBER); // related index num
- buffer.putInt(0); // related table definition page number
- buffer.put((byte)0); // cascade updates flag
- buffer.put((byte)0); // cascade deletes flag
- buffer.put(idx.getType()); // index type flags
- buffer.putInt(0); // unknown
- }
-
- // write index names
- for(IndexBuilder idx : creator.getIndexes()) {
- TableImpl.writeName(buffer, idx.getName(), creator.getCharset());
- }
- }
-
- /**
- * Information about a foreign key reference defined in an index (when
- * referential integrity should be enforced).
- */
- public static class ForeignKeyReference
- {
- private final byte _tableType;
- private final int _otherIndexNumber;
- private final int _otherTablePageNumber;
- private final boolean _cascadeUpdates;
- private final boolean _cascadeDeletes;
+ public int getColumnIndex();
- public ForeignKeyReference(
- byte tableType, int otherIndexNumber, int otherTablePageNumber,
- boolean cascadeUpdates, boolean cascadeDeletes)
- {
- _tableType = tableType;
- _otherIndexNumber = otherIndexNumber;
- _otherTablePageNumber = otherTablePageNumber;
- _cascadeUpdates = cascadeUpdates;
- _cascadeDeletes = cascadeDeletes;
- }
-
- public byte getTableType() {
- return _tableType;
- }
-
- public boolean isPrimaryTable() {
- return(getTableType() == PRIMARY_TABLE_TYPE);
- }
-
- public int getOtherIndexNumber() {
- return _otherIndexNumber;
- }
-
- public int getOtherTablePageNumber() {
- return _otherTablePageNumber;
- }
-
- public boolean isCascadeUpdates() {
- return _cascadeUpdates;
- }
-
- public boolean isCascadeDeletes() {
- return _cascadeDeletes;
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("\n\t\tOther Index Number: ").append(_otherIndexNumber)
- .append("\n\t\tOther Table Page Num: ").append(_otherTablePageNumber)
- .append("\n\t\tIs Primary Table: ").append(isPrimaryTable())
- .append("\n\t\tIs Cascade Updates: ").append(isCascadeUpdates())
- .append("\n\t\tIs Cascade Deletes: ").append(isCascadeDeletes())
- .toString();
- }
+ public String getName();
}
}
}
public boolean isPrimaryKey() {
- return (getType() == Index.PRIMARY_KEY_INDEX_TYPE);
+ return (getType() == IndexImpl.PRIMARY_KEY_INDEX_TYPE);
}
public boolean isUnique() {
* unique).
*/
public IndexBuilder setPrimaryKey() {
- _type = Index.PRIMARY_KEY_INDEX_TYPE;
+ _type = IndexImpl.PRIMARY_KEY_INDEX_TYPE;
return setUnique();
}
private final IndexDirHandler _reverseDirHandler =
new ReverseIndexDirHandler();
/** logical index which this cursor is using */
- private final Index _index;
+ private final IndexImpl _index;
/** Cursor over the entries of the relevant index */
private final IndexData.EntryCursor _entryCursor;
/** column names for the index entry columns */
private Set<String> _indexEntryPattern;
- private IndexCursor(TableImpl table, Index index,
+ private IndexCursor(TableImpl table, IndexImpl index,
IndexData.EntryCursor entryCursor)
throws IOException
{
* @param index index for the table which will define traversal order as
* well as enhance certain lookups
*/
- public static IndexCursor createCursor(TableImpl table, Index index)
+ public static IndexCursor createCursor(TableImpl table, IndexImpl index)
throws IOException
{
return createCursor(table, index, null, null);
* {@code null} for the last entry
*/
public static IndexCursor createCursor(
- TableImpl table, Index index, Object[] startRow, Object[] endRow)
+ TableImpl table, IndexImpl index, Object[] startRow, Object[] endRow)
throws IOException
{
return createCursor(table, index, startRow, true, endRow, true);
* the last entry
* @param endInclusive whether or not endRow is inclusive or exclusive
*/
- public static IndexCursor createCursor(TableImpl table, Index index,
+ public static IndexCursor createCursor(TableImpl table, IndexImpl index,
Object[] startRow,
boolean startInclusive,
Object[] endRow,
return cursor;
}
- public Index getIndex() {
+ public IndexImpl getIndex() {
return _index;
}
* Information about the columns in an index. Also encodes new index
* values.
*/
- public static abstract class ColumnDescriptor
+ public static abstract class ColumnDescriptor implements Index.ColumnInfo
{
private final Column _column;
private final byte _flags;
--- /dev/null
+/*
+Copyright (c) 2005 Health Market Science, Inc.
+
+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
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Access table (logical) index. Logical indexes are backed for IndexData,
+ * where one or more logical indexes could be backed by the same data.
+ *
+ * @author Tim McCune
+ */
+public class IndexImpl extends Index implements Comparable<IndexImpl>
+{
+
+ protected static final Log LOG = LogFactory.getLog(Index.class);
+
+ /** index type for primary key indexes */
+ static final byte PRIMARY_KEY_INDEX_TYPE = (byte)1;
+
+ /** index type for foreign key indexes */
+ static final byte FOREIGN_KEY_INDEX_TYPE = (byte)2;
+
+ /** flag for indicating that updates should cascade in a foreign key index */
+ private static final byte CASCADE_UPDATES_FLAG = (byte)1;
+ /** flag for indicating that deletes should cascade in a foreign key index */
+ private static final byte CASCADE_DELETES_FLAG = (byte)1;
+
+ /** index table type for the "primary" table in a foreign key index */
+ private static final byte PRIMARY_TABLE_TYPE = (byte)1;
+
+ /** indicate an invalid index number for foreign key field */
+ private static final int INVALID_INDEX_NUMBER = -1;
+
+ /** the actual data backing this index (more than one index may be backed by
+ the same data */
+ private final IndexData _data;
+ /** 0-based index number */
+ private final int _indexNumber;
+ /** the type of the index */
+ private final byte _indexType;
+ /** Index name */
+ private String _name;
+ /** foreign key reference info, if any */
+ private final ForeignKeyReference _reference;
+
+ protected IndexImpl(ByteBuffer tableBuffer, List<IndexData> indexDatas,
+ JetFormat format)
+ throws IOException
+ {
+
+ ByteUtil.forward(tableBuffer, format.SKIP_BEFORE_INDEX_SLOT); //Forward past Unknown
+ _indexNumber = tableBuffer.getInt();
+ int indexDataNumber = tableBuffer.getInt();
+
+ // read foreign key reference info
+ byte relIndexType = tableBuffer.get();
+ int relIndexNumber = tableBuffer.getInt();
+ int relTablePageNumber = tableBuffer.getInt();
+ byte cascadeUpdatesFlag = tableBuffer.get();
+ byte cascadeDeletesFlag = tableBuffer.get();
+
+ _indexType = tableBuffer.get();
+
+ if((_indexType == FOREIGN_KEY_INDEX_TYPE) &&
+ (relIndexNumber != INVALID_INDEX_NUMBER)) {
+ _reference = new ForeignKeyReference(
+ relIndexType, relIndexNumber, relTablePageNumber,
+ (cascadeUpdatesFlag == CASCADE_UPDATES_FLAG),
+ (cascadeDeletesFlag == CASCADE_DELETES_FLAG));
+ } else {
+ _reference = null;
+ }
+
+ ByteUtil.forward(tableBuffer, format.SKIP_AFTER_INDEX_SLOT); //Skip past Unknown
+
+ _data = indexDatas.get(indexDataNumber);
+
+ _data.addIndex(this);
+ }
+
+ public IndexData getIndexData() {
+ return _data;
+ }
+
+ @Override
+ public TableImpl getTable() {
+ return getIndexData().getTable();
+ }
+
+ public JetFormat getFormat() {
+ return getTable().getFormat();
+ }
+
+ public PageChannel getPageChannel() {
+ return getTable().getPageChannel();
+ }
+
+ public int getIndexNumber() {
+ return _indexNumber;
+ }
+
+ public byte getIndexFlags() {
+ return getIndexData().getIndexFlags();
+ }
+
+ public int getUniqueEntryCount() {
+ return getIndexData().getUniqueEntryCount();
+ }
+
+ public int getUniqueEntryCountOffset() {
+ return getIndexData().getUniqueEntryCountOffset();
+ }
+
+ @Override
+ public String getName() {
+ return _name;
+ }
+
+ public void setName(String name) {
+ _name = name;
+ }
+
+ @Override
+ public boolean isPrimaryKey() {
+ return _indexType == PRIMARY_KEY_INDEX_TYPE;
+ }
+
+ @Override
+ public boolean isForeignKey() {
+ return _indexType == FOREIGN_KEY_INDEX_TYPE;
+ }
+
+ public ForeignKeyReference getReference() {
+ return _reference;
+ }
+
+ @Override
+ public IndexImpl getReferencedIndex() throws IOException {
+
+ if(_reference == null) {
+ return null;
+ }
+
+ TableImpl refTable = getTable().getDatabase().getTable(
+ _reference.getOtherTablePageNumber());
+
+ if(refTable == null) {
+ throw new IOException("Reference to missing table " +
+ _reference.getOtherTablePageNumber());
+ }
+
+ IndexImpl refIndex = null;
+ int idxNumber = _reference.getOtherIndexNumber();
+ for(IndexImpl idx : refTable.getIndexes()) {
+ if(idx.getIndexNumber() == idxNumber) {
+ refIndex = idx;
+ break;
+ }
+ }
+
+ if(refIndex == null) {
+ throw new IOException("Reference to missing index " + idxNumber +
+ " on table " + refTable.getName());
+ }
+
+ // finally verify that we found the expected index (should reference this
+ // index)
+ ForeignKeyReference otherRef = refIndex.getReference();
+ if((otherRef == null) ||
+ (otherRef.getOtherTablePageNumber() !=
+ getTable().getTableDefPageNumber()) ||
+ (otherRef.getOtherIndexNumber() != _indexNumber)) {
+ throw new IOException("Found unexpected index " + refIndex.getName() +
+ " on table " + refTable.getName() +
+ " with reference " + otherRef);
+ }
+
+ return refIndex;
+ }
+
+ @Override
+ public boolean shouldIgnoreNulls() {
+ return getIndexData().shouldIgnoreNulls();
+ }
+
+ @Override
+ public boolean isUnique() {
+ return getIndexData().isUnique();
+ }
+
+ @Override
+ public List<IndexData.ColumnDescriptor> getColumns() {
+ return getIndexData().getColumns();
+ }
+
+ /**
+ * Whether or not the complete index state has been read.
+ */
+ public boolean isInitialized() {
+ return getIndexData().isInitialized();
+ }
+
+ /**
+ * Forces initialization of this index (actual parsing of index pages).
+ * normally, the index will not be initialized until the entries are
+ * actually needed.
+ */
+ public void initialize() throws IOException {
+ getIndexData().initialize();
+ }
+
+ /**
+ * Writes the current index state to the database.
+ * <p>
+ * Forces index initialization.
+ */
+ public void update() throws IOException {
+ getIndexData().update();
+ }
+
+ /**
+ * Adds a row to this index
+ * <p>
+ * Forces index initialization.
+ *
+ * @param row Row to add
+ * @param rowId rowId of the row to be added
+ */
+ public void addRow(Object[] row, RowId rowId)
+ throws IOException
+ {
+ getIndexData().addRow(row, rowId);
+ }
+
+ /**
+ * Removes a row from this index
+ * <p>
+ * Forces index initialization.
+ *
+ * @param row Row to remove
+ * @param rowId rowId of the row to be removed
+ */
+ public void deleteRow(Object[] row, RowId rowId)
+ throws IOException
+ {
+ getIndexData().deleteRow(row, rowId);
+ }
+
+ /**
+ * Gets a new cursor for this index.
+ * <p>
+ * Forces index initialization.
+ */
+ public IndexData.EntryCursor cursor()
+ throws IOException
+ {
+ return cursor(null, true, null, true);
+ }
+
+ /**
+ * Gets a new cursor for this index, narrowed to the range defined by the
+ * given startRow and endRow.
+ * <p>
+ * Forces index initialization.
+ *
+ * @param startRow the first row of data for the cursor, or {@code null} for
+ * the first entry
+ * @param startInclusive whether or not startRow is inclusive or exclusive
+ * @param endRow the last row of data for the cursor, or {@code null} for
+ * the last entry
+ * @param endInclusive whether or not endRow is inclusive or exclusive
+ */
+ public IndexData.EntryCursor cursor(Object[] startRow,
+ boolean startInclusive,
+ Object[] endRow,
+ boolean endInclusive)
+ throws IOException
+ {
+ return getIndexData().cursor(startRow, startInclusive, endRow,
+ endInclusive);
+ }
+
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column values, expected to match the columns for this index.
+ * @return the appropriate sparse array of data
+ * @throws IllegalArgumentException if the wrong number of values are
+ * provided
+ */
+ public Object[] constructIndexRowFromEntry(Object... values)
+ {
+ return getIndexData().constructIndexRowFromEntry(values);
+ }
+
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column value.
+ * @return the appropriate sparse array of data or {@code null} if not all
+ * columns for this index were provided
+ */
+ public Object[] constructIndexRow(String colName, Object value)
+ {
+ return constructIndexRow(Collections.singletonMap(colName, value));
+ }
+
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column values.
+ * @return the appropriate sparse array of data or {@code null} if not all
+ * columns for this index were provided
+ */
+ public Object[] constructIndexRow(Map<String,?> row)
+ {
+ return getIndexData().constructIndexRow(row);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder rtn = new StringBuilder();
+ rtn.append("\tName: (").append(getTable().getName()).append(") ")
+ .append(_name);
+ rtn.append("\n\tNumber: ").append(_indexNumber);
+ rtn.append("\n\tIs Primary Key: ").append(isPrimaryKey());
+ rtn.append("\n\tIs Foreign Key: ").append(isForeignKey());
+ if(_reference != null) {
+ rtn.append("\n\tForeignKeyReference: ").append(_reference);
+ }
+ rtn.append(_data.toString());
+ rtn.append("\n\n");
+ return rtn.toString();
+ }
+
+ public int compareTo(IndexImpl other) {
+ if (_indexNumber > other.getIndexNumber()) {
+ return 1;
+ } else if (_indexNumber < other.getIndexNumber()) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Writes the logical index definitions into a table definition buffer.
+ * @param buffer Buffer to write to
+ * @param indexes List of IndexBuilders to write definitions for
+ */
+ protected static void writeDefinitions(
+ TableCreator creator, ByteBuffer buffer)
+ throws IOException
+ {
+ // write logical index information
+ for(IndexBuilder idx : creator.getIndexes()) {
+ 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.put((byte)0); // related table type
+ buffer.putInt(INVALID_INDEX_NUMBER); // related index num
+ buffer.putInt(0); // related table definition page number
+ buffer.put((byte)0); // cascade updates flag
+ buffer.put((byte)0); // cascade deletes flag
+ buffer.put(idx.getType()); // index type flags
+ buffer.putInt(0); // unknown
+ }
+
+ // write index names
+ for(IndexBuilder idx : creator.getIndexes()) {
+ TableImpl.writeName(buffer, idx.getName(), creator.getCharset());
+ }
+ }
+
+ /**
+ * Information about a foreign key reference defined in an index (when
+ * referential integrity should be enforced).
+ */
+ public static class ForeignKeyReference
+ {
+ private final byte _tableType;
+ private final int _otherIndexNumber;
+ private final int _otherTablePageNumber;
+ private final boolean _cascadeUpdates;
+ private final boolean _cascadeDeletes;
+
+ public ForeignKeyReference(
+ byte tableType, int otherIndexNumber, int otherTablePageNumber,
+ boolean cascadeUpdates, boolean cascadeDeletes)
+ {
+ _tableType = tableType;
+ _otherIndexNumber = otherIndexNumber;
+ _otherTablePageNumber = otherTablePageNumber;
+ _cascadeUpdates = cascadeUpdates;
+ _cascadeDeletes = cascadeDeletes;
+ }
+
+ public byte getTableType() {
+ return _tableType;
+ }
+
+ public boolean isPrimaryTable() {
+ return(getTableType() == PRIMARY_TABLE_TYPE);
+ }
+
+ public int getOtherIndexNumber() {
+ return _otherIndexNumber;
+ }
+
+ public int getOtherTablePageNumber() {
+ return _otherTablePageNumber;
+ }
+
+ public boolean isCascadeUpdates() {
+ return _cascadeUpdates;
+ }
+
+ public boolean isCascadeDeletes() {
+ return _cascadeDeletes;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("\n\t\tOther Index Number: ").append(_otherIndexNumber)
+ .append("\n\t\tOther Table Page Num: ").append(_otherTablePageNumber)
+ .append("\n\t\tIs Primary Table: ").append(isPrimaryTable())
+ .append("\n\t\tIs Cascade Updates: ").append(isCascadeUpdates())
+ .append("\n\t\tIs Cascade Deletes: ").append(isCascadeDeletes())
+ .toString();
+ }
+ }
+}
*/
public class Joiner
{
- private final Index _fromIndex;
+ private final IndexImpl _fromIndex;
private final List<IndexData.ColumnDescriptor> _fromCols;
private final IndexCursor _toCursor;
private final Object[] _entryValues;
- private Joiner(Index fromIndex, IndexCursor toCursor)
+ private Joiner(IndexImpl fromIndex, IndexCursor toCursor)
{
_fromIndex = fromIndex;
_fromCols = _fromIndex.getColumns();
* @throws IllegalArgumentException if there is no relationship between the
* given tables
*/
- public static Joiner create(Table fromTable, Table toTable)
+ public static Joiner create(TableImpl fromTable, TableImpl toTable)
throws IOException
{
return create(fromTable.getForeignKeyIndex(toTable));
*
* @param fromIndex the index backing one side of a foreign-key relationship
*/
- public static Joiner create(Index fromIndex)
+ public static Joiner create(IndexImpl fromIndex)
throws IOException
{
- Index toIndex = fromIndex.getReferencedIndex();
+ IndexImpl toIndex = fromIndex.getReferencedIndex();
IndexCursor toCursor = IndexCursor.createCursor(
toIndex.getTable(), toIndex);
// text lookups are always case-insensitive
return create(getToTable(), getFromTable());
}
- public Table getFromTable() {
+ public TableImpl getFromTable() {
return getFromIndex().getTable();
}
- public Index getFromIndex() {
+ public IndexImpl getFromIndex() {
return _fromIndex;
}
- public Table getToTable() {
+ public TableImpl getToTable() {
return getToCursor().getTable();
}
- public Index getToIndex() {
+ public IndexImpl getToIndex() {
return getToCursor().getIndex();
}
* @return All of the Indexes on this table (unmodifiable List)
* @usage _intermediate_method_
*/
- public abstract List<Index> getIndexes();
+ public abstract List<? extends Index> getIndexes();
/**
* @return the index with the given name
private List<Column> _autoNumColumns;
/** List of indexes on this table (multiple logical indexes may be backed by
the same index data) */
- private final List<Index> _indexes = new ArrayList<Index>();
+ private final List<IndexImpl> _indexes = new ArrayList<IndexImpl>();
/** List of index datas on this table (the actual backing data for an
index) */
private final List<IndexData> _indexDatas = new ArrayList<IndexData>();
}
@Override
- public List<Index> getIndexes() {
+ public List<IndexImpl> getIndexes() {
return Collections.unmodifiableList(_indexes);
}
@Override
- public Index getIndex(String name) {
- for(Index index : _indexes) {
+ public IndexImpl getIndex(String name) {
+ for(IndexImpl index : _indexes) {
if(index.getName().equalsIgnoreCase(name)) {
return index;
}
}
@Override
- public Index getPrimaryKeyIndex() {
- for(Index index : _indexes) {
+ public IndexImpl getPrimaryKeyIndex() {
+ for(IndexImpl index : _indexes) {
if(index.isPrimaryKey()) {
return index;
}
}
@Override
- public Index getForeignKeyIndex(Table otherTable) {
- for(Index index : _indexes) {
+ public IndexImpl getForeignKeyIndex(Table otherTable) {
+ for(IndexImpl index : _indexes) {
if(index.isForeignKey() && (index.getReference() != null) &&
(index.getReference().getOtherTablePageNumber() ==
((TableImpl)otherTable).getTableDefPageNumber())) {
if(creator.hasIndexes()) {
// index and index data definitions
IndexData.writeDefinitions(creator, buffer);
- Index.writeDefinitions(creator, buffer);
+ IndexImpl.writeDefinitions(creator, buffer);
}
//End of tabledef
// read logical index info (may be more logical indexes than index datas)
for (int i = 0; i < _logicalIndexCount; i++) {
- _indexes.add(new Index(tableBuffer, _indexDatas, getFormat()));
+ _indexes.add(new IndexImpl(tableBuffer, _indexDatas, getFormat()));
}
// read logical index names