throws DatabaseException {
Database priDb1 = openPrimary("pri1");
Database priDb2 = openPrimary("pri2");
SecondaryDatabase secDb1 = openSecondary(priDb1, "sec1", null, null);
SecondaryDatabase secDb2 = openSecondary(priDb2, "sec2", priDb1,
onDelete);
OperationStatus status;
DatabaseEntry data = new DatabaseEntry();
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry pkey = new DatabaseEntry();
Transaction txn = txnBegin();
/*
* pri1 has a record with primary key 1 and index key 3.
* pri2 has a record with primary key 2 and foreign key 1,
* which is the primary key of pri1.
* pri2 has another record with primary key 3 and foreign key 1,
* to enable testing cascade and nullify for secondary duplicates.
*/
/* Add three records. */
status = priDb1.put(txn, entry(1), entry(3));
assertEquals(OperationStatus.SUCCESS, status);
status = priDb2.put(txn, entry(2), entry(1));
assertEquals(OperationStatus.SUCCESS, status);
status = priDb2.put(txn, entry(3), entry(1));
assertEquals(OperationStatus.SUCCESS, status);
/* Verify record data. */
status = priDb1.get(txn, entry(1), data, LockMode.DEFAULT);
assertEquals(OperationStatus.SUCCESS, status);
assertEquals(3, val(data));
status = secDb1.get(txn, entry(3), data, LockMode.DEFAULT);
assertEquals(OperationStatus.SUCCESS, status);
assertEquals(3, val(data));
status = priDb2.get(txn, entry(2), data, LockMode.DEFAULT);
assertEquals(OperationStatus.SUCCESS, status);
assertEquals(1, val(data));
status = priDb2.get(txn, entry(3), data, LockMode.DEFAULT);
assertEquals(OperationStatus.SUCCESS, status);
assertEquals(1, val(data));
SecondaryCursor cursor = secDb2.openSecondaryCursor(txn, null);
status = cursor.getFirst(key, pkey, data, LockMode.DEFAULT);
assertEquals(OperationStatus.SUCCESS, status);
assertEquals(1, val(key));
assertEquals(2, val(pkey));
assertEquals(1, val(data));
status = cursor.getNext(key, pkey, data, LockMode.DEFAULT);
assertEquals(OperationStatus.SUCCESS, status);
assertEquals(1, val(key));
assertEquals(3, val(pkey));
assertEquals(1, val(data));
status = cursor.getNext(key, pkey, data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
cursor.close();
txnCommit(txn);
txn = txnBegin();
/* Test delete action. */
if (onDelete == ForeignKeyDeleteAction.ABORT) {
/* Test that we abort trying to delete a referenced key. */
try {
status = priDb1.delete(txn, entry(1));
fail();
} catch (DatabaseException expected) {
txnAbort(txn);
txn = txnBegin();
}
/* Test that we can put a record into pri2 with a null foreign key
* value. */
status = priDb2.put(txn, entry(2), entry(0));
assertEquals(OperationStatus.SUCCESS, status);
status = priDb2.put(txn, entry(3), entry(0));
assertEquals(OperationStatus.SUCCESS, status);
/* The sec2 records should not be present since the key was set
* to null above. */
status = secDb2.get(txn, entry(1), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
/* Test that now we can delete the record in pri1, since it is no
* longer referenced. */
status = priDb1.delete(txn, entry(1));
assertEquals(OperationStatus.SUCCESS, status);
status = priDb1.get(txn, entry(1), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
status = secDb1.get(txn, entry(3), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
} else if (onDelete == ForeignKeyDeleteAction.NULLIFY) {
/* Delete the referenced key. */
status = priDb1.delete(txn, entry(1));
assertEquals(OperationStatus.SUCCESS, status);
status = priDb1.get(txn, entry(1), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
status = secDb1.get(txn, entry(3), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
/* The pri2 records should still exist, but should have a zero/null
* secondary key since it was nullified. */
status = priDb2.get(txn, entry(2), data, LockMode.DEFAULT);
assertEquals(OperationStatus.SUCCESS, status);
assertEquals(0, val(data));
status = priDb2.get(txn, entry(3), data, LockMode.DEFAULT);
assertEquals(OperationStatus.SUCCESS, status);
assertEquals(0, val(data));
status = secDb2.get(txn, entry(1), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
} else if (onDelete == ForeignKeyDeleteAction.CASCADE) {
/* Delete the referenced key. */
status = priDb1.delete(txn, entry(1));
assertEquals(OperationStatus.SUCCESS, status);
status = priDb1.get(txn, entry(1), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
status = secDb1.get(txn, entry(3), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
/* The pri2 records should have deleted also. */
status = priDb2.get(txn, entry(2), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
status = priDb2.get(txn, entry(3), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
status = secDb2.get(txn, entry(1), data, LockMode.DEFAULT);
assertEquals(OperationStatus.NOTFOUND, status);
} else {
throw new IllegalStateException();
}
/*
* Test that a foreign key value may not be used that is not present
* in the foreign db. Key 2 is not in pri1 in this case.
*/
try {
status = priDb2.put(txn, entry(3), entry(2));
fail();
} catch (DatabaseException expected) { }
txnCommit(txn);
secDb1.close();
secDb2.close();
priDb1.close();
priDb2.close();
}