Browse Source

Fix edge case which can cause table to be considered corrupt. Fixes #156

git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1385 f203690c-595d-4dc9-a70b-905162fa7fd2
tags/jackcess-4.0.3
James Ahlborn 1 year ago
parent
commit
1ac3fbe1c9

+ 5
- 0
src/changes/changes.xml View File

@@ -4,6 +4,11 @@
<author email="javajedi@users.sf.net">Tim McCune</author>
</properties>
<body>
<release version="4.0.3" date="TBD">
<action dev="jahlborn" type="fix" system="SourceForge2" issue="156">
Fix edge case which can cause table to be considered corrupt.
</action>
</release>
<release version="4.0.2" date="2022-08-26">
<action dev="jahlborn" type="update">
Add Table methods to get the creation and last modified dates.

+ 87
- 78
src/main/java/com/healthmarketscience/jackcess/impl/UsageMap.java View File

@@ -36,7 +36,7 @@ public class UsageMap

/** bit index value for an invalid page number */
private static final int INVALID_BIT_INDEX = -1;
/** owning database */
private final DatabaseImpl _database;
/** Page number of the map table declaration */
@@ -84,7 +84,7 @@ public class UsageMap
public DatabaseImpl getDatabase() {
return _database;
}
public JetFormat getFormat() {
return getDatabase().getFormat();
}
@@ -106,7 +106,7 @@ public class UsageMap
int umapPageNum = ByteUtil.get3ByteInt(buf);
return read(database, umapPageNum, umapRowNum, false);
}
/**
* @param database database that contains this usage map
* @param pageNum Page number that this usage map is contained in
@@ -130,7 +130,7 @@ public class UsageMap
pageChannel.readPage(tableBuffer, pageNum);
short rowStart = TableImpl.findRowStart(tableBuffer, rowNum, format);
int rowEnd = TableImpl.findRowEnd(tableBuffer, rowNum, format);
tableBuffer.limit(rowEnd);
tableBuffer.limit(rowEnd);
byte mapType = tableBuffer.get(rowStart);
UsageMap rtn = new UsageMap(database, tableBuffer, pageNum, rowStart);
rtn.initHandler(mapType, isGlobal);
@@ -141,16 +141,16 @@ public class UsageMap
throws IOException
{
if (mapType == MAP_TYPE_INLINE) {
_handler = (isGlobal ? new GlobalInlineHandler() :
_handler = (isGlobal ? new GlobalInlineHandler() :
new InlineHandler());
} else if (mapType == MAP_TYPE_REFERENCE) {
_handler = (isGlobal ? new GlobalReferenceHandler() :
_handler = (isGlobal ? new GlobalReferenceHandler() :
new ReferenceHandler());
} else {
throw new IOException(MSG_PREFIX_UNRECOGNIZED_MAP + mapType);
}
}
public PageCursor cursor() {
return new PageCursor();
}
@@ -158,7 +158,7 @@ public class UsageMap
public int getPageCount() {
return _pageNumbers.cardinality();
}
protected short getRowStart() {
return _rowStart;
}
@@ -170,27 +170,27 @@ public class UsageMap
protected void setStartOffset(int startOffset) {
_startOffset = startOffset;
}
protected int getStartOffset() {
return _startOffset;
}
protected ByteBuffer getTableBuffer() {
return _tableBuffer;
}
protected int getTablePageNumber() {
return _tablePageNum;
}
protected int getStartPage() {
return _startPage;
}
protected int getEndPage() {
return _endPage;
}
protected BitSet getPageNumbers() {
return _pageNumbers;
}
@@ -206,7 +206,7 @@ public class UsageMap
}

protected int getFirstPageNumber() {
return bitIndexToPageNumber(getNextBitIndex(-1),
return bitIndexToPageNumber(getNextBitIndex(-1),
RowIdImpl.LAST_PAGE_NUMBER);
}

@@ -214,12 +214,12 @@ public class UsageMap
return bitIndexToPageNumber(
getNextBitIndex(pageNumberToBitIndex(curPage)),
RowIdImpl.LAST_PAGE_NUMBER);
}
}
protected int getNextBitIndex(int curIndex) {
return _pageNumbers.nextSetBit(curIndex + 1);
}
}
protected int getLastPageNumber() {
return bitIndexToPageNumber(getPrevBitIndex(_pageNumbers.length()),
RowIdImpl.FIRST_PAGE_NUMBER);
@@ -229,16 +229,16 @@ public class UsageMap
return bitIndexToPageNumber(
getPrevBitIndex(pageNumberToBitIndex(curPage)),
RowIdImpl.FIRST_PAGE_NUMBER);
}
}
protected int getPrevBitIndex(int curIndex) {
--curIndex;
while((curIndex >= 0) && !_pageNumbers.get(curIndex)) {
--curIndex;
}
return curIndex;
}
}
protected int bitIndexToPageNumber(int bitIndex,
int invalidPageNumber) {
return((bitIndex >= 0) ? (_startPage + bitIndex) : invalidPageNumber);
@@ -256,20 +256,20 @@ public class UsageMap
_startPage = 0;
_endPage = 0;
++_modCount;
// clear out the table data (everything except map type)
int tableStart = getRowStart() + 1;
int tableEnd = getRowEnd();
ByteUtil.clearRange(_tableBuffer, tableStart, tableEnd);
}
protected void writeTable()
throws IOException
{
// note, we only want to write the row data with which we are working
getPageChannel().writePage(_tableBuffer, _tablePageNum, _rowStart);
}
/**
* Read in the page numbers in this inline map
*/
@@ -305,7 +305,7 @@ public class UsageMap
public boolean containsPageNumber(int pageNumber) {
return _handler.containsPageNumber(pageNumber);
}
/**
* Add a page number to this usage map
*/
@@ -313,23 +313,23 @@ public class UsageMap
++_modCount;
_handler.addOrRemovePageNumber(pageNumber, true, false);
}
/**
* Remove a page number from this usage map
*/
public void removePageNumber(int pageNumber)
throws IOException
public void removePageNumber(int pageNumber)
throws IOException
{
removePageNumber(pageNumber, true);
}
private void removePageNumber(int pageNumber, boolean force)
throws IOException
private void removePageNumber(int pageNumber, boolean force)
throws IOException
{
++_modCount;
_handler.addOrRemovePageNumber(pageNumber, false, force);
}
protected void updateMap(int absolutePageNumber,
int bufferRelativePageNumber,
ByteBuffer buffer, boolean add, boolean force)
@@ -349,7 +349,7 @@ public class UsageMap
" usage map, expected range " +
_startPage + " to " + _endPage);
}
//Apply the bitmask
if (add) {
b |= bitmask;
@@ -373,13 +373,13 @@ public class UsageMap

// clear out the main table (inline usage map data and start page)
clearTableAndPages();
// set the new map type
_tableBuffer.put(getRowStart(), MAP_TYPE_REFERENCE);

// write the new table data
writeTable();
// set new handler
_handler = new ReferenceHandler();

@@ -444,7 +444,12 @@ public class UsageMap
ranges.add(String.valueOf(rangeStart));
}
}

private static int toValidStartPage(int startPage) {
// start page must be a multiple of 8
return ((startPage / 8) * 8);
}

private abstract class Handler
{
protected Handler() {
@@ -454,7 +459,7 @@ public class UsageMap
return(isPageWithinRange(pageNumber) &&
getPageNumbers().get(pageNumberToBitIndex(pageNumber)));
}
/**
* @param pageNumber Page number to add or remove from this map
* @param add True to add it, false to remove it
@@ -476,7 +481,7 @@ public class UsageMap
private class InlineHandler extends Handler
{
private final int _maxInlinePages;
protected InlineHandler() throws IOException
{
_maxInlinePages = (getInlineDataEnd() - getInlineDataStart()) * 8;
@@ -496,15 +501,15 @@ public class UsageMap
protected final int getInlineDataEnd() {
return getRowEnd();
}
/**
* Sets the page range for an inline usage map starting from the given
* page.
*/
private void setInlinePageRange(int startPage) {
setPageRange(startPage, startPage + getMaxInlinePages());
}
}
@Override
public void addOrRemovePageNumber(int pageNumber, boolean add,
boolean force)
@@ -518,7 +523,7 @@ public class UsageMap
force);
// Write the updated map back to disk
writeTable();
} else {

// uh-oh, we've split our britches. what now?
@@ -531,7 +536,7 @@ public class UsageMap
throws IOException
{
// determine what our status is before taking action
if(add) {

int firstPage = getFirstPageNumber();
@@ -547,11 +552,14 @@ public class UsageMap
} else {
firstPage = pageNumber;
}

firstPage = toValidStartPage(firstPage);

if((lastPage - firstPage + 1) < getMaxInlinePages()) {

// we can still fit within an inline map
moveToNewStartPage(firstPage, pageNumber);
} else {
// not going to happen, need to promote the usage map to a
// reference map
@@ -635,7 +643,7 @@ public class UsageMap
// since we assuming out-of-range bits are "on". Note, we are leaving
// small holes in the database here (leaving behind some free pages),
// but it's not the end of the world.
if(!add) {

int firstPage = getFirstPageNumber();
@@ -666,10 +674,11 @@ public class UsageMap
throws IOException
{
int oldEndPage = getEndPage();
int newStartPage =
((firstPage <= PageChannel.INVALID_PAGE_NUMBER) ? newPageNumber :
// just shift a little and discard any initial unused pages.
(newPageNumber - (getMaxInlinePages() / 2)));
int newStartPage =
toValidStartPage(
((firstPage <= PageChannel.INVALID_PAGE_NUMBER) ? newPageNumber :
// just shift a little and discard any initial unused pages.
(newPageNumber - (getMaxInlinePages() / 2))));

// move the current data
moveToNewStartPage(newStartPage, PageChannel.INVALID_PAGE_NUMBER);
@@ -708,19 +717,19 @@ public class UsageMap
*/
private class ReferenceHandler extends Handler
{
/** Buffer that contains the current reference map page */
/** Buffer that contains the current reference map page */
private final TempPageHolder _mapPageHolder =
TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
private final int _maxPagesPerUsageMapPage;
private ReferenceHandler() throws IOException
{
_maxPagesPerUsageMapPage = ((getFormat().PAGE_SIZE -
_maxPagesPerUsageMapPage = ((getFormat().PAGE_SIZE -
getFormat().OFFSET_USAGE_MAP_PAGE_DATA) * 8);
int numUsagePages = (getRowEnd() - getRowStart() - 1) / 4;
setStartOffset(getFormat().OFFSET_USAGE_MAP_PAGE_DATA);
setPageRange(0, (numUsagePages * _maxPagesPerUsageMapPage));
// there is no "start page" for a reference usage map, so we get an
// extra page reference on top of the number of page references that fit
// in the table
@@ -745,7 +754,7 @@ public class UsageMap
protected final int getMaxPagesPerUsagePage() {
return _maxPagesPerUsageMapPage;
}
@Override
public void addOrRemovePageNumber(int pageNumber, boolean add,
boolean force)
@@ -774,11 +783,11 @@ public class UsageMap
mapPageBuffer, add, force);
getPageChannel().writePage(mapPageBuffer, mapPageNum);
}
/**
* Create a new usage map page and update the map declaration with a
* pointer to it.
* @param pageIndex Index of the page reference within the map declaration
* @param pageIndex Index of the page reference within the map declaration
*/
private ByteBuffer createNewUsageMapPage(int pageIndex) throws IOException
{
@@ -789,14 +798,14 @@ public class UsageMap
writeTable();
return mapPageBuffer;
}
private int calculateMapPagePointerOffset(int pageIndex) {
return getRowStart() + getFormat().OFFSET_REFERENCE_MAP_PAGE_NUMBERS +
(pageIndex * 4);
}

protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
throws IOException
protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
throws IOException
{
ByteBuffer mapPageBuffer = _mapPageHolder.setNewPage(getPageChannel());
mapPageBuffer.put(PageTypes.USAGE_MAP);
@@ -850,7 +859,7 @@ public class UsageMap
super.addOrRemovePageNumber(pageNumber, add, force);

while(_pendingPage != null) {
// while updating our usage map, we needed to allocate a new page (and
// thus mark a new page as used). we delayed that marking so that we
// didn't get into an infinite loop. now that we completed the
@@ -862,12 +871,12 @@ public class UsageMap
_pendingPage = null;

super.addOrRemovePageNumber(removedPageNumber, false, true);
}
}
}
}

@Override
protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
throws IOException
throws IOException
{
try {
// keep track of the fact that we are actively allocating a page for our
@@ -882,7 +891,7 @@ public class UsageMap
// load this map, which is fine because we should only add pages which
// represent the size of the database currently in use).
int dataStart = getFormat().OFFSET_USAGE_MAP_PAGE_DATA;
ByteUtil.fillRange(mapPageBuffer, dataStart,
ByteUtil.fillRange(mapPageBuffer, dataStart,
getFormat().PAGE_SIZE - dataStart);

int maxPagesPerUmapPage = getMaxPagesPerUsagePage();
@@ -924,7 +933,7 @@ public class UsageMap
public UsageMap getUsageMap() {
return UsageMap.this;
}
/**
* Returns the DirHandler for the given direction
*/
@@ -938,7 +947,7 @@ public class UsageMap
*/
public boolean isUpToDate() {
return(UsageMap.this._modCount == _lastModCount);
}
}

/**
* @return valid page number if there was another page to read,
@@ -972,7 +981,7 @@ public class UsageMap
}

checkForModification();
_prevPageNumber = _curPageNumber;
_curPageNumber = handler.getAnotherPageNumber(_curPageNumber);
return _curPageNumber;
@@ -1019,7 +1028,7 @@ public class UsageMap
{
restorePosition(curPageNumber, _curPageNumber);
}
/**
* Restores a current and previous position for the cursor.
*/
@@ -1055,14 +1064,14 @@ public class UsageMap
}
return pageNumber;
}
@Override
public String toString() {
return getClass().getSimpleName() + " CurPosition " + _curPageNumber +
", PrevPosition " + _prevPageNumber;
}
/**
* Handles moving the cursor in a given direction. Separates cursor
* logic from value storage.
@@ -1072,7 +1081,7 @@ public class UsageMap
public abstract int getBeginningPageNumber();
public abstract int getEndPageNumber();
}
/**
* Handles moving the cursor forward.
*/
@@ -1093,7 +1102,7 @@ public class UsageMap
return RowIdImpl.LAST_PAGE_NUMBER;
}
}
/**
* Handles moving the cursor backward.
*/
@@ -1114,7 +1123,7 @@ public class UsageMap
return RowIdImpl.FIRST_PAGE_NUMBER;
}
}
}
}
}

Loading…
Cancel
Save