Browse Source

Reuse previously written memo/ole values when updating other values in a row in order to reduce unnecessary data duplication

git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@595 f203690c-595d-4dc9-a70b-905162fa7fd2
tags/jackcess-1.2.6
James Ahlborn 12 years ago
parent
commit
ede3f48641

+ 0
- 2
TODO.txt View File

Missing pieces: 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) - fix long text index entries (for new general sort order)
* ??? * ???
- implement foreign key index creation & relationship creation - implement foreign key index creation & relationship creation

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

<action dev="jahlborn" type="fix" issue="3435774"> <action dev="jahlborn" type="fix" issue="3435774">
Fix problem with reading row from table with deleted/added columns. Fix problem with reading row from table with deleted/added columns.
</action> </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>
<release version="1.2.5" date="2011-10-19"> <release version="1.2.5" date="2011-10-19">
<action dev="jahlborn" type="update"> <action dev="jahlborn" type="update">

+ 53
- 22
src/java/com/healthmarketscience/jackcess/Table.java View File

import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
ByteBuffer rowBuffer = positionAtRowData(rowState, rowId); ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
requireNonDeletedRow(rowState, rowId); requireNonDeletedRow(rowState, rowId);
return getRowColumn(getFormat(), rowBuffer, column, rowState);
return getRowColumn(getFormat(), rowBuffer, column, rowState, null);
} }


/** /**
if((columnNames == null) || (columnNames.contains(column.getName()))) { if((columnNames == null) || (columnNames.contains(column.getName()))) {
// Add the value to the row data // Add the value to the row data
column.setRowValue( column.setRowValue(
rtn, getRowColumn(format, rowBuffer, column, rowState));
rtn, getRowColumn(format, rowBuffer, column, rowState, null));
} }
} }
return rtn; return rtn;
private static Object getRowColumn(JetFormat format, private static Object getRowColumn(JetFormat format,
ByteBuffer rowBuffer, ByteBuffer rowBuffer,
Column column, Column column,
RowState rowState)
RowState rowState,
Map<Column,byte[]> rawVarValues)
throws IOException throws IOException
{ {
byte[] columnData = null; byte[] columnData = null;
rowBuffer.position(colDataPos); rowBuffer.position(colDataPos);
rowBuffer.get(columnData); 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 // 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 // to update the index on row deletion. note, most of the returned
// values are immutable, except for binary data (returned as byte[]), // values are immutable, except for binary data (returned as byte[]),
handleAutoNumbersForAdd(row); handleAutoNumbersForAdd(row);
// write the row of data to a temporary buffer // 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) { if (rowData[i].limit() > getFormat().MAX_ROW_SIZE) {
throw new IOException("Row size " + rowData[i].limit() + throw new IOException("Row size " + rowData[i].limit() +
// modified) // modified)
handleAutoNumbersForUpdate(row, rowBuffer, rowState); 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 // fill in any "keep value" fields
for(Column column : _columns) { for(Column column : _columns) {
if(column.getRowValue(row) == Column.KEEP_VALUE) { if(column.getRowValue(row) == Column.KEEP_VALUE) {
column.setRowValue( column.setRowValue(
row, getRowColumn(getFormat(), rowBuffer, column, rowState));
row, getRowColumn(getFormat(), rowBuffer, column, rowState,
rawVarValues));
} }
} }


// generate new row bytes // generate new row bytes
ByteBuffer newRowData = createRow( 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) { if (newRowData.limit() > getFormat().MAX_ROW_SIZE) {
throw new IOException("Row size " + newRowData.limit() + throw new IOException("Row size " + newRowData.limit() +
" is too large"); " 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 // see if we can squeeze the new row data into the existing row
return dataPage; 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. * 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 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 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 * @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 throws IOException
{ {
buffer.putShort(_maxColumnCount); buffer.putShort(_maxColumnCount);
// only need this info if this table contains any var length data // only need this info if this table contains any var length data
if(_maxVarColumnCount > 0) { if(_maxVarColumnCount > 0) {


int maxRowSize = getFormat().MAX_ROW_SIZE;

// figure out how much space remains for var length data. first, // figure out how much space remains for var length data. first,
// account for already written space // account for already written space
maxRowSize -= buffer.position(); maxRowSize -= buffer.position();
// we have a value // we have a value
nullMask.markNotNull(varCol); 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(); maxRowSize -= varDataBuf.remaining();
if(varCol.getType().isLongValue()) { if(varCol.getType().isLongValue()) {
// we already accounted for some amount of the long value data // we already accounted for some amount of the long value data
} }


for(Column col : _autoNumColumns) { for(Column col : _autoNumColumns) {
col.setRowValue(row, getRowColumn(getFormat(), rowBuffer, col, rowState));
col.setRowValue(row, getRowColumn(getFormat(), rowBuffer, col, rowState,
null));
} }
} }



+ 1
- 2
test/src/java/com/healthmarketscience/jackcess/TableTest.java View File

throws IOException throws IOException
{ {
return _testTable.createRow( return _testTable.createRow(
row, _testTable.getFormat().MAX_ROW_SIZE,
_testTable.getPageChannel().createPageBuffer(), 0);
row, _testTable.getPageChannel().createPageBuffer());
} }


private ByteBuffer[] encodeColumns(Object... row) private ByteBuffer[] encodeColumns(Object... row)

Loading…
Cancel
Save