summaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java')
-rw-r--r--src/java/com/healthmarketscience/jackcess/Column.java84
-rw-r--r--src/java/com/healthmarketscience/jackcess/Table.java30
2 files changed, 85 insertions, 29 deletions
diff --git a/src/java/com/healthmarketscience/jackcess/Column.java b/src/java/com/healthmarketscience/jackcess/Column.java
index 76012ef..528ce12 100644
--- a/src/java/com/healthmarketscience/jackcess/Column.java
+++ b/src/java/com/healthmarketscience/jackcess/Column.java
@@ -771,20 +771,20 @@ public class Column implements Comparable<Column> {
def.put(value);
} else {
+ TempPageHolder lvalBufferH = getTable().getLongValueBuffer();
+ ByteBuffer lvalPage = null;
int firstLvalPageNum = PageChannel.INVALID_PAGE_NUMBER;
byte firstLvalRow = 0;
- ByteBuffer lvalPage = getPageChannel().createPageBuffer();
-
// write other page(s)
switch(type) {
case LONG_VALUE_TYPE_OTHER_PAGE:
- writeLongValueHeader(lvalPage);
- firstLvalRow = (byte)Table.addDataPageRow(lvalPage,
- value.length,
+ lvalPage = getLongValuePage(value.length, lvalBufferH);
+ firstLvalPageNum = lvalBufferH.getPageNumber();
+ firstLvalRow = (byte)Table.addDataPageRow(lvalPage, value.length,
getFormat());
lvalPage.put(value);
- firstLvalPageNum = getPageChannel().writeNewPage(lvalPage);
+ getPageChannel().writePage(lvalPage, firstLvalPageNum);
break;
case LONG_VALUE_TYPE_OTHER_PAGES:
@@ -792,22 +792,35 @@ public class Column implements Comparable<Column> {
ByteBuffer buffer = ByteBuffer.wrap(value);
int remainingLen = buffer.remaining();
buffer.limit(0);
- int lvalPageNum = getPageChannel().allocateNewPage();
- byte lvalRow = 0;
+ lvalPage = getLongValuePage(getFormat().MAX_LONG_VALUE_ROW_SIZE,
+ lvalBufferH);
+ firstLvalPageNum = lvalBufferH.getPageNumber();
+ int lvalPageNum = firstLvalPageNum;
+ ByteBuffer nextLvalPage = null;
int nextLvalPageNum = 0;
while(remainingLen > 0) {
lvalPage.clear();
- writeLongValueHeader(lvalPage);
- // figure out how much we will put in this page
+ // figure out how much we will put in this page (we need 4 bytes for
+ // the next page pointer)
int chunkLength = Math.min(getFormat().MAX_LONG_VALUE_ROW_SIZE - 4,
remainingLen);
- nextLvalPageNum = ((chunkLength < remainingLen) ?
- getPageChannel().allocateNewPage() : 0);
+
+ // figure out if we will need another page, and if so, allocate it
+ if(chunkLength < remainingLen) {
+ // force a new page to be allocated
+ lvalBufferH.clear();
+ nextLvalPage = getLongValuePage(
+ getFormat().MAX_LONG_VALUE_ROW_SIZE, lvalBufferH);
+ nextLvalPageNum = lvalBufferH.getPageNumber();
+ } else {
+ nextLvalPage = null;
+ nextLvalPageNum = 0;
+ }
// add row to this page
- lvalRow = (byte)Table.addDataPageRow(lvalPage, chunkLength + 4,
- getFormat());
+ byte lvalRow = (byte)Table.addDataPageRow(lvalPage, chunkLength + 4,
+ getFormat());
// write next page info (we'll always be writing into row 0 for
// newly created pages)
@@ -821,14 +834,20 @@ public class Column implements Comparable<Column> {
// write new page to database
getPageChannel().writePage(lvalPage, lvalPageNum);
-
- // hang onto first page info
- if(firstLvalPageNum == PageChannel.INVALID_PAGE_NUMBER) {
- firstLvalPageNum = lvalPageNum;
+
+ if(lvalPageNum == firstLvalPageNum) {
+ // save initial row info
firstLvalRow = lvalRow;
+ } else {
+ // check assertion that we wrote to row 0 for all subsequent pages
+ if(lvalRow != (byte)0) {
+ throw new IllegalStateException("Expected row 0, but was " +
+ lvalRow);
+ }
}
-
+
// move to next page
+ lvalPage = nextLvalPage;
lvalPageNum = nextLvalPageNum;
}
break;
@@ -855,14 +874,35 @@ public class Column implements Comparable<Column> {
{
lvalPage.put(PageTypes.DATA); //Page type
lvalPage.put((byte) 1); //Unknown
- lvalPage.putShort((short) (getFormat().PAGE_SIZE -
- getFormat().OFFSET_ROW_START)); //Free space
+ lvalPage.putShort((short)getFormat().PAGE_INITIAL_FREE_SPACE); //Free space
lvalPage.put((byte) 'L');
lvalPage.put((byte) 'V');
lvalPage.put((byte) 'A');
lvalPage.put((byte) 'L');
- lvalPage.putShort((short)0); // num rows in page
lvalPage.putInt(0); //unknown
+ lvalPage.putShort((short)0); // num rows in page
+ }
+
+ /**
+ * Returns a long value data page with space for data of the given length.
+ */
+ private ByteBuffer getLongValuePage(int dataLength,
+ TempPageHolder lvalBufferH)
+ throws IOException
+ {
+ ByteBuffer lvalPage = null;
+ if(lvalBufferH.getPageNumber() != PageChannel.INVALID_PAGE_NUMBER) {
+ lvalPage = lvalBufferH.getPage(getPageChannel());
+ if(Table.rowFitsOnDataPage(dataLength, lvalPage, getFormat())) {
+ // the current page has space
+ return lvalPage;
+ }
+ }
+
+ // need new page
+ lvalPage = lvalBufferH.setNewPage(getPageChannel());
+ writeLongValueHeader(lvalPage);
+ return lvalPage;
}
/**
diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java
index 35c9e72..b4424dc 100644
--- a/src/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/java/com/healthmarketscience/jackcess/Table.java
@@ -122,6 +122,9 @@ public class Table
every call) */
private final TempBufferHolder _multiRowBufferH =
TempBufferHolder.newHolder(TempBufferHolder.Type.NONE, true);
+ /** page buffer used to write out-of-line "long value" data */
+ private final TempPageHolder _longValueBufferH =
+ TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
/** for now, "big index support" is optional */
private final boolean _useBigIndex;
@@ -224,6 +227,10 @@ public class Table
return _ownedPages.cursor();
}
+ protected TempPageHolder getLongValueBuffer() {
+ return _longValueBufferH;
+ }
+
/**
* @return All of the columns in this table (unmodifiable List)
*/
@@ -1217,11 +1224,7 @@ public class Table
for (int i = 0; i < rowData.length; i++) {
int rowSize = rowData[i].remaining();
- int rowSpaceUsage = getRowSpaceUsage(rowSize, getFormat());
- short freeSpaceInPage = dataPage.getShort(getFormat().OFFSET_FREE_SPACE);
- int rowsOnPage = getRowsOnDataPage(dataPage, getFormat());
- if((freeSpaceInPage < rowSpaceUsage) ||
- (rowsOnPage >= getFormat().MAX_NUM_ROWS_ON_DATA_PAGE)) {
+ if(!rowFitsOnDataPage(rowSize, dataPage, getFormat())) {
// Last data page is full. Create a new one.
writeDataPage(dataPage, pageNumber);
@@ -1229,8 +1232,6 @@ public class Table
dataPage = newDataPage();
pageNumber = _addRowBufferH.getPageNumber();
-
- freeSpaceInPage = dataPage.getShort(getFormat().OFFSET_FREE_SPACE);
}
// write out the row data
@@ -1643,6 +1644,21 @@ public class Table
}
/**
+ * Returns {@code true} if a row of the given size will fit on the given
+ * data page, {@code false} otherwise.
+ */
+ public static boolean rowFitsOnDataPage(
+ int rowLength, ByteBuffer dataPage, JetFormat format)
+ throws IOException
+ {
+ int rowSpaceUsage = getRowSpaceUsage(rowLength, format);
+ short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
+ int rowsOnPage = getRowsOnDataPage(dataPage, format);
+ return ((rowSpaceUsage <= freeSpaceInPage) &&
+ (rowsOnPage < format.MAX_NUM_ROWS_ON_DATA_PAGE));
+ }
+
+ /**
* Duplicates and returns a row of data, optionally with a longer length
* filled with {@code null}.
*/