private static final Pattern GUID_PATTERN = Pattern.compile("\\s*[{]([\\p{XDigit}]{8})-([\\p{XDigit}]{4})-([\\p{XDigit}]{4})-([\\p{XDigit}]{4})-([\\p{XDigit}]{12})[}]\\s*");
+ /** owning table */
+ private final Table _table;
/** For text columns, whether or not they are compressed */
private boolean _compressedUnicode = false;
/** Whether or not the column is of variable length */
private byte _scale;
/** Data type */
private DataType _type;
- /** Format that the containing database is in */
- private JetFormat _format;
- /** Used to read in LVAL pages */
- private PageChannel _pageChannel;
/** Maximum column length */
private short _columnLength;
/** 0-based column number */
}
public Column(JetFormat format) {
- _format = format;
+ _table = null;
}
/**
if(!testing) {
throw new IllegalArgumentException();
}
- _format = JetFormat.VERSION_4;
- _pageChannel = new PageChannel(testing);
+ _table = null;
}
/**
* Read a column definition in from a buffer
+ * @param table owning table
* @param buffer Buffer containing column definition
* @param offset Offset in the buffer at which the column definition starts
* @param format Format that the containing database is in
*/
- public Column(ByteBuffer buffer, int offset, PageChannel pageChannel, JetFormat format)
- throws IOException
+ public Column(Table table, ByteBuffer buffer, int offset)
+ throws IOException
{
+ _table = table;
if (LOG.isDebugEnabled()) {
LOG.debug("Column def block:\n" + ByteUtil.toHexString(buffer, offset, 25));
}
- _pageChannel = pageChannel;
- _format = format;
- setType(DataType.fromByte(buffer.get(offset + format.OFFSET_COLUMN_TYPE)));
- _columnNumber = buffer.getShort(offset + format.OFFSET_COLUMN_NUMBER);
- _columnLength = buffer.getShort(offset + format.OFFSET_COLUMN_LENGTH);
+ setType(DataType.fromByte(buffer.get(offset + getFormat().OFFSET_COLUMN_TYPE)));
+ _columnNumber = buffer.getShort(offset + getFormat().OFFSET_COLUMN_NUMBER);
+ _columnLength = buffer.getShort(offset + getFormat().OFFSET_COLUMN_LENGTH);
if (_type.getHasScalePrecision()) {
- _precision = buffer.get(offset + format.OFFSET_COLUMN_PRECISION);
- _scale = buffer.get(offset + format.OFFSET_COLUMN_SCALE);
+ _precision = buffer.get(offset + getFormat().OFFSET_COLUMN_PRECISION);
+ _scale = buffer.get(offset + getFormat().OFFSET_COLUMN_SCALE);
}
- byte flags = buffer.get(offset + format.OFFSET_COLUMN_FLAGS);
+ byte flags = buffer.get(offset + getFormat().OFFSET_COLUMN_FLAGS);
_variableLength = ((flags & FIXED_LEN_FLAG_MASK) == 0);
_autoNumber = ((flags & AUTO_NUMBER_FLAG_MASK) != 0);
_compressedUnicode = ((buffer.get(offset +
- format.OFFSET_COLUMN_COMPRESSED_UNICODE) & 1) == 1);
+ getFormat().OFFSET_COLUMN_COMPRESSED_UNICODE) & 1) == 1);
if(_variableLength) {
- _varLenTableIndex = buffer.getShort(offset + format.OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
+ _varLenTableIndex = buffer.getShort(offset + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
} else {
- _fixedDataOffset = buffer.getShort(offset + format.OFFSET_COLUMN_FIXED_DATA_OFFSET);
+ _fixedDataOffset = buffer.getShort(offset + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
}
}
+
+ public Table getTable() {
+ return _table;
+ }
+
+ public JetFormat getFormat() {
+ return getTable().getFormat();
+ }
+
+ public PageChannel getPageChannel() {
+ return getTable().getPageChannel();
+ }
public String getName() {
return _name;
* @throws IllegalArgumentException if this column definition is invalid.
*/
public void validate(JetFormat format) {
- if(_format != format) {
- throw new IllegalArgumentException("format must be " + format +
- " but is " + _format);
- }
if(getType() == null) {
throw new IllegalArgumentException("must have type");
}
} else {
// long value on other page(s)
- if (lvalDefinition.length != _format.SIZE_LONG_VALUE_DEF) {
- throw new IOException("Expected " + _format.SIZE_LONG_VALUE_DEF +
+ if (lvalDefinition.length != getFormat().SIZE_LONG_VALUE_DEF) {
+ throw new IOException("Expected " + getFormat().SIZE_LONG_VALUE_DEF +
" bytes in long value definition, but found " +
lvalDefinition.length);
}
byte rowNum = def.get();
int pageNum = ByteUtil.get3ByteInt(def, def.position());
- ByteBuffer lvalPage = _pageChannel.createPageBuffer();
+ ByteBuffer lvalPage = getPageChannel().createPageBuffer();
switch (type) {
case LONG_VALUE_TYPE_OTHER_PAGE:
{
- _pageChannel.readPage(lvalPage, pageNum);
+ getPageChannel().readPage(lvalPage, pageNum);
- short rowStart = Table.findRowStart(lvalPage, rowNum, _format);
- short rowEnd = Table.findRowEnd(lvalPage, rowNum, _format);
+ short rowStart = Table.findRowStart(lvalPage, rowNum, getFormat());
+ short rowEnd = Table.findRowEnd(lvalPage, rowNum, getFormat());
if((rowEnd - rowStart) != length) {
throw new IOException("Unexpected lval row length");
int remainingLen = length;
while(remainingLen > 0) {
lvalPage.clear();
- _pageChannel.readPage(lvalPage, pageNum);
+ getPageChannel().readPage(lvalPage, pageNum);
- short rowStart = Table.findRowStart(lvalPage, rowNum, _format);
- short rowEnd = Table.findRowEnd(lvalPage, rowNum, _format);
+ short rowStart = Table.findRowStart(lvalPage, rowNum, getFormat());
+ short rowEnd = Table.findRowEnd(lvalPage, rowNum, getFormat());
// read next page information
lvalPage.position(rowStart);
// determine which type to write
byte type = 0;
- int lvalDefLen = _format.SIZE_LONG_VALUE_DEF;
- if((_format.SIZE_LONG_VALUE_DEF + value.length) <= remainingRowLength) {
+ int lvalDefLen = getFormat().SIZE_LONG_VALUE_DEF;
+ if((getFormat().SIZE_LONG_VALUE_DEF + value.length) <= remainingRowLength) {
type = LONG_VALUE_TYPE_THIS_PAGE;
lvalDefLen += value.length;
- } else if(Table.getRowSpaceUsage(value.length, _format) <=
- _format.MAX_ROW_SIZE)
+ } else if(Table.getRowSpaceUsage(value.length, getFormat()) <=
+ getFormat().MAX_ROW_SIZE)
{
type = LONG_VALUE_TYPE_OTHER_PAGE;
} else {
type = LONG_VALUE_TYPE_OTHER_PAGES;
}
- ByteBuffer def = _pageChannel.createBuffer(lvalDefLen);
+ ByteBuffer def = getPageChannel().createBuffer(lvalDefLen);
ByteUtil.put3ByteInt(def, value.length);
def.put(type);
int firstLvalPageNum = PageChannel.INVALID_PAGE_NUMBER;
byte firstLvalRow = 0;
- ByteBuffer lvalPage = _pageChannel.createPageBuffer();
+ ByteBuffer lvalPage = getPageChannel().createPageBuffer();
// write other page(s)
switch(type) {
writeLongValueHeader(lvalPage);
firstLvalRow = (byte)Table.addDataPageRow(lvalPage,
value.length,
- _format);
+ getFormat());
lvalPage.put(value);
- firstLvalPageNum = _pageChannel.writeNewPage(lvalPage);
+ firstLvalPageNum = getPageChannel().writeNewPage(lvalPage);
break;
case LONG_VALUE_TYPE_OTHER_PAGES:
ByteBuffer buffer = ByteBuffer.wrap(value);
int remainingLen = buffer.remaining();
buffer.limit(0);
- int lvalPageNum = _pageChannel.allocateNewPage();
+ int lvalPageNum = getPageChannel().allocateNewPage();
byte lvalRow = 0;
int nextLvalPageNum = 0;
while(remainingLen > 0) {
writeLongValueHeader(lvalPage);
// figure out how much we will put in this page
- int chunkLength = Math.min(_format.MAX_ROW_SIZE - 4,
+ int chunkLength = Math.min(getFormat().MAX_ROW_SIZE - 4,
remainingLen);
nextLvalPageNum = ((chunkLength < remainingLen) ?
- _pageChannel.allocateNewPage() : 0);
+ getPageChannel().allocateNewPage() : 0);
// add row to this page
lvalRow = (byte)Table.addDataPageRow(lvalPage, chunkLength + 4,
- _format);
+ getFormat());
// write next page info (we'll always be writing into row 0 for
// newly created pages)
remainingLen -= chunkLength;
// write new page to database
- _pageChannel.writePage(lvalPage, lvalPageNum);
+ getPageChannel().writePage(lvalPage, lvalPageNum);
// hang onto first page info
if(firstLvalPageNum == PageChannel.INVALID_PAGE_NUMBER) {
{
lvalPage.put(PageTypes.DATA); //Page type
lvalPage.put((byte) 1); //Unknown
- lvalPage.putShort((short) (_format.PAGE_SIZE -
- _format.OFFSET_ROW_START)); //Free space
+ lvalPage.putShort((short) (getFormat().PAGE_SIZE -
+ getFormat().OFFSET_ROW_START)); //Free space
lvalPage.put((byte) 'L');
lvalPage.put((byte) 'V');
lvalPage.put((byte) 'A');
switch(getType()) {
case NUMERIC:
// don't ask me why numerics are "var length" columns...
- ByteBuffer buffer = _pageChannel.createBuffer(getLength(), order);
+ ByteBuffer buffer = getPageChannel().createBuffer(getLength(), order);
writeNumericValue(buffer, obj);
buffer.flip();
return buffer;
int size = getType().getFixedSize();
// create buffer for data
- ByteBuffer buffer = _pageChannel.createBuffer(size, order);
+ ByteBuffer buffer = getPageChannel().createBuffer(size, order);
obj = booleanToInteger(obj);
* @return A buffer with the text encoded
*/
private ByteBuffer encodeUncompressedText(CharSequence text) {
- return _format.CHARSET.encode(CharBuffer.wrap(text));
+ return getFormat().CHARSET.encode(CharBuffer.wrap(text));
}
/**
*/
private CharBuffer decodeUncompressedText(byte[] textBytes, int startPost,
int length) {
- return _format.CHARSET.decode(ByteBuffer.wrap(textBytes, startPost,
+ return getFormat().CHARSET.decode(ByteBuffer.wrap(textBytes, startPost,
length));
}
/** ID of the Tables system object */
private Integer _tableParentId;
/** Format that the containing database is in */
- private JetFormat _format;
+ private final JetFormat _format;
/**
* Map of UPPERCASE table names to page numbers containing their definition
* and their stored table name.
/** set of table names as stored in the mdb file, created on demand */
private Set<String> _tableNames;
/** Reads and writes database pages */
- private PageChannel _pageChannel;
+ private final PageChannel _pageChannel;
/** System catalog table */
private Table _systemCatalog;
/** System access control entries table */
{
_format = JetFormat.getFormat(channel);
_pageChannel = new PageChannel(channel, _format, autoSync);
+ // note, it's slighly sketchy to pass ourselves along partially
+ // constructed, but only our _format and _pageChannel refs should be
+ // needed
+ _pageChannel.initialize(this);
_buffer = _pageChannel.createPageBuffer();
readSystemCatalog();
}
public PageChannel getPageChannel() {
return _pageChannel;
}
+
+ public JetFormat getFormat() {
+ return _format;
+ }
/**
* @return The system catalog table
throw new IOException("Looking for system catalog at page " +
PAGE_SYSTEM_CATALOG + ", but page type is " + pageType);
}
- _systemCatalog = new Table(_buffer, _pageChannel, _format, PAGE_SYSTEM_CATALOG, "System Catalog");
+ _systemCatalog = new Table(this, _buffer, PAGE_SYSTEM_CATALOG,
+ "System Catalog");
Map<String,Object> row;
while ( (row = _systemCatalog.getNextRow(SYSTEM_CATALOG_COLUMNS)) != null)
{
throw new IOException("Looking for MSysACEs at page " + pageNum +
", but page type is " + pageType);
}
- _accessControlEntries = new Table(buffer, _pageChannel, _format, pageNum, "Access Control Entries");
+ _accessControlEntries = new Table(this, buffer, pageNum,
+ "Access Control Entries");
}
/**
int pageNumber = tableInfo.pageNumber.intValue();
_pageChannel.readPage(_buffer, pageNumber);
- return new Table(_buffer, _pageChannel, _format, pageNumber,
- tableInfo.tableName);
+ return new Table(this, _buffer, pageNumber, tableInfo.tableName);
}
/**
CODES_EXT.put('\'', new byte[]{(byte)6, (byte)128});
CODES_EXT.put('-', new byte[]{(byte)6, (byte)130});
}
-
+
+ /** owning table */
+ private final Table _table;
/** Page number of the index data */
private int _pageNumber;
- private int _parentPageNumber;
/** Number of rows in the index
NOTE: this does not actually seem to be the row count, unclear what the
value means*/
private int _rowCount;
- private JetFormat _format;
private SortedSet<Entry> _entries;
/** Map of columns to flags */
private Map<Column, Byte> _columns = new LinkedHashMap<Column, Byte>();
- private PageChannel _pageChannel;
/** 0-based index number */
private int _indexNumber;
/** Index name */
/** FIXME, for now, we can't write multi-page indexes or indexes using the funky primary key compression scheme */
boolean _readOnly;
- public Index(int parentPageNumber, PageChannel channel, JetFormat format) {
- _parentPageNumber = parentPageNumber;
- _pageChannel = channel;
- _format = format;
+ public Index(Table table) {
+ _table = table;
+ }
+
+ public Table getTable() {
+ return _table;
}
+ public JetFormat getFormat() {
+ return getTable().getFormat();
+ }
+
+ public PageChannel getPageChannel() {
+ return getTable().getPageChannel();
+ }
+
public void setIndexNumber(int indexNumber) {
_indexNumber = indexNumber;
}
throw new UnsupportedOperationException(
"FIXME cannot write indexes of this type yet");
}
- _pageChannel.writePage(write(), _pageNumber);
+ getPageChannel().writePage(write(), _pageNumber);
}
/**
* Write this index out to a buffer
*/
private ByteBuffer write() throws IOException {
- ByteBuffer buffer = _pageChannel.createPageBuffer();
+ ByteBuffer buffer = getPageChannel().createPageBuffer();
buffer.put((byte) 0x04); //Page type
buffer.put((byte) 0x01); //Unknown
buffer.putShort((short) 0); //Free space
- buffer.putInt(_parentPageNumber);
+ buffer.putInt(getTable().getTableDefPageNumber());
buffer.putInt(0); //Prev page
buffer.putInt(0); //Next page
buffer.putInt(0); //Leaf page
buffer.put((byte) 0); // compressed byte count
buffer.put((byte) 0); //Unknown
buffer.put((byte) 0); //Unknown
- byte[] entryMask = new byte[_format.SIZE_INDEX_ENTRY_MASK];
+ byte[] entryMask = new byte[getFormat().SIZE_INDEX_ENTRY_MASK];
int totalSize = 0;
for(Entry entry : _entries) {
int size = entry.size();
for(Entry entry : _entries) {
entry.write(buffer);
}
- buffer.putShort(2, (short) (_format.PAGE_SIZE - buffer.position()));
+ buffer.putShort(2, (short) (getFormat().PAGE_SIZE - buffer.position()));
return buffer;
}
{
_entries = new TreeSet<Entry>();
- ByteBuffer indexPage = _pageChannel.createPageBuffer();
+ ByteBuffer indexPage = getPageChannel().createPageBuffer();
// find first leaf page
int leafPageNumber = _pageNumber;
while(true) {
- _pageChannel.readPage(indexPage, leafPageNumber);
+ getPageChannel().readPage(indexPage, leafPageNumber);
if(indexPage.get(0) == INDEX_NODE_PAGE_TYPE) {
// FIXME we can't modify this index at this point in time
_readOnly = true;
// found another one
- _pageChannel.readPage(indexPage, leafPageNumber);
+ getPageChannel().readPage(indexPage, leafPageNumber);
} else {
// all done
// note, "header" data is in LITTLE_ENDIAN format, entry data is in
// BIG_ENDIAN format
- int nextLeafPage = leafPage.getInt(_format.OFFSET_NEXT_INDEX_LEAF_PAGE);
+ int nextLeafPage = leafPage.getInt(getFormat().OFFSET_NEXT_INDEX_LEAF_PAGE);
readIndexPage(leafPage, true, _entries, null);
return nextLeafPage;
// note, "header" data is in LITTLE_ENDIAN format, entry data is in
// BIG_ENDIAN format
int numCompressedBytes = indexPage.get(
- _format.OFFSET_INDEX_COMPRESSED_BYTE_COUNT);
- int entryMaskLength = _format.SIZE_INDEX_ENTRY_MASK;
- int entryMaskPos = _format.OFFSET_INDEX_ENTRY_MASK;
- int entryPos = entryMaskPos + _format.SIZE_INDEX_ENTRY_MASK;
+ getFormat().OFFSET_INDEX_COMPRESSED_BYTE_COUNT);
+ int entryMaskLength = getFormat().SIZE_INDEX_ENTRY_MASK;
+ int entryMaskPos = getFormat().OFFSET_INDEX_ENTRY_MASK;
+ int entryPos = entryMaskPos + getFormat().SIZE_INDEX_ENTRY_MASK;
int lastStart = 0;
byte[] valuePrefix = null;
boolean firstEntry = true;
private static final int PAGE_GLOBAL_USAGE_MAP = 1;
/** Channel containing the database */
- private FileChannel _channel;
+ private final FileChannel _channel;
/** Format of the database in the channel */
- private JetFormat _format;
+ private final JetFormat _format;
+ /** whether or not to force all writes to disk immediately */
+ private final boolean _autoSync;
/** Tracks free pages in the database. */
private UsageMap _globalUsageMap;
- /** whether or not to force all writes to disk immediately */
- private boolean _autoSync;
/**
* @param channel Channel containing the database
_channel = channel;
_format = format;
_autoSync = autoSync;
- //Null check only exists for unit tests. Channel should never normally be null.
- if (channel != null) {
- // note the global usage map is a special map where any page outside of
- // the current range is assumed to be "on"
- _globalUsageMap = UsageMap.read(this, PAGE_GLOBAL_USAGE_MAP, (byte) 0,
- format, true);
- }
}
+ /**
+ * Does second-stage initialization, must be called after construction.
+ */
+ public void initialize(Database database)
+ throws IOException
+ {
+ // note the global usage map is a special map where any page outside of
+ // the current range is assumed to be "on"
+ _globalUsageMap = UsageMap.read(database, PAGE_GLOBAL_USAGE_MAP, (byte) 0,
+ true);
+ }
+
/**
* Only used by unit tests
*/
_format = JetFormat.VERSION_4;
_autoSync = false;
}
+
+ public JetFormat getFormat() {
+ return _format;
+ }
/**
* @param buffer Buffer to read the page into
LOG.debug("Reading in page " + Integer.toHexString(pageNumber));
}
buffer.clear();
- boolean rtn = _channel.read(buffer, (long) pageNumber * (long) _format.PAGE_SIZE) != -1;
+ boolean rtn = _channel.read(buffer, (long) pageNumber * (long) getFormat().PAGE_SIZE) != -1;
buffer.flip();
return rtn;
}
{
page.rewind();
page.position(pageOffset);
- _channel.write(page, (((long) pageNumber * (long) _format.PAGE_SIZE) +
+ _channel.write(page, (((long) pageNumber * (long) getFormat().PAGE_SIZE) +
(long) pageOffset));
if(_autoSync) {
flush();
// push the buffer to the end of the page, so that a full page's worth of
// data is written regardless of the incoming buffer size (we use a tiny
// buffer in allocateNewPage)
- long offset = size + (_format.PAGE_SIZE - page.remaining());
+ long offset = size + (getFormat().PAGE_SIZE - page.remaining());
_channel.write(page, offset);
- int pageNumber = (int) (size / _format.PAGE_SIZE);
+ int pageNumber = (int) (size / getFormat().PAGE_SIZE);
_globalUsageMap.removePageNumber(pageNumber); //force is done here
return pageNumber;
}
* @return A newly-allocated buffer that can be passed to readPage
*/
public ByteBuffer createPageBuffer() {
- return createBuffer(_format.PAGE_SIZE);
+ return createBuffer(getFormat().PAGE_SIZE);
}
/**
0));
}
};
-
+
+ /** owning database */
+ private final Database _database;
/** State used for reading the table rows */
private RowState _rowState;
/** Type of the table (either TYPE_SYSTEM or TYPE_USER) */
/** last auto number for the table */
private int _lastAutoNumber;
/** page number of the definition of this table */
- private int _tableDefPageNumber;
+ private final int _tableDefPageNumber;
/** Number of rows left to be read on the current page */
private short _rowsLeftOnPage = 0;
/** max Number of columns in the table (includes previous deletions) */
private short _maxColumnCount;
/** max Number of variable columns in the table */
private short _maxVarColumnCount;
- /** Format of the database that contains this table */
- private JetFormat _format;
/** List of columns in this table, ordered by column number */
private List<Column> _columns = new ArrayList<Column>();
/** List of variable length columns in this table, ordered by offset */
private List<Column> _varColumns = new ArrayList<Column>();
/** List of indexes on this table */
private List<Index> _indexes = new ArrayList<Index>();
- /** Used to read in pages */
- private PageChannel _pageChannel;
/** Table name as stored in Database */
- private String _name;
+ private final String _name;
/** Usage map of pages that this table owns */
private UsageMap _ownedPages;
/** Iterator over the pages that this table owns */
/**
* Only used by unit tests
*/
- Table(boolean testing) throws IOException {
+ Table(boolean testing, List<Column> columns) throws IOException {
if(!testing) {
throw new IllegalArgumentException();
}
- _pageChannel = new PageChannel(testing);
+ _database = null;
+ _tableDefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
+ _name = null;
+ setColumns(columns);
}
/**
* @param pageNumber Page number of the table definition
* @param name Table name
*/
- protected Table(ByteBuffer tableBuffer, PageChannel pageChannel,
- JetFormat format, int pageNumber, String name)
+ protected Table(Database database, ByteBuffer tableBuffer,
+ int pageNumber, String name)
throws IOException
{
- _pageChannel = pageChannel;
- _format = format;
+ _database = database;
_tableDefPageNumber = pageNumber;
_name = name;
- int nextPage;
+ int nextPage = tableBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
ByteBuffer nextPageBuffer = null;
- nextPage = tableBuffer.getInt(_format.OFFSET_NEXT_TABLE_DEF_PAGE);
while (nextPage != 0) {
if (nextPageBuffer == null) {
- nextPageBuffer = _pageChannel.createPageBuffer();
+ nextPageBuffer = getPageChannel().createPageBuffer();
}
- _pageChannel.readPage(nextPageBuffer, nextPage);
- nextPage = nextPageBuffer.getInt(_format.OFFSET_NEXT_TABLE_DEF_PAGE);
- ByteBuffer newBuffer = _pageChannel.createBuffer(
- tableBuffer.capacity() + format.PAGE_SIZE - 8);
+ getPageChannel().readPage(nextPageBuffer, nextPage);
+ nextPage = nextPageBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
+ ByteBuffer newBuffer = getPageChannel().createBuffer(
+ tableBuffer.capacity() + getFormat().PAGE_SIZE - 8);
newBuffer.put(tableBuffer);
- newBuffer.put(nextPageBuffer.array(), 8, format.PAGE_SIZE - 8);
+ newBuffer.put(nextPageBuffer.array(), 8, getFormat().PAGE_SIZE - 8);
tableBuffer = newBuffer;
tableBuffer.flip();
}
public String getName() {
return _name;
}
+
+ public Database getDatabase() {
+ return _database;
+ }
+
+ public JetFormat getFormat() {
+ return getDatabase().getFormat();
+ }
+
+ public PageChannel getPageChannel() {
+ return getDatabase().getPageChannel();
+ }
+
+ protected int getTableDefPageNumber() {
+ return _tableDefPageNumber;
+ }
/**
* @return All of the columns in this table (unmodifiable List)
/**
* Only called by unit tests
*/
- void setColumns(List<Column> columns) {
+ private void setColumns(List<Column> columns) {
_columns = columns;
int colIdx = 0;
int varLenIdx = 0;
}
// delete flag always gets set in the "root" page (even if overflow row)
- ByteBuffer rowBuffer = _rowState.getPage(_pageChannel);
+ ByteBuffer rowBuffer = _rowState.getPage(getPageChannel());
int pageNumber = _rowState.getPageNumber();
- int rowIndex = getRowStartOffset(_currentRowInPage, _format);
+ int rowIndex = getRowStartOffset(_currentRowInPage, getFormat());
rowBuffer.putShort(rowIndex, (short)(rowBuffer.getShort(rowIndex)
| DELETED_ROW_MASK | OVERFLOW_ROW_MASK));
writeDataPage(rowBuffer, pageNumber);
}
// load new page
- ByteBuffer rowBuffer = _rowState.setPage(_pageChannel, nextPageNumber);
+ ByteBuffer rowBuffer = _rowState.setPage(getPageChannel(), nextPageNumber);
if(rowBuffer.get() != PageTypes.DATA) {
//Only interested in data pages
continue;
}
- _rowsLeftOnPage = rowBuffer.getShort(_format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
+ _rowsLeftOnPage = rowBuffer.getShort(getFormat().OFFSET_NUM_ROWS_ON_DATA_PAGE);
if(_rowsLeftOnPage == 0) {
// no rows on this page?
continue;
_rowsLeftOnPage--;
ByteBuffer rowBuffer =
- positionAtRow(_rowState, _currentRowInPage, _pageChannel, _format);
+ positionAtRow(_rowState, _currentRowInPage, getPageChannel(), getFormat());
if(rowBuffer != null) {
// we found a non-deleted row, return it
return rowBuffer;
if (LOG.isDebugEnabled()) {
tableBuffer.rewind();
LOG.debug("Table def block:\n" + ByteUtil.toHexString(tableBuffer,
- _format.SIZE_TDEF_HEADER));
+ getFormat().SIZE_TDEF_HEADER));
}
- _rowCount = tableBuffer.getInt(_format.OFFSET_NUM_ROWS);
- _lastAutoNumber = tableBuffer.getInt(_format.OFFSET_NEXT_AUTO_NUMBER);
- _tableType = tableBuffer.get(_format.OFFSET_TABLE_TYPE);
- _maxColumnCount = tableBuffer.getShort(_format.OFFSET_MAX_COLS);
- _maxVarColumnCount = tableBuffer.getShort(_format.OFFSET_NUM_VAR_COLS);
- short columnCount = tableBuffer.getShort(_format.OFFSET_NUM_COLS);
- _indexSlotCount = tableBuffer.getInt(_format.OFFSET_NUM_INDEX_SLOTS);
- _indexCount = tableBuffer.getInt(_format.OFFSET_NUM_INDEXES);
+ _rowCount = tableBuffer.getInt(getFormat().OFFSET_NUM_ROWS);
+ _lastAutoNumber = tableBuffer.getInt(getFormat().OFFSET_NEXT_AUTO_NUMBER);
+ _tableType = tableBuffer.get(getFormat().OFFSET_TABLE_TYPE);
+ _maxColumnCount = tableBuffer.getShort(getFormat().OFFSET_MAX_COLS);
+ _maxVarColumnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_VAR_COLS);
+ short columnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_COLS);
+ _indexSlotCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEX_SLOTS);
+ _indexCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEXES);
- byte rowNum = tableBuffer.get(_format.OFFSET_OWNED_PAGES);
- int pageNum = ByteUtil.get3ByteInt(tableBuffer, _format.OFFSET_OWNED_PAGES + 1);
- _ownedPages = UsageMap.read(_pageChannel, pageNum, rowNum, _format,
- false);
+ byte rowNum = tableBuffer.get(getFormat().OFFSET_OWNED_PAGES);
+ int pageNum = ByteUtil.get3ByteInt(tableBuffer, getFormat().OFFSET_OWNED_PAGES + 1);
+ _ownedPages = UsageMap.read(getDatabase(), pageNum, rowNum, false);
_ownedPagesIterator = _ownedPages.iterator();
- rowNum = tableBuffer.get(_format.OFFSET_FREE_SPACE_PAGES);
- pageNum = ByteUtil.get3ByteInt(tableBuffer, _format.OFFSET_FREE_SPACE_PAGES + 1);
- _freeSpacePages = UsageMap.read(_pageChannel, pageNum, rowNum, _format,
- false);
+ rowNum = tableBuffer.get(getFormat().OFFSET_FREE_SPACE_PAGES);
+ pageNum = ByteUtil.get3ByteInt(tableBuffer, getFormat().OFFSET_FREE_SPACE_PAGES + 1);
+ _freeSpacePages = UsageMap.read(getDatabase(), pageNum, rowNum, false);
for (int i = 0; i < _indexCount; i++) {
- Index index = new Index(_tableDefPageNumber, _pageChannel, _format);
+ Index index = new Index(this);
_indexes.add(index);
- index.setRowCount(tableBuffer.getInt(_format.OFFSET_INDEX_DEF_BLOCK +
- i * _format.SIZE_INDEX_DEFINITION + 4));
+ index.setRowCount(tableBuffer.getInt(getFormat().OFFSET_INDEX_DEF_BLOCK +
+ i * getFormat().SIZE_INDEX_DEFINITION + 4));
}
- int offset = _format.OFFSET_INDEX_DEF_BLOCK +
- _indexCount * _format.SIZE_INDEX_DEFINITION;
+ int offset = getFormat().OFFSET_INDEX_DEF_BLOCK +
+ _indexCount * getFormat().SIZE_INDEX_DEFINITION;
Column column;
for (int i = 0; i < columnCount; i++) {
- column = new Column(tableBuffer,
- offset + i * _format.SIZE_COLUMN_HEADER, _pageChannel, _format);
+ column = new Column(this, tableBuffer,
+ offset + i * getFormat().SIZE_COLUMN_HEADER);
_columns.add(column);
if(column.isVariableLength()) {
// also shove it in the variable columns list, which is ordered
_varColumns.add(column);
}
}
- offset += columnCount * _format.SIZE_COLUMN_HEADER;
+ offset += columnCount * getFormat().SIZE_COLUMN_HEADER;
for (int i = 0; i < columnCount; i++) {
column = (Column) _columns.get(i);
short nameLength = tableBuffer.getShort(offset);
byte[] nameBytes = new byte[nameLength];
tableBuffer.position(offset);
tableBuffer.get(nameBytes, 0, (int) nameLength);
- column.setName(_format.CHARSET.decode(ByteBuffer.wrap(nameBytes)).toString());
+ column.setName(getFormat().CHARSET.decode(ByteBuffer.wrap(nameBytes)).toString());
offset += nameLength;
}
Collections.sort(_columns);
int idxOffset = tableBuffer.position();
tableBuffer.position(idxOffset +
- (_format.OFFSET_INDEX_NUMBER_BLOCK * _indexCount));
+ (getFormat().OFFSET_INDEX_NUMBER_BLOCK * _indexCount));
// there are _indexSlotCount blocks here, we ignore any slot with an index
// number greater than the number of actual indexes
for (int i = 0; i < _indexCount; i++) {
byte[] nameBytes = new byte[tableBuffer.getShort()];
tableBuffer.get(nameBytes);
- _indexes.get(i).setName(_format.CHARSET.decode(ByteBuffer.wrap(
+ _indexes.get(i).setName(getFormat().CHARSET.decode(ByteBuffer.wrap(
nameBytes)).toString());
}
int idxEndOffset = tableBuffer.position();
throws IOException
{
// write the page data
- _pageChannel.writePage(pageBuffer, pageNumber);
+ getPageChannel().writePage(pageBuffer, pageNumber);
// if the overflow buffer is this page, invalidate it
_rowState.possiblyInvalidate(pageNumber, pageBuffer);
* @param rows List of Object[] row values
*/
public void addRows(List<? extends Object[]> rows) throws IOException {
- ByteBuffer dataPage = _pageChannel.createPageBuffer();
+ ByteBuffer dataPage = getPageChannel().createPageBuffer();
ByteBuffer[] rowData = new ByteBuffer[rows.size()];
Iterator<? extends Object[]> iter = rows.iterator();
for (int i = 0; iter.hasNext(); i++) {
- rowData[i] = createRow(iter.next(), _format.MAX_ROW_SIZE);
- if (rowData[i].limit() > _format.MAX_ROW_SIZE) {
+ rowData[i] = createRow(iter.next(), getFormat().MAX_ROW_SIZE);
+ if (rowData[i].limit() > getFormat().MAX_ROW_SIZE) {
throw new IOException("Row size " + rowData[i].limit() +
" is too large");
}
revPageIter.hasNextPage(); )
{
int tmpPageNumber = revPageIter.getNextPage();
- _pageChannel.readPage(dataPage, tmpPageNumber);
+ getPageChannel().readPage(dataPage, tmpPageNumber);
if(dataPage.get() == PageTypes.DATA) {
// found last data page
pageNumber = tmpPageNumber;
for (int i = 0; i < rowData.length; i++) {
rowSize = rowData[i].remaining();
- int rowSpaceUsage = getRowSpaceUsage(rowSize, _format);
- short freeSpaceInPage = dataPage.getShort(_format.OFFSET_FREE_SPACE);
+ int rowSpaceUsage = getRowSpaceUsage(rowSize, getFormat());
+ short freeSpaceInPage = dataPage.getShort(getFormat().OFFSET_FREE_SPACE);
if (freeSpaceInPage < rowSpaceUsage) {
//Last data page is full. Create a new one.
_freeSpacePages.removePageNumber(pageNumber);
pageNumber = newDataPage(dataPage);
- freeSpaceInPage = dataPage.getShort(_format.OFFSET_FREE_SPACE);
+ freeSpaceInPage = dataPage.getShort(getFormat().OFFSET_FREE_SPACE);
}
// write out the row data
- int rowNum = addDataPageRow(dataPage, rowSize, _format);
+ int rowNum = addDataPageRow(dataPage, rowSize, getFormat());
dataPage.put(rowData[i]);
// update the indexes
private void updateTableDefinition() throws IOException
{
// load table definition
- ByteBuffer tdefPage = _pageChannel.createPageBuffer();
- _pageChannel.readPage(tdefPage, _tableDefPageNumber);
+ ByteBuffer tdefPage = getPageChannel().createPageBuffer();
+ getPageChannel().readPage(tdefPage, _tableDefPageNumber);
// make sure rowcount and autonumber are up-to-date
- tdefPage.putInt(_format.OFFSET_NUM_ROWS, _rowCount);
- tdefPage.putInt(_format.OFFSET_NEXT_AUTO_NUMBER, _lastAutoNumber);
+ tdefPage.putInt(getFormat().OFFSET_NUM_ROWS, _rowCount);
+ tdefPage.putInt(getFormat().OFFSET_NEXT_AUTO_NUMBER, _lastAutoNumber);
// write any index changes
Iterator<Index> indIter = _indexes.iterator();
for (int i = 0; i < _indexes.size(); i++) {
- tdefPage.putInt(_format.OFFSET_INDEX_DEF_BLOCK +
- (i * _format.SIZE_INDEX_DEFINITION) + 4, _rowCount);
+ tdefPage.putInt(getFormat().OFFSET_INDEX_DEF_BLOCK +
+ (i * getFormat().SIZE_INDEX_DEFINITION) + 4, _rowCount);
Index index = indIter.next();
index.update();
}
// write modified table definition
- _pageChannel.writePage(tdefPage, _tableDefPageNumber);
+ getPageChannel().writePage(tdefPage, _tableDefPageNumber);
}
/**
}
dataPage.put(PageTypes.DATA); //Page type
dataPage.put((byte) 1); //Unknown
- dataPage.putShort((short)getRowSpaceUsage(_format.MAX_ROW_SIZE,
- _format)); //Free space in this page
+ dataPage.putShort((short)getRowSpaceUsage(getFormat().MAX_ROW_SIZE,
+ getFormat())); //Free space in this page
dataPage.putInt(_tableDefPageNumber); //Page pointer to table definition
dataPage.putInt(0); //Unknown
dataPage.putInt(0); //Number of records on this page
- int pageNumber = _pageChannel.writeNewPage(dataPage);
+ int pageNumber = getPageChannel().writeNewPage(dataPage);
_ownedPages.addPageNumber(pageNumber);
_freeSpacePages.addPageNumber(pageNumber);
return pageNumber;
* Serialize a row of Objects into a byte buffer
*/
ByteBuffer createRow(Object[] rowArray, int maxRowSize) throws IOException {
- ByteBuffer buffer = _pageChannel.createPageBuffer();
+ ByteBuffer buffer = getPageChannel().createPageBuffer();
buffer.putShort((short) _maxColumnCount);
NullMask nullMask = new NullMask(_maxColumnCount);
public static final byte MAP_TYPE_INLINE = 0x0;
/** Reference map type, for maps that are too large to fit inline */
public static final byte MAP_TYPE_REFERENCE = 0x1;
-
+
+ /** owning database */
+ private final Database _database;
/** Page number of the map table declaration */
- private int _tablePageNum;
+ private final int _tablePageNum;
/** Offset of the data page at which the usage map data starts */
private int _startOffset;
/** Offset of the data page at which the usage map declaration starts */
private short _rowStart;
- /** Format of the database that contains this usage map */
- private JetFormat _format;
/** First page that this usage map applies to */
private int _startPage;
/** Last page that this usage map applies to */
/** bits representing page numbers used, offset from _startPage */
private BitSet _pageNumbers = new BitSet();
/** Buffer that contains the usage map table declaration page */
- private ByteBuffer _tableBuffer;
- /** Used to read in pages */
- private PageChannel _pageChannel;
+ private final ByteBuffer _tableBuffer;
/** modification count on the usage map, used to keep the iterators in
sync */
private int _modCount = 0;
private Handler _handler;
/**
- * @param pageChannel Used to read in pages
+ * @param database database that contains this usage map
* @param tableBuffer Buffer that contains this map's declaration
* @param pageNum Page number that this usage map is contained in
- * @param format Format of the database that contains this usage map
* @param rowStart Offset at which the declaration starts in the buffer
*/
- private UsageMap(PageChannel pageChannel, ByteBuffer tableBuffer,
- int pageNum, JetFormat format, short rowStart)
+ private UsageMap(Database database, ByteBuffer tableBuffer,
+ int pageNum, short rowStart)
throws IOException
{
- _pageChannel = pageChannel;
+ _database = database;
_tableBuffer = tableBuffer;
_tablePageNum = pageNum;
- _format = format;
_rowStart = rowStart;
- _tableBuffer.position((int) _rowStart + format.OFFSET_USAGE_MAP_START);
+ _tableBuffer.position((int) _rowStart + getFormat().OFFSET_USAGE_MAP_START);
_startOffset = _tableBuffer.position();
if (LOG.isDebugEnabled()) {
LOG.debug("Usage map block:\n" + ByteUtil.toHexString(_tableBuffer, _rowStart,
}
}
+ public Database getDatabase() {
+ return _database;
+ }
+
+ public JetFormat getFormat() {
+ return getDatabase().getFormat();
+ }
+
+ public PageChannel getPageChannel() {
+ return getDatabase().getPageChannel();
+ }
+
/**
- * @param pageChannel Used to read in pages
+ * @param database database that contains this usage map
* @param pageNum Page number that this usage map is contained in
* @param rowNum Number of the row on the page that contains this usage map
- * @param format Format of the database that contains this usage map
* @return Either an InlineUsageMap or a ReferenceUsageMap, depending on
* which type of map is found
*/
- public static UsageMap read(PageChannel pageChannel, int pageNum,
- byte rowNum, JetFormat format,
- boolean assumeOutOfRangeBitsOn)
+ public static UsageMap read(Database database, int pageNum,
+ byte rowNum, boolean assumeOutOfRangeBitsOn)
throws IOException
{
+ JetFormat format = database.getFormat();
+ PageChannel pageChannel = database.getPageChannel();
ByteBuffer tableBuffer = pageChannel.createPageBuffer();
pageChannel.readPage(tableBuffer, pageNum);
short rowStart = Table.findRowStart(tableBuffer, rowNum, format);
int rowEnd = Table.findRowEnd(tableBuffer, rowNum, format);
tableBuffer.limit(rowEnd);
byte mapType = tableBuffer.get(rowStart);
- UsageMap rtn = new UsageMap(pageChannel, tableBuffer, pageNum, format,
- rowStart);
+ UsageMap rtn = new UsageMap(database, tableBuffer, pageNum, rowStart);
rtn.initHandler(mapType, assumeOutOfRangeBitsOn);
return rtn;
}
return _tablePageNum;
}
- protected PageChannel getPageChannel() {
- return _pageChannel;
- }
-
- protected JetFormat getFormat() {
- return _format;
- }
-
protected int getStartPage() {
return _startPage;
}
throws IOException
{
// note, we only want to write the row data with which we are working
- _pageChannel.writePage(_tableBuffer, _tablePageNum, _rowStart);
+ getPageChannel().writePage(_tableBuffer, _tablePageNum, _rowStart);
}
/**
}
public void testCreateRow() throws Exception {
- JetFormat format = JetFormat.VERSION_4;
- Table table = new Table(true);
+ final JetFormat format = JetFormat.VERSION_4;
+ final PageChannel pageChannel = new PageChannel(true);
List<Column> columns = new ArrayList<Column>();
- Column col = new Column(true);
+ Column col = newTestColumn(pageChannel);
col.setType(DataType.INT);
columns.add(col);
- col = new Column(true);
+ col = newTestColumn(pageChannel);
col.setType(DataType.TEXT);
columns.add(col);
- col = new Column(true);
+ col = newTestColumn(pageChannel);
col.setType(DataType.TEXT);
columns.add(col);
- table.setColumns(columns);
+ Table table = new Table(true, columns) {
+ @Override
+ public PageChannel getPageChannel() {
+ return pageChannel;
+ }
+ };
int colCount = 3;
Object[] row = new Object[colCount];
row[0] = new Short((short) 9);
assertEquals((short) 2, buffer.getShort(28));
assertEquals((byte) 7, buffer.get(30));
}
+
+ private static Column newTestColumn(final PageChannel pageChannel) {
+ return new Column(true) {
+ @Override
+ public PageChannel getPageChannel() {
+ return pageChannel;
+ }
+ @Override
+ public JetFormat getFormat() {
+ return JetFormat.VERSION_4;
+ }
+ };
+ }
}