aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO.txt2
-rw-r--r--src/changes/changes.xml4
-rw-r--r--src/java/com/healthmarketscience/jackcess/Table.java75
-rw-r--r--test/src/java/com/healthmarketscience/jackcess/TableTest.java3
4 files changed, 58 insertions, 26 deletions
diff --git a/TODO.txt b/TODO.txt
index 7c9802e..225fc0f 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,7 +1,5 @@
Missing pieces:
-- re-use memo/ole data in "other page(s)" when updating rows
- * MEDIUM
- fix long text index entries (for new general sort order)
* ???
- implement foreign key index creation & relationship creation
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index df41751..17dacb2 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -16,6 +16,10 @@
<action dev="jahlborn" type="fix" issue="3435774">
Fix problem with reading row from table with deleted/added columns.
</action>
+ <action dev="jahlborn" type="update">
+ Reuse previously written memo/ole values when updating other values in
+ a row in order to reduce unnecessary data duplication.
+ </action>
</release>
<release version="1.2.5" date="2011-10-19">
<action dev="jahlborn" type="update">
diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java
index a868317..e11c7f9 100644
--- a/src/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/java/com/healthmarketscience/jackcess/Table.java
@@ -35,6 +35,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
@@ -598,7 +599,7 @@ public class Table
ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
requireNonDeletedRow(rowState, rowId);
- return getRowColumn(getFormat(), rowBuffer, column, rowState);
+ return getRowColumn(getFormat(), rowBuffer, column, rowState, null);
}
/**
@@ -638,7 +639,7 @@ public class Table
if((columnNames == null) || (columnNames.contains(column.getName()))) {
// Add the value to the row data
column.setRowValue(
- rtn, getRowColumn(format, rowBuffer, column, rowState));
+ rtn, getRowColumn(format, rowBuffer, column, rowState, null));
}
}
return rtn;
@@ -651,7 +652,8 @@ public class Table
private static Object getRowColumn(JetFormat format,
ByteBuffer rowBuffer,
Column column,
- RowState rowState)
+ RowState rowState,
+ Map<Column,byte[]> rawVarValues)
throws IOException
{
byte[] columnData = null;
@@ -716,6 +718,11 @@ public class Table
rowBuffer.position(colDataPos);
rowBuffer.get(columnData);
+ if((rawVarValues != null) && column.isVariableLength()) {
+ // caller wants raw value as well
+ rawVarValues.put(column, columnData);
+ }
+
// parse the column data. we cache the row values in order to be able
// to update the index on row deletion. note, most of the returned
// values are immutable, except for binary data (returned as byte[]),
@@ -1453,9 +1460,7 @@ public class Table
handleAutoNumbersForAdd(row);
// write the row of data to a temporary buffer
- rowData[i] = createRow(row, getFormat().MAX_ROW_SIZE,
- writeRowBufferH.getPageBuffer(getPageChannel()),
- 0);
+ rowData[i] = createRow(row, writeRowBufferH.getPageBuffer(getPageChannel()));
if (rowData[i].limit() > getFormat().MAX_ROW_SIZE) {
throw new IOException("Row size " + rowData[i].limit() +
@@ -1532,30 +1537,38 @@ public class Table
// modified)
handleAutoNumbersForUpdate(row, rowBuffer, rowState);
+ // hang on to the raw values of var length columns we are "keeping". this
+ // will allow us to re-use pre-written var length data, which can save
+ // space for things like long value columns.
+ Map<Column,byte[]> rawVarValues =
+ (!_varColumns.isEmpty() ? new HashMap<Column,byte[]>() : null);
+
// fill in any "keep value" fields
for(Column column : _columns) {
if(column.getRowValue(row) == Column.KEEP_VALUE) {
column.setRowValue(
- row, getRowColumn(getFormat(), rowBuffer, column, rowState));
+ row, getRowColumn(getFormat(), rowBuffer, column, rowState,
+ rawVarValues));
}
}
// generate new row bytes
ByteBuffer newRowData = createRow(
- row, getFormat().MAX_ROW_SIZE,
- _singleRowBufferH.getPageBuffer(getPageChannel()), oldRowSize);
+ row, _singleRowBufferH.getPageBuffer(getPageChannel()), oldRowSize,
+ rawVarValues);
if (newRowData.limit() > getFormat().MAX_ROW_SIZE) {
throw new IOException("Row size " + newRowData.limit() +
" is too large");
}
- Object[] oldRowValues = (!_indexDatas.isEmpty() ?
- rowState.getRowValues() : null);
+ if(!_indexDatas.isEmpty()) {
+ Object[] oldRowValues = rowState.getRowValues();
- // delete old values from indexes
- for(IndexData indexData : _indexDatas) {
- indexData.deleteRow(oldRowValues, rowId);
+ // delete old values from indexes
+ for(IndexData indexData : _indexDatas) {
+ indexData.deleteRow(oldRowValues, rowId);
+ }
}
// see if we can squeeze the new row data into the existing row
@@ -1724,19 +1737,24 @@ public class Table
return dataPage;
}
+ ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer)
+ throws IOException
+ {
+ return createRow(rowArray, buffer, 0, Collections.<Column,byte[]>emptyMap());
+ }
+
/**
* Serialize a row of Objects into a byte buffer.
- * <p>
- * Note, if this table has an auto-number column, the value written will be
- * put back into the given row array.
*
* @param rowArray row data, expected to be correct length for this table
- * @param maxRowSize max size the data can be for this row
* @param buffer buffer to which to write the row data
+ * @param minRowSize min size for result row
+ * @param rawVarValues optional, pre-written values for var length columns
+ * (enables re-use of previously written values).
* @return the given buffer, filled with the row data
*/
- ByteBuffer createRow(Object[] rowArray, int maxRowSize, ByteBuffer buffer,
- int minRowSize)
+ private ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer,
+ int minRowSize, Map<Column,byte[]> rawVarValues)
throws IOException
{
buffer.putShort(_maxColumnCount);
@@ -1791,6 +1809,8 @@ public class Table
// only need this info if this table contains any var length data
if(_maxVarColumnCount > 0) {
+ int maxRowSize = getFormat().MAX_ROW_SIZE;
+
// figure out how much space remains for var length data. first,
// account for already written space
maxRowSize -= buffer.position();
@@ -1818,7 +1838,17 @@ public class Table
// we have a value
nullMask.markNotNull(varCol);
- ByteBuffer varDataBuf = varCol.write(rowValue, maxRowSize);
+ byte[] rawValue = null;
+ ByteBuffer varDataBuf = null;
+ if(((rawValue = rawVarValues.get(varCol)) != null) &&
+ (rawValue.length <= maxRowSize)) {
+ // save time and potentially db space, re-use raw value
+ varDataBuf = ByteBuffer.wrap(rawValue);
+ } else {
+ // write column value
+ varDataBuf = varCol.write(rowValue, maxRowSize);
+ }
+
maxRowSize -= varDataBuf.remaining();
if(varCol.getType().isLongValue()) {
// we already accounted for some amount of the long value data
@@ -1880,7 +1910,8 @@ public class Table
}
for(Column col : _autoNumColumns) {
- col.setRowValue(row, getRowColumn(getFormat(), rowBuffer, col, rowState));
+ col.setRowValue(row, getRowColumn(getFormat(), rowBuffer, col, rowState,
+ null));
}
}
diff --git a/test/src/java/com/healthmarketscience/jackcess/TableTest.java b/test/src/java/com/healthmarketscience/jackcess/TableTest.java
index 7672e96..43f092f 100644
--- a/test/src/java/com/healthmarketscience/jackcess/TableTest.java
+++ b/test/src/java/com/healthmarketscience/jackcess/TableTest.java
@@ -120,8 +120,7 @@ public class TableTest extends TestCase {
throws IOException
{
return _testTable.createRow(
- row, _testTable.getFormat().MAX_ROW_SIZE,
- _testTable.getPageChannel().createPageBuffer(), 0);
+ row, _testTable.getPageChannel().createPageBuffer());
}
private ByteBuffer[] encodeColumns(Object... row)