diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/DatabaseType.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/DatabaseType.java
index 045ba27..74438c0 100644
--- a/src/main/java/com/tacitknowledge/util/migration/jdbc/DatabaseType.java
+++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/DatabaseType.java
@@ -30,6 +30,7 @@
*
* - patches.create - DDL that creates the patches table
* - level.create - SQL that inserts a new patch level record for the system
+ * - level.count - SQL that counts patches for the system
* - level.read - SQL that selects the current patch level of the system
* - level.update - SQL that updates the current patch level of the system
* - lock.read - Returns 'T' if the system patch lock is in use, 'F' otherwise
diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/PatchTable.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/PatchTable.java
index 7f9b761..be3a5a1 100644
--- a/src/main/java/com/tacitknowledge/util/migration/jdbc/PatchTable.java
+++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/PatchTable.java
@@ -131,9 +131,6 @@ public void createPatchStoreIfNeeded() throws MigrationException
}
stmt.execute();
context.commit();
-
- // We don't yet have a patch record for this system; create one
- createSystemPatchRecord();
}
catch (SQLException sqle)
{
@@ -150,6 +147,20 @@ public void createPatchStoreIfNeeded() throws MigrationException
{
SqlUtil.close(conn, stmt, rs);
}
+
+ try {
+ // We don't yet have a patch record for this system; create one
+ createSystemPatchRecord();
+ }
+ catch (Exception ex)
+ {
+ throw new MigrationException("Unexpected exception while creating system record.", ex);
+ }
+ finally
+ {
+ SqlUtil.close(conn, stmt, rs);
+ }
+
}
/**
@@ -361,14 +372,26 @@ private void createSystemPatchRecord() throws MigrationException, SQLException
String systemName = context.getSystemName();
Connection conn = null;
PreparedStatement stmt = null;
+ ResultSet rs = null;
try
{
conn = context.getConnection();
- stmt = conn.prepareStatement(getSql("level.create"));
+ stmt = conn.prepareStatement(getSql("level.count"));
stmt.setString(1, systemName);
- stmt.execute();
- context.commit();
- log.info("Created patch record for " + systemName);
+ rs = stmt.executeQuery();
+ rs.next();
+ if (rs.getInt(1) == 0)
+ {
+ SqlUtil.close(null, stmt, null);
+ stmt = conn.prepareStatement(getSql("level.create"));
+ stmt.setString(1, systemName);
+ stmt.execute();
+ context.commit();
+ log.info("Created patch record for " + systemName);
+ } else
+ {
+ log.debug("Patch record already existed for " + systemName);
+ }
}
catch (SQLException e)
{
@@ -377,7 +400,7 @@ private void createSystemPatchRecord() throws MigrationException, SQLException
}
finally
{
- SqlUtil.close(conn, stmt, null);
+ SqlUtil.close(conn, stmt, rs);
}
}
@@ -402,7 +425,7 @@ private boolean updatePatchLock(boolean lock) throws MigrationException
stmt = conn.prepareStatement(getSql(sqlkey));
if (log.isDebugEnabled())
{
- log.debug("Updating patch table lock: " + getSql(sqlkey));
+ log.debug("Updating patch table lock for system " + context.getSystemName() + ": " + getSql(sqlkey));
}
stmt.setString(1, context.getSystemName());
if (lock)
diff --git a/src/main/resources/com/tacitknowledge/util/migration/jdbc/hsqldb.properties b/src/main/resources/com/tacitknowledge/util/migration/jdbc/hsqldb.properties
index 79d816a..4a9cb44 100644
--- a/src/main/resources/com/tacitknowledge/util/migration/jdbc/hsqldb.properties
+++ b/src/main/resources/com/tacitknowledge/util/migration/jdbc/hsqldb.properties
@@ -9,6 +9,7 @@ patches.create=CREATE TABLE patches ( \
# Validates that a record exists for a given system
level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0)
+level.count=SELECT COUNT(*) FROM patches WHERE system_name = ?
level.table.exists=SELECT patch_level FROM patches WHERE system_name = ?
level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ?
level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ?
diff --git a/src/main/resources/com/tacitknowledge/util/migration/jdbc/mysql.properties b/src/main/resources/com/tacitknowledge/util/migration/jdbc/mysql.properties
index 058dfc8..5bf99a6 100644
--- a/src/main/resources/com/tacitknowledge/util/migration/jdbc/mysql.properties
+++ b/src/main/resources/com/tacitknowledge/util/migration/jdbc/mysql.properties
@@ -9,6 +9,7 @@ patches.create=CREATE TABLE IF NOT EXISTS patches ( \
# Validates that a record exists for a given system
level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0 )
+level.count=SELECT COUNT(*) FROM patches WHERE system_name = ?
level.table.exists=SELECT patch_level FROM patches WHERE system_name = ?
level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ?
level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ?
@@ -21,4 +22,4 @@ patches.all=SELECT patch_level FROM patches WHERE system_name = ?
# the system is currently locked.
lock.read=SELECT patch_in_progress FROM patches WHERE system_name = ? AND ( patch_in_progress <> 'F' OR patch_level in ( SELECT MAX(patch_level) FROM patches WHERE system_name = ? ))
lock.obtain=UPDATE patches SET patch_in_progress = 'T' WHERE system_name = ? AND patch_in_progress = 'F' AND patch_level in ( SELECT max_patch_level FROM (SELECT MAX(patch_level) AS max_patch_level FROM patches WHERE system_name = ? ) AS tmptable )
-lock.release=UPDATE patches SET patch_in_progress = 'F' WHERE system_name = ? AND patch_in_progress <> 'F'
\ No newline at end of file
+lock.release=UPDATE patches SET patch_in_progress = 'F' WHERE system_name = ? AND patch_in_progress <> 'F'
diff --git a/src/main/resources/com/tacitknowledge/util/migration/jdbc/oracle.properties b/src/main/resources/com/tacitknowledge/util/migration/jdbc/oracle.properties
index ca3f1ce..ff91c9b 100644
--- a/src/main/resources/com/tacitknowledge/util/migration/jdbc/oracle.properties
+++ b/src/main/resources/com/tacitknowledge/util/migration/jdbc/oracle.properties
@@ -9,6 +9,7 @@ patches.create=CREATE TABLE tk_patches (\
# Validates that a record exists for a given system
level.create=INSERT INTO tk_patches (system_name, patch_level) VALUES ( ?, 0 )
+level.count=SELECT COUNT(*) FROM tk_patches WHERE system_name = ?
level.table.exists=SELECT patch_level FROM tk_patches WHERE system_name = ?
level.read=SELECT MAX(patch_level) FROM tk_patches WHERE system_name = ?
level.rollback=DELETE FROM tk_patches WHERE patch_level = ? and system_name = ?
diff --git a/src/main/resources/com/tacitknowledge/util/migration/jdbc/postgres.properties b/src/main/resources/com/tacitknowledge/util/migration/jdbc/postgres.properties
index 7eca275..fac1949 100644
--- a/src/main/resources/com/tacitknowledge/util/migration/jdbc/postgres.properties
+++ b/src/main/resources/com/tacitknowledge/util/migration/jdbc/postgres.properties
@@ -9,6 +9,7 @@ patches.create=CREATE TABLE patches ( \
# Validates that a record exists for a given system
level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0 )
+level.count=SELECT COUNT(*) FROM patches WHERE system_name = ?
level.table.exists=SELECT patch_level FROM patches WHERE system_name = ?
level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ?
level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ?
diff --git a/src/main/resources/com/tacitknowledge/util/migration/jdbc/sqlserver.properties b/src/main/resources/com/tacitknowledge/util/migration/jdbc/sqlserver.properties
index 2a2b89b..c0888c1 100644
--- a/src/main/resources/com/tacitknowledge/util/migration/jdbc/sqlserver.properties
+++ b/src/main/resources/com/tacitknowledge/util/migration/jdbc/sqlserver.properties
@@ -9,6 +9,7 @@ patches.create=CREATE TABLE patches ( \
# Validates that a record exists for a given system
level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0)
+level.count=SELECT COUNT(*) FROM patches WHERE system_name = ?
level.table.exists=SELECT patch_level FROM patches WHERE system_name = ?
level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ?
level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ?
diff --git a/src/main/resources/com/tacitknowledge/util/migration/jdbc/sybase.properties b/src/main/resources/com/tacitknowledge/util/migration/jdbc/sybase.properties
index 39e89b3..1d590ee 100644
--- a/src/main/resources/com/tacitknowledge/util/migration/jdbc/sybase.properties
+++ b/src/main/resources/com/tacitknowledge/util/migration/jdbc/sybase.properties
@@ -9,6 +9,7 @@ patches.create=CREATE TABLE patches (\
# Validates that a record exists for a given system
level.create=INSERT INTO patches (system_name, patch_level) VALUES ( ?, 0 )
+level.count=SELECT COUNT(*) FROM patches WHERE system_name = ?
level.table.exists=SELECT patch_level FROM patches WHERE system_name = ?
level.read=SELECT MAX(patch_level) FROM patches WHERE system_name = ?
level.rollback=DELETE FROM patches WHERE patch_level = ? and system_name = ?
diff --git a/src/test/java/com/tacitknowledge/util/migration/jdbc/PatchTableTest.java b/src/test/java/com/tacitknowledge/util/migration/jdbc/PatchTableTest.java
index 081affe..7aaba70 100644
--- a/src/test/java/com/tacitknowledge/util/migration/jdbc/PatchTableTest.java
+++ b/src/test/java/com/tacitknowledge/util/migration/jdbc/PatchTableTest.java
@@ -16,9 +16,11 @@
package com.tacitknowledge.util.migration.jdbc;
import java.sql.SQLException;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
+import org.apache.commons.lang.BooleanUtils;
import org.easymock.MockControl;
import com.mockrunner.jdbc.JDBCTestCaseAdapter;
@@ -115,6 +117,7 @@ public void testCreatePatchesTable() throws Exception
PreparedStatementResultSetHandler h = conn.getPreparedStatementResultSetHandler();
h.prepareThrowsSQLException(table.getSql("level.table.exists"));
+ setupLevelCreatedMock(h, false);
table.createPatchStoreIfNeeded();
commonVerifications();
@@ -193,6 +196,18 @@ public void testGetPatchLevel() throws Exception
verifyPreparedStatementNotPresent(table.getSql("level.create"));
}
+ /**
+ * Adds a result set to a data source mock to simulate existence of a patch level record.
+ *
+ * @param handler prepared statement handler
+ * @param levelExists controls what the mock returns for whether a level was already created or not
+ */
+ private void setupLevelCreatedMock(PreparedStatementResultSetHandler handler, Boolean levelExists) {
+ MockResultSet rs = handler.createResultSet();
+ handler.prepareResultSet(table.getSql("level.count"), rs);
+ rs.addRow(new Integer[] {BooleanUtils.toInteger(levelExists)});
+ }
+
/**
* Validates that getPatchLevel
works on a new system.
*
@@ -206,6 +221,7 @@ public void testGetPatchLevelFirstTime() throws Exception
// empty result set
handler.prepareResultSet(table.getSql("level.read"), rs);
handler.prepareThrowsSQLException(table.getSql("level.table.exists"));
+ setupLevelCreatedMock(handler, false);
int i = table.getPatchLevel();
@@ -227,6 +243,7 @@ public void testUpdatePatchLevel() throws Exception
MockResultSet rs = handler.createResultSet();
rs.addRow(new Integer[]{new Integer(12)});
handler.prepareResultSet(table.getSql("level.read"), rs, new String[]{"milestone"});
+ setupLevelCreatedMock(handler, true);
table.updatePatchLevel(13);
@@ -251,6 +268,7 @@ public void testIsPatchTableNotLocked() throws Exception
MockResultSet rs = handler.createResultSet();
rs.addRow(new String[]{"F"});
handler.prepareResultSet(table.getSql("lock.read"), rs, new String[]{"milestone", "milestone"});
+ setupLevelCreatedMock(handler, true);
assertFalse(table.isPatchStoreLocked());
commonVerifications();
@@ -272,6 +290,7 @@ public void testIsPatchTableLocked() throws Exception
MockResultSet rs = handler.createResultSet();
rs.addRow(new String[]{"T"});
handler.prepareResultSet(table.getSql("lock.read"), rs, new String[]{"milestone", "milestone"});
+ setupLevelCreatedMock(handler, true);
assertTrue(table.isPatchStoreLocked());
commonVerifications();
@@ -292,6 +311,7 @@ public void testLockPatchTableWhenAlreadyLocked() throws Exception
// Return a non-empty set in response to the patch lock query
handler = conn.getPreparedStatementResultSetHandler();
handler.prepareUpdateCount(table.getSql("lock.obtain"), 0, new String[] {"milestone", "milestone"});
+ setupLevelCreatedMock(handler, true);
try
{
@@ -322,7 +342,7 @@ public void testLockPatchTableWhenNotAlreadyLocked() throws Exception
// Test-specific setup
// Return an empty set in response to the patch lock query
handler = conn.getPreparedStatementResultSetHandler();
- MockResultSet rs = handler.createResultSet();
+ setupLevelCreatedMock(handler, true);
handler.prepareUpdateCount(table.getSql("lock.obtain"), 1, new String[] {"milestone", "milestone"});
table.lockPatchStore();
@@ -407,6 +427,48 @@ public void testPatchRetrievesSetWithPatchesApplied () throws SQLException, Migr
verifyPreparedStatementPresent(table.getSql("patches.all"));
}
+ public void testSecondSystemInitialized () throws Exception, SQLException, MigrationException {
+ String systemName = "milestone";
+ // setup test like testGetPatchLevelFirstTime
+ handler = conn.getPreparedStatementResultSetHandler();
+ MockResultSet rs = handler.createResultSet();
+ // empty result set
+ handler.prepareResultSet(table.getSql("level.read"), rs, Arrays.asList(systemName));
+ handler.prepareThrowsSQLException(table.getSql("level.table.exists"));
+ setupLevelCreatedMock(handler, false);
+
+ int i = table.getPatchLevel();
+
+ assertEquals(0, i);
+ commonVerifications();
+ verifyPreparedStatementPresent(table.getSql("level.create"));
+ verifyPreparedStatementParameter(table.getSql("level.create"), 1, systemName);
+
+ // Test-specific setup
+ // setup a second system backed by the same database table and verify that it is initialized properly
+ systemName = "orders";
+ DataSourceMigrationContext context2 = new DataSourceMigrationContext();
+ context2.setDataSource(new ConnectionWrapperDataSource(conn));
+ context2.setSystemName(systemName);
+ context2.setDatabaseType(new DatabaseType("hsqldb"));
+ PatchTable table2 = new PatchTable(context2);
+
+ // clear the exception from above which caused the database table to be created
+ handler.clearThrowsSQLException();
+ // clear out prepared statements since one is for "patches.create"
+ handler.clearPreparedStatements();
+ // simulate the table existing (since it was created by the first system)
+ rs = handler.createResultSet();
+ handler.prepareResultSet(table2.getSql("level.table.exists"), rs, Arrays.asList(systemName));
+ rs = handler.createResultSet();
+ handler.prepareResultSet(table2.getSql("level.read"), rs, Arrays.asList(systemName));
+
+ i = table2.getPatchLevel();
+ assertEquals(0, i);
+ verifyPreparedStatementNotPresent(table2.getSql("patches.create"));
+ verifyPreparedStatementPresent(table2.getSql("level.create"));
+ verifyPreparedStatementParameter(table2.getSql("level.create"), 1, systemName);
+ }
private void commonVerifications()
{