summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2007-11-17 04:14:00 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2007-11-17 04:14:00 +0000
commit1fcd851a9f5d780b3d78b92f89bd35a2d634b467 (patch)
tree1b6f37f25a95d3aaeb3d907f8d925f5889b39f4a /src
parent9ba0e78522e68b40c75b0cb86fe74815146ce20d (diff)
downloadjackcess-1fcd851a9f5d780b3d78b92f89bd35a2d634b467.tar.gz
jackcess-1fcd851a9f5d780b3d78b92f89bd35a2d634b467.zip
Update table row count correctly on row deletion or bulk row addition,
bug #1681954. git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@174 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src')
-rw-r--r--src/changes/changes.xml4
-rw-r--r--src/java/com/healthmarketscience/jackcess/Index.java80
-rw-r--r--src/java/com/healthmarketscience/jackcess/Table.java78
3 files changed, 127 insertions, 35 deletions
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 11d8e23..b92065b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -6,6 +6,10 @@
</properties>
<body>
<release version="1.1.10" date="TBD">
+ <action dev="jahlborn" type="fix" issue="1681954">
+ Update table row count correctly on row deletion or bulk row addition,
+ bug #1681954.
+ </action>
<action dev="jahlborn" type="update" issue="1565216">
Add experimental support for auto-number columns, feature request
#1565216.
diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java
index 0cd6689..073ce57 100644
--- a/src/java/com/healthmarketscience/jackcess/Index.java
+++ b/src/java/com/healthmarketscience/jackcess/Index.java
@@ -32,6 +32,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -42,7 +43,10 @@ import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
+
import org.apache.commons.lang.builder.CompareToBuilder;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
@@ -52,6 +56,8 @@ import org.apache.commons.lang.builder.CompareToBuilder;
*/
public class Index implements Comparable<Index> {
+ private static final Log LOG = LogFactory.getLog(Index.class);
+
/** Max number of columns in an index */
private static final int MAX_COLUMNS = 10;
@@ -182,7 +188,9 @@ public class Index implements Comparable<Index> {
/** Page number of the index data */
private int _pageNumber;
private int _parentPageNumber;
- /** Number of rows in the index */
+ /** 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;
@@ -317,9 +325,7 @@ public class Index implements Comparable<Index> {
buffer.put((byte) 0); //Unknown
byte[] entryMask = new byte[_format.SIZE_INDEX_ENTRY_MASK];
int totalSize = 0;
- Iterator iter = _entries.iterator();
- while (iter.hasNext()) {
- Entry entry = (Entry) iter.next();
+ for(Entry entry : _entries) {
int size = entry.size();
totalSize += size;
int idx = totalSize / 8;
@@ -330,9 +336,7 @@ public class Index implements Comparable<Index> {
entryMask[idx] |= (1 << (totalSize % 8));
}
buffer.put(entryMask);
- iter = _entries.iterator();
- while (iter.hasNext()) {
- Entry entry = (Entry) iter.next();
+ for(Entry entry : _entries) {
entry.write(buffer);
}
buffer.putShort(2, (short) (_format.PAGE_SIZE - buffer.position()));
@@ -498,7 +502,7 @@ public class Index implements Comparable<Index> {
/**
- * Add a row to this index
+ * Adds a row to this index
* <p>
* Forces index initialization.
*
@@ -511,10 +515,48 @@ public class Index implements Comparable<Index> {
{
// make sure we've parsed the entries
initialize();
-
+
+ ++_rowCount;
_entries.add(new Entry(row, pageNumber, rowNumber));
}
+ /**
+ * Removes a row from this index
+ * <p>
+ * Forces index initialization.
+ *
+ * @param row Row to remove
+ * @param pageNumber Page number on which the row is removed
+ * @param rowNumber Row number at which the row is removed
+ */
+ public void deleteRow(Object[] row, int pageNumber, byte rowNumber)
+ throws IOException
+ {
+ // make sure we've parsed the entries
+ initialize();
+
+ --_rowCount;
+ Entry oldEntry = new Entry(row, pageNumber, rowNumber);
+ if(!_entries.remove(oldEntry)) {
+ // the caller may have only read some of the row data, if this is the
+ // case, just search for the page/row numbers
+ boolean removed = false;
+ for(Iterator<Entry> iter = _entries.iterator(); iter.hasNext(); ) {
+ Entry entry = iter.next();
+ if((entry.getPage() == pageNumber) &&
+ (entry.getRow() == rowNumber)) {
+ iter.remove();
+ removed = true;
+ break;
+ }
+ }
+ if(!removed) {
+ LOG.warn("Failed removing index entry " + oldEntry + " for row: " +
+ Arrays.asList(row));
+ }
+ }
+ }
+
@Override
public String toString() {
StringBuilder rtn = new StringBuilder();
@@ -719,7 +761,7 @@ public class Index implements Comparable<Index> {
return new FixedEntryColumn(col);
}
- public List getEntryColumns() {
+ public List<EntryColumn> getEntryColumns() {
return _entryColumns;
}
@@ -733,9 +775,8 @@ public class Index implements Comparable<Index> {
public int size() {
int rtn = 4;
- Iterator iter = _entryColumns.iterator();
- while (iter.hasNext()) {
- rtn += ((EntryColumn) iter.next()).size();
+ for(EntryColumn entryCol : _entryColumns) {
+ rtn += entryCol.size();
}
return rtn;
}
@@ -744,9 +785,8 @@ public class Index implements Comparable<Index> {
* Write this entry into a buffer
*/
public void write(ByteBuffer buffer) throws IOException {
- Iterator iter = _entryColumns.iterator();
- while (iter.hasNext()) {
- ((EntryColumn) iter.next()).write(buffer);
+ for(EntryColumn entryCol : _entryColumns) {
+ entryCol.write(buffer);
}
buffer.put((byte) (_page >>> 16));
buffer.put((byte) (_page >>> 8));
@@ -763,15 +803,15 @@ public class Index implements Comparable<Index> {
if (this == other) {
return 0;
}
- Iterator myIter = _entryColumns.iterator();
- Iterator otherIter = other.getEntryColumns().iterator();
+ Iterator<EntryColumn> myIter = _entryColumns.iterator();
+ Iterator<EntryColumn> otherIter = other.getEntryColumns().iterator();
while (myIter.hasNext()) {
if (!otherIter.hasNext()) {
throw new IllegalArgumentException(
"Trying to compare index entries with a different number of entry columns");
}
- EntryColumn myCol = (EntryColumn) myIter.next();
- EntryColumn otherCol = (EntryColumn) otherIter.next();
+ EntryColumn myCol = myIter.next();
+ EntryColumn otherCol = otherIter.next();
int i = myCol.compareTo(otherCol);
if (i != 0) {
return i;
diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java
index 3bed3cb..6acab8d 100644
--- a/src/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/java/com/healthmarketscience/jackcess/Table.java
@@ -30,6 +30,7 @@ package com.healthmarketscience.jackcess;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -161,7 +162,7 @@ public class Table
readTableDefinition(tableBuffer);
tableBuffer = null;
- _rowState = new RowState(true);
+ _rowState = new RowState(true, _maxColumnCount);
}
/**
@@ -236,13 +237,29 @@ public class Table
// FIXME, want to make this static, but writeDataPage is not static, also, this may screw up other rowstates...
+ // see if row was already deleted
+ if(_rowState.isDeleted()) {
+ throw new IllegalStateException("Deleting already deleted row");
+ }
+
// delete flag always gets set in the "root" page (even if overflow row)
ByteBuffer rowBuffer = _rowState.getPage(_pageChannel);
- int index = getRowStartOffset(_currentRowInPage, _format);
- rowBuffer.putShort(index, (short)(rowBuffer.getShort(index)
+ int pageNumber = _rowState.getPageNumber();
+ int rowIndex = getRowStartOffset(_currentRowInPage, _format);
+ rowBuffer.putShort(rowIndex, (short)(rowBuffer.getShort(rowIndex)
| DELETED_ROW_MASK | OVERFLOW_ROW_MASK));
- writeDataPage(rowBuffer, _rowState.getPageNumber());
+ writeDataPage(rowBuffer, pageNumber);
_rowState.setDeleted(true);
+
+ // update the indexes
+ for(Index index : _indexes) {
+ index.deleteRow(_rowState.getRowValues(), pageNumber,
+ (byte)_currentRowInPage);
+ }
+
+ // make sure table def gets updated
+ --_rowCount;
+ updateTableDefinition();
}
/**
@@ -265,7 +282,7 @@ public class Table
return null;
}
- return getRow(rowBuffer, getRowNullMask(rowBuffer), _columns,
+ return getRow(_rowState, rowBuffer, getRowNullMask(rowBuffer), _columns,
columnNames);
}
@@ -312,7 +329,7 @@ public class Table
return null;
}
- return getRow(rowBuffer, getRowNullMask(rowBuffer), columns,
+ return getRow(rowState, rowBuffer, getRowNullMask(rowBuffer), columns,
columnNames);
}
@@ -320,6 +337,7 @@ public class Table
* Reads the row data from the given row buffer. Leaves limit unchanged.
*/
private static Map<String, Object> getRow(
+ RowState rowState,
ByteBuffer rowBuffer,
NullMask nullMask,
Collection<Column> columns,
@@ -329,10 +347,18 @@ public class Table
Map<String, Object> rtn = new LinkedHashMap<String, Object>(
columns.size());
for(Column column : columns) {
+ Object value = null;
if((columnNames == null) || (columnNames.contains(column.getName()))) {
// Add the value to the row data
- rtn.put(column.getName(), getRowColumn(rowBuffer, nullMask, column));
+ value = getRowColumn(rowBuffer, nullMask, column);
+ rtn.put(column.getName(), value);
}
+
+ // 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[]), but binary data shouldn't be
+ // indexed anyway.
+ rowState._rowValues[column.getColumnNumber()] = value;
}
return rtn;
@@ -1033,26 +1059,40 @@ public class Table
dataPage.put(rowData[i]);
// update the indexes
- Iterator<Index> indIter = _indexes.iterator();
- while (indIter.hasNext()) {
- Index index = indIter.next();
- index.addRow(rows.get(i), pageNumber, (byte) rowNum);
+ for(Index index : _indexes) {
+ index.addRow(rows.get(i), pageNumber, (byte)rowNum);
}
}
writeDataPage(dataPage, pageNumber);
//Update tdef page
+ _rowCount += rows.size();
+ updateTableDefinition();
+ }
+
+ /**
+ * Updates the table definition after rows are modified.
+ */
+ private void updateTableDefinition() throws IOException
+ {
+ // load table definition
ByteBuffer tdefPage = _pageChannel.createPageBuffer();
_pageChannel.readPage(tdefPage, _tableDefPageNumber);
- tdefPage.putInt(_format.OFFSET_NUM_ROWS, ++_rowCount);
+
+ // make sure rowcount and autonumber are up-to-date
+ tdefPage.putInt(_format.OFFSET_NUM_ROWS, _rowCount);
tdefPage.putInt(_format.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);
+ (i * _format.SIZE_INDEX_DEFINITION) + 4, _rowCount);
Index index = indIter.next();
index.update();
}
+
+ // write modified table definition
_pageChannel.writePage(tdefPage, _tableDefPageNumber);
}
@@ -1393,15 +1433,19 @@ public class Table
/** the row buffer which contains the final data (after following any
overflow pointers) */
public ByteBuffer _finalRowBuffer;
-
- public RowState(boolean hardRowBuffer) {
+ /** values read from the last row */
+ public Object[] _rowValues;
+
+ public RowState(boolean hardRowBuffer, int colCount) {
_rowBufferH = TempPageHolder.newHolder(hardRowBuffer);
+ _rowValues = new Object[colCount];
}
public void reset() {
_finalRowBuffer = null;
_deleted = false;
_overflow = false;
+ Arrays.fill(_rowValues, null);
}
public int getPageNumber() {
@@ -1430,6 +1474,10 @@ public class Table
return _overflow;
}
+ public Object[] getRowValues() {
+ return _rowValues;
+ }
+
public void possiblyInvalidate(int modifiedPageNumber,
ByteBuffer modifiedBuffer) {
_rowBufferH.possiblyInvalidate(modifiedPageNumber,