diff --git a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java index 6f86d7eea2..b75a2a545a 100644 --- a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java +++ b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java @@ -46,6 +46,7 @@ public class DBHandle { private long lastTransactionID; private volatile boolean txStarted = false; + private volatile long txStartingModCount; private boolean waitingForNewTransaction = false; private boolean reloadInProgress = false; @@ -472,6 +473,7 @@ public class DBHandle { } waitingForNewTransaction = false; txStarted = true; + txStartingModCount = getModCount(); return ++lastTransactionID; } @@ -509,7 +511,7 @@ public class DBHandle { */ private synchronized boolean doEndTransaction(long id, boolean commit) throws DBRollbackException, IOException { - if (id != lastTransactionID) { + if (!txStarted || id != lastTransactionID) { throw new IllegalStateException("Transaction id is not active"); } try { @@ -530,8 +532,10 @@ public class DBHandle { } } finally { + if (commit) { + cachedChangedState = isChanged(); + } txStarted = false; - cacheChangedState(); } return false; } @@ -762,11 +766,17 @@ public class DBHandle { /** * Determine if the underlying database has changed. - * NOTE: The returned value reflects a cached state assuming all underlaying database - * transactions, saving, etc. are facilitated by this handle object. + *
+ * While a transaction is active this value will reflect changes made during the transaction + * which could ultimately never get committed. When a transaction is not active it is a + * reflection of the committed state. + * * @return true if unsaved changes have been made. */ public boolean isChanged() { + if (txStarted) { + return cachedChangedState || txStartingModCount != getModCount(); + } return cachedChangedState; } diff --git a/Ghidra/Framework/DB/src/test/java/db/DBTest.java b/Ghidra/Framework/DB/src/test/java/db/DBTest.java index d3f0a619b3..a59d75ca86 100644 --- a/Ghidra/Framework/DB/src/test/java/db/DBTest.java +++ b/Ghidra/Framework/DB/src/test/java/db/DBTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -552,6 +552,37 @@ public class DBTest extends AbstractGenericTest { } + @Test + public void testIsChanged() throws IOException { + + assertFalse(dbh.isChanged()); + + long txId = dbh.startTransaction(); + try { + assertFalse(dbh.isChanged()); + } + finally { + dbh.endTransaction(txId, true); + } + + assertFalse(dbh.isChanged()); + + txId = dbh.startTransaction(); + try { + assertFalse(dbh.isChanged()); + + DBTestUtils.createLongKeyTable(dbh, "TABLE", DBTestUtils.SINGLE_LONG, false, false); + assertEquals(1, dbh.getTableCount()); + + assertTrue(dbh.isChanged()); + } + finally { + dbh.endTransaction(txId, true); + } + + assertTrue(dbh.isChanged()); + } + @Test public void testEvents() throws IOException {