static final int TEXT_VALUE_MAX_LENGTH = 4000;
private static final String SELECT_APPLICATION_UUID_BY_KEY = "select uuid from projects where kee = ? and qualifier = 'APP'";
private static final String SELECT_PROJECTS_BY_KEYS = "select kee,uuid from projects where kee in (%s) and qualifier = 'TRK'";
+ private static final String SELECT_PROJECTS_BY_APP = "select project_uuid from app_projects where application_uuid = ?";
private static final String UPDATE_INTERNAL_PROP_TEXT_VALUE = "update internal_properties set text_value = ?, clob_value = NULL where kee = ?";
private static final String UPDATE_INTERNAL_PROP_CLOB_VALUE = "update internal_properties set clob_value = ?, text_value = NULL where kee = ?";
private static final String VIEWS_DEF_KEY = "views.def";
return;
}
+ List<String> alreadyAddedProjects = context.prepareSelect(SELECT_PROJECTS_BY_APP).setString(1, applicationUuid)
+ .list(r -> r.getString(1));
+
String queryParam = projects.stream().map(uuid -> "'" + uuid + "'").collect(Collectors.joining(","));
Map<String, String> projectUuidsByKeys = context.prepareSelect(format(SELECT_PROJECTS_BY_KEYS, queryParam))
.list(r -> new AbstractMap.SimpleEntry<>(r.getString(1), r.getString(2)))
.stream()
+ .filter(project -> !alreadyAddedProjects.contains(project.getValue()))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
- insertApplicationProjects(context, app, applicationUuid, projectUuidsByKeys, now);
+ if (!projectUuidsByKeys.isEmpty()) {
+ insertApplicationProjects(context, app, applicationUuid, projectUuidsByKeys, now);
+ }
if (!app.getApplicationBranches().isEmpty()) {
insertApplicationBranchesProjects(context, app, applicationUuid, projectUuidsByKeys, now);
}
tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
}
+ @Test
+ public void migration_is_resilient() throws SQLException {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+ // first attempt
+ underTest.execute();
+
+ // xml stays the same (stopped during migration)
+ updateViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+ // second attempt should not fail
+ underTest.execute();
+
+ assertThat(db.select("select uuid from projects"))
+ .extracting(r -> r.get("UUID"))
+ .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID, APP_2_UUID);
+ assertThat(db.select("select application_uuid, project_uuid from app_projects"))
+ .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
+ .containsExactlyInAnyOrder(
+ tuple(APP_1_UUID, PROJECT_1_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID),
+ tuple(APP_2_UUID, PROJECT_1_UUID),
+ tuple(APP_2_UUID, PROJECT_2_UUID));
+ assertThat(db.select("select application_uuid, project_uuid, application_branch_uuid, project_branch_uuid from app_branch_project_branch"))
+ .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"), r -> r.get("APPLICATION_BRANCH_UUID"), r -> r.get("PROJECT_BRANCH_UUID"))
+ .containsExactlyInAnyOrder(
+ tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID),
+ tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_2_UUID, PROJECT_1_BRANCH_2_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_2_UUID, PROJECT_2_BRANCH_1_UUID),
+ tuple(APP_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
+ tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
+ }
+
@Test
public void migrates_applications_without_application_branches_to_new_tables() throws SQLException {
setupFullProject1();
"created_at", system2.now());
}
+ private void updateViewsDefInternalProperty(@Nullable String xml) {
+ db.executeUpdateSql("update internal_properties set text_value = ? where kee = 'views.def'",
+ xml);
+ }
+
private void insertProject(String uuid, String key, String qualifier) {
db.executeInsert("PROJECTS",
"UUID", uuid,