Browse Source

attempt to restore autonumbers on some add row failures

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

+ 30
- 0
src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java View File

@@ -1993,6 +1993,11 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
*/
public abstract Object getNext(Object prevRowValue);

/**
* Restores a previous autonumber generated by this generator.
*/
public abstract void restoreLast(Object last);
/**
* Returns the type of values generated by this generator.
*/
@@ -2015,6 +2020,13 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return getTable().getNextLongAutoNumber();
}

@Override
public void restoreLast(Object last) {
if(last instanceof Integer) {
getTable().restoreLastLongAutoNumber((Integer)last);
}
}
@Override
public DataType getType() {
return DataType.LONG;
@@ -2039,6 +2051,11 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return _lastAutoNumber;
}

@Override
public void restoreLast(Object last) {
_lastAutoNumber = null;
}
@Override
public DataType getType() {
return DataType.GUID;
@@ -2066,6 +2083,14 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
return new ComplexValueForeignKeyImpl(ColumnImpl.this, nextComplexAutoNum);
}

@Override
public void restoreLast(Object last) {
if(last instanceof ComplexValueForeignKey) {
getTable().restoreLastComplexTypeAutoNumber(
((ComplexValueForeignKey)last).get());
}
}
@Override
public DataType getType() {
return DataType.COMPLEX_TYPE;
@@ -2090,6 +2115,11 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
throw new UnsupportedOperationException();
}

@Override
public void restoreLast(Object last) {
throw new UnsupportedOperationException();
}
@Override
public DataType getType() {
return _genType;

+ 37
- 1
src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java View File

@@ -1503,6 +1503,7 @@ public class TableImpl implements Table
ByteBuffer dataPage = null;
int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
int updateCount = 0;
int autoNumAssignCount = 0;
try {

List<Object[]> dupeRows = null;
@@ -1529,6 +1530,7 @@ public class TableImpl implements Table

// fill in autonumbers
handleAutoNumbersForAdd(row);
++autoNumAssignCount;
// write the row of data to a temporary buffer
ByteBuffer rowData = createRow(
@@ -1592,6 +1594,14 @@ public class TableImpl implements Table

} catch(Exception rowWriteFailure) {

boolean isWriteFailure = isWriteFailure(rowWriteFailure);

if(!isWriteFailure && (autoNumAssignCount > updateCount)) {
// we assigned some autonumbers which won't get written. attempt to
// recover them so we don't get ugly "holes"
restoreAutoNumbersFromAdd(rows.get(autoNumAssignCount - 1));
}
if(!isBatchWrite) {
// just re-throw the original exception
if(rowWriteFailure instanceof IOException) {
@@ -1601,7 +1611,7 @@ public class TableImpl implements Table
}

// attempt to resolve a partial batch write
if(isWriteFailure(rowWriteFailure)) {
if(isWriteFailure) {

// we don't really know the status of any of the rows, so clear the
// update count
@@ -2149,6 +2159,22 @@ public class TableImpl implements Table
}
}

/**
* Restores all autonumber column values from a failed add row.
*/
private void restoreAutoNumbersFromAdd(Object[] row)
throws IOException
{
if(_autoNumColumns.isEmpty()) {
return;
}

for(ColumnImpl col : _autoNumColumns) {
// restore the last value from the row
col.getAutoNumberGenerator().restoreLast(col.getRowValue(row));
}
}

private static void padRowBuffer(ByteBuffer buffer, int minRowSize,
int trailerSize)
{
@@ -2174,6 +2200,11 @@ public class TableImpl implements Table
// gets the last used auto number (does not modify)
return _lastLongAutoNumber;
}

void restoreLastLongAutoNumber(int lastLongAutoNumber) {
// restores the last used auto number
_lastLongAutoNumber = lastLongAutoNumber - 1;
}
int getNextComplexTypeAutoNumber() {
// note, the saved value is the last one handed out, so pre-increment
@@ -2184,6 +2215,11 @@ public class TableImpl implements Table
// gets the last used auto number (does not modify)
return _lastComplexTypeAutoNumber;
}

void restoreLastComplexTypeAutoNumber(int lastComplexTypeAutoNumber) {
// restores the last used auto number
_lastComplexTypeAutoNumber = lastComplexTypeAutoNumber - 1;
}
@Override
public String toString() {

+ 75
- 0
src/test/java/com/healthmarketscience/jackcess/IndexTest.java View File

@@ -593,6 +593,81 @@ public class IndexTest extends TestCase {
}
}

public void testAutoNumberRecover() throws Exception
{
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
Database db = create(fileFormat);

Table t = new TableBuilder("TestTable")
.addColumn(new ColumnBuilder("id", DataType.LONG).setAutoNumber(true))
.addColumn(new ColumnBuilder("data", DataType.TEXT))
.addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
.addColumns("id").setPrimaryKey())
.addIndex(new IndexBuilder("data_ind")
.addColumns("data").setUnique())
.toTable(db);

for(int i = 1; i < 3; ++i) {
t.addRow(null, "row" + i);
}

try {
t.addRow(null, "row1");
fail("ConstraintViolationException should have been thrown");
} catch(ConstraintViolationException ce) {
// success
}
t.addRow(null, "row3");

assertEquals(3, t.getRowCount());

List<Row> expectedRows =
createExpectedTable(
createExpectedRow(
"id", 1, "data", "row1"),
createExpectedRow(
"id", 2, "data", "row2"),
createExpectedRow(
"id", 3, "data", "row3"));

assertTable(expectedRows, t);

IndexCursor pkCursor = CursorBuilder.createCursor(t.getPrimaryKeyIndex());
assertCursor(expectedRows, pkCursor);

assertCursor(expectedRows,
CursorBuilder.createCursor(t.getIndex("data_ind")));

List<Object[]> batch = new ArrayList<Object[]>();
batch.add(new Object[]{null, "row4"});
batch.add(new Object[]{null, "row5"});
batch.add(new Object[]{null, "row3"});

try {
t.addRows(batch);
fail("BatchUpdateException should have been thrown");
} catch(BatchUpdateException be) {
// success
assertTrue(be.getCause() instanceof ConstraintViolationException);
assertEquals(2, be.getUpdateCount());
}

expectedRows = new ArrayList<Row>(expectedRows);
expectedRows.add(createExpectedRow("id", 4, "data", "row4"));
expectedRows.add(createExpectedRow("id", 5, "data", "row5"));

assertTable(expectedRows, t);
assertCursor(expectedRows, pkCursor);

assertCursor(expectedRows,
CursorBuilder.createCursor(t.getIndex("data_ind")));

db.close();
}
}
private void doCheckForeignKeyIndex(Table ta, Index ia, Table tb)
throws Exception
{

Loading…
Cancel
Save