aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2016-06-09 17:08:37 +0200
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>2016-06-14 15:51:13 +0200
commitdaee45d773b4f7ca9fa835c761b66ecec134d961 (patch)
treebbf57306d23510a00146c123b4d9ab4ec5d39c7a /sonar-db
parentc04041c1a5f84f9e41baf0ba737747c952db13f2 (diff)
downloadsonarqube-daee45d773b4f7ca9fa835c761b66ecec134d961.tar.gz
sonarqube-daee45d773b4f7ca9fa835c761b66ecec134d961.zip
SONAR-7693 replace SNAPSHOTS.*PROJECT_ID by columns with uuids
Diffstat (limited to 'sonar-db')
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java2
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java82
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java12
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/AddUuidColumnsToSnapshots.java49
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/CleanOrphanRowsInSnapshots.java62
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/DropIdColumnsFromSnapshots.java47
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshots.java49
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshots.java85
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql6
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl8
-rw-r--r--sonar-db/src/test/java/org/sonar/db/DbTester.java8
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java2
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v60/AddUuidColumnsToSnapshotsTest.java77
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v60/CleanOrphanRowsInSnapshotsTest.java163
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v60/DropIdColumnsFromSnapshotsTest.java48
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshotsTest.java86
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshotsTest.java122
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v60/AddUuidColumnsToSnapshotsTest/old_snapshots.sql32
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v60/CleanOrphanRowsInSnapshotsTest/in_progress_snapshots_and_children_tables.sql101
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshotsTest/in_progress_snapshots.sql34
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshotsTest/in_progress_snapshots_with_projects.sql57
21 files changed, 1106 insertions, 26 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
index 5e4ed02478b..1175f6c9260 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
@@ -30,7 +30,7 @@ import org.sonar.db.MyBatis;
public class DatabaseVersion {
- public static final int LAST_VERSION = 1_207;
+ public static final int LAST_VERSION = 1_213;
/**
* The minimum supported version which can be upgraded. Lower
diff --git a/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java b/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java
index 027603fde98..4b88f34325b 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java
@@ -21,12 +21,17 @@ package org.sonar.db.version;
import java.sql.Connection;
import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.sonar.core.util.ProgressLogger;
import org.sonar.db.Database;
+import static com.google.common.base.Preconditions.checkState;
+
public class MassUpdate {
+ @FunctionalInterface
public interface Handler {
/**
* Convert some column values of a given row.
@@ -36,6 +41,17 @@ public class MassUpdate {
boolean handle(Select.Row row, SqlStatement update) throws SQLException;
}
+ @FunctionalInterface
+ public interface MultiHandler {
+ /**
+ * Convert some column values of a given row.
+ *
+ * @param updateIndex 0-based
+ * @return true if the row must be updated, else false. If false, then the update parameter must not be touched.
+ */
+ boolean handle(Select.Row row, SqlStatement update, int updateIndex) throws SQLException;
+ }
+
private final Database db;
private final Connection readConnection;
private final Connection writeConnection;
@@ -43,7 +59,7 @@ public class MassUpdate {
private final ProgressLogger progress = ProgressLogger.create(getClass(), counter);
private Select select;
- private Upsert update;
+ private List<UpsertImpl> updates = new ArrayList<>(1);
MassUpdate(Database db, Connection readConnection, Connection writeConnection) {
this.db = db;
@@ -57,7 +73,7 @@ public class MassUpdate {
}
public MassUpdate update(String sql) throws SQLException {
- this.update = UpsertImpl.create(writeConnection, sql);
+ this.updates.add(UpsertImpl.create(writeConnection, sql));
return this;
}
@@ -66,26 +82,29 @@ public class MassUpdate {
return this;
}
- public void execute(final Handler handler) throws SQLException {
- if (select == null || update == null) {
- throw new IllegalStateException("SELECT or UPDATE requests are not defined");
+ public void execute(Handler handler) throws SQLException {
+ checkState(select != null && !updates.isEmpty(), "SELECT or UPDATE requests are not defined");
+ checkState(updates.size() == 1, "There should be only one update when using a " + Handler.class.getName());
+
+ progress.start();
+ try {
+ select.scroll(row -> callSingleHandler(handler, updates.iterator().next(), row));
+ closeUpdates();
+
+ // log the total number of processed rows
+ progress.log();
+ } finally {
+ progress.stop();
}
+ }
+
+ public void execute(MultiHandler handler) throws SQLException {
+ checkState(select != null && !updates.isEmpty(), "SELECT or UPDATE(s) requests are not defined");
progress.start();
try {
- select.scroll(new Select.RowHandler() {
- @Override
- public void handle(Select.Row row) throws SQLException {
- if (handler.handle(row, update)) {
- update.addBatch();
- }
- counter.getAndIncrement();
- }
- });
- if (((UpsertImpl) update).getBatchCount() > 0L) {
- update.execute().commit();
- }
- update.close();
+ select.scroll(row -> callMultiHandler(handler, updates, row));
+ closeUpdates();
// log the total number of processed rows
progress.log();
@@ -94,4 +113,31 @@ public class MassUpdate {
}
}
+ private void callSingleHandler(Handler handler, Upsert update, Select.Row row) throws SQLException {
+ if (handler.handle(row, update)) {
+ update.addBatch();
+ }
+ counter.getAndIncrement();
+ }
+
+ private void callMultiHandler(MultiHandler handler, List<UpsertImpl> updates, Select.Row row) throws SQLException {
+ int i = 0;
+ for (UpsertImpl update : updates) {
+ if (handler.handle(row, update, i)) {
+ update.addBatch();
+ }
+ i++;
+ }
+ counter.getAndIncrement();
+ }
+
+ private void closeUpdates() throws SQLException {
+ for (UpsertImpl update : updates) {
+ if (update.getBatchCount() > 0L) {
+ update.execute().commit();
+ }
+ update.close();
+ }
+ }
+
}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
index cde89e3adb0..22486d912a6 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
@@ -84,11 +84,16 @@ import org.sonar.db.version.v55.FeedRulesTypes;
import org.sonar.db.version.v56.FixLengthOfIssuesMessageOnOracle;
import org.sonar.db.version.v56.FixTypeOfRuleTypeOnMysql;
import org.sonar.db.version.v60.AddUuidColumnsToResourceIndex;
+import org.sonar.db.version.v60.AddUuidColumnsToSnapshots;
import org.sonar.db.version.v60.CleanOrphanRowsInResourceIndex;
+import org.sonar.db.version.v60.CleanOrphanRowsInSnapshots;
import org.sonar.db.version.v60.DropIdColumnsFromResourceIndex;
import org.sonar.db.version.v60.DropUnusedMeasuresColumns;
+import org.sonar.db.version.v60.DropIdColumnsFromSnapshots;
import org.sonar.db.version.v60.MakeUuidColumnsNotNullOnResourceIndex;
+import org.sonar.db.version.v60.MakeUuidColumnsNotNullOnSnapshots;
import org.sonar.db.version.v60.PopulateUuidColumnsOfResourceIndex;
+import org.sonar.db.version.v60.PopulateUuidColumnsOfSnapshots;
public class MigrationStepModule extends Module {
@Override
@@ -179,6 +184,11 @@ public class MigrationStepModule extends Module {
CleanOrphanRowsInResourceIndex.class,
MakeUuidColumnsNotNullOnResourceIndex.class,
DropIdColumnsFromResourceIndex.class,
- DropUnusedMeasuresColumns.class);
+ DropUnusedMeasuresColumns.class,
+ AddUuidColumnsToSnapshots.class,
+ PopulateUuidColumnsOfSnapshots.class,
+ CleanOrphanRowsInSnapshots.class,
+ MakeUuidColumnsNotNullOnSnapshots.class,
+ DropIdColumnsFromSnapshots.class);
}
}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/AddUuidColumnsToSnapshots.java b/sonar-db/src/main/java/org/sonar/db/version/v60/AddUuidColumnsToSnapshots.java
new file mode 100644
index 00000000000..34dbbfd7f48
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/AddUuidColumnsToSnapshots.java
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AddColumnsBuilder;
+import org.sonar.db.version.DdlChange;
+
+import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class AddUuidColumnsToSnapshots extends DdlChange {
+
+ private static final String TABLE_SNAPSHOTS = "snapshots";
+
+ private final Database db;
+
+ public AddUuidColumnsToSnapshots(Database db) {
+ super(db);
+ this.db = db;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(db.getDialect(), TABLE_SNAPSHOTS)
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("root_component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(true).build())
+ .build());
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/CleanOrphanRowsInSnapshots.java b/sonar-db/src/main/java/org/sonar/db/version/v60/CleanOrphanRowsInSnapshots.java
new file mode 100644
index 00000000000..f803661fc65
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/CleanOrphanRowsInSnapshots.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+
+public class CleanOrphanRowsInSnapshots extends BaseDataChange {
+
+ public CleanOrphanRowsInSnapshots(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT sn.id, sn.project_id, sn.root_project_id from snapshots sn where sn.component_uuid is null or sn.root_component_uuid is null");
+ massUpdate.update("DELETE from duplications_index WHERE snapshot_id=? or project_snapshot_id=?");
+ massUpdate.update("DELETE from project_measures WHERE snapshot_id=?");
+ massUpdate.update("DELETE from ce_activity WHERE snapshot_id=?");
+ massUpdate.update("DELETE from events WHERE snapshot_id=?");
+ massUpdate.update("DELETE from snapshots WHERE id=?");
+ massUpdate.rowPluralName("snapshots");
+ massUpdate.execute((row, update, updateIndex) -> {
+ long snapshotId = row.getLong(1);
+ switch (updateIndex) {
+ case 0:
+ update.setLong(1, snapshotId);
+ update.setLong(2, snapshotId);
+ return true;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ update.setLong(1, snapshotId);
+ return true;
+ default:
+ throw new IllegalArgumentException("Unsupported update index " + updateIndex);
+ }
+ });
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/DropIdColumnsFromSnapshots.java b/sonar-db/src/main/java/org/sonar/db/version/v60/DropIdColumnsFromSnapshots.java
new file mode 100644
index 00000000000..dbaa08c9c7e
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/DropIdColumnsFromSnapshots.java
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.DdlChange;
+import org.sonar.db.version.DropColumnsBuilder;
+
+public class DropIdColumnsFromSnapshots extends DdlChange {
+
+ private static final String TABLE_SNAPSHOTS = "snapshots";
+
+ private final Database db;
+
+ public DropIdColumnsFromSnapshots(Database db) {
+ super(db);
+ this.db = db;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(
+ new DropColumnsBuilder(
+ db.getDialect(), TABLE_SNAPSHOTS,
+ "project_id", "root_project_id")
+ .build());
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshots.java b/sonar-db/src/main/java/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshots.java
new file mode 100644
index 00000000000..6bf5025cba5
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshots.java
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AlterColumnsBuilder;
+import org.sonar.db.version.DdlChange;
+
+import static org.sonar.db.version.VarcharColumnDef.UUID_VARCHAR_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class MakeUuidColumnsNotNullOnSnapshots extends DdlChange {
+
+ private static final String TABLE_SNAPSHOTS = "snapshots";
+
+ private final Database db;
+
+ public MakeUuidColumnsNotNullOnSnapshots(Database db) {
+ super(db);
+ this.db = db;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AlterColumnsBuilder(db.getDialect(), TABLE_SNAPSHOTS)
+ .updateColumn(newVarcharColumnDefBuilder().setColumnName("component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(false).build())
+ .updateColumn(newVarcharColumnDefBuilder().setColumnName("root_component_uuid").setLimit(UUID_VARCHAR_SIZE).setIsNullable(false).build())
+ .build());
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshots.java b/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshots.java
new file mode 100644
index 00000000000..146421d8200
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshots.java
@@ -0,0 +1,85 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class PopulateUuidColumnsOfSnapshots extends BaseDataChange {
+
+ public PopulateUuidColumnsOfSnapshots(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ Map<Long, String> componentUuidById = buildComponentUuidMap(context);
+ if (componentUuidById.isEmpty()) {
+ return;
+ }
+
+ populateUuidColumns(context, componentUuidById);
+ }
+
+ private Map<Long, String> buildComponentUuidMap(Context context) throws SQLException {
+ Map<Long, String> componentUuidById = new HashMap<>();
+ context.prepareSelect("select distinct p.id, p.uuid from projects p" +
+ " join snapshots sn1 on sn1.project_id = p.id and sn1.component_uuid is null")
+ .scroll(row -> componentUuidById.put(row.getLong(1), row.getString(2)));
+ context.prepareSelect("select distinct p.id, p.uuid from projects p" +
+ " join snapshots sn2 on sn2.root_project_id = p.id and sn2.root_component_uuid is null")
+ .scroll(row -> componentUuidById.put(row.getLong(1), row.getString(2)));
+ return componentUuidById;
+ }
+
+ private void populateUuidColumns(Context context, Map<Long, String> componentUuidById) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT sn.id, sn.project_id, sn.root_project_id from snapshots sn where sn.component_uuid is null or sn.root_component_uuid is null");
+ massUpdate.update("UPDATE snapshots SET component_uuid=?, root_component_uuid=? WHERE id=?");
+ massUpdate.rowPluralName("snapshots");
+ massUpdate.execute((row, update) -> this.handle(componentUuidById, row, update));
+ }
+
+ private boolean handle(Map<Long, String> componentUuidById, Select.Row row, SqlStatement update) throws SQLException {
+ long id = row.getLong(1);
+ long componentId = row.getLong(2);
+ long rootProjectId = row.getLong(3);
+
+ String componentUuid = componentUuidById.get(componentId);
+ String rootComponentUuid = componentUuidById.get(rootProjectId);
+
+ if (componentUuid == null && rootComponentUuid == null) {
+ return false;
+ }
+
+ update.setString(1, componentUuid);
+ update.setString(2, rootComponentUuid);
+ update.setLong(3, id);
+
+ return true;
+ }
+
+}
diff --git a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
index ea7ecb61914..bd633797bf1 100644
--- a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
+++ b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
@@ -413,6 +413,12 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1204');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1205');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1206');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1207');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1208');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1209');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1210');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1211');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1212');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1213');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null);
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
diff --git a/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl b/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
index effcaec7dfd..cc740743516 100644
--- a/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
+++ b/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
@@ -56,7 +56,7 @@ CREATE TABLE "SNAPSHOTS" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"CREATED_AT" BIGINT,
"BUILD_DATE" BIGINT,
- "PROJECT_ID" INTEGER NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50) NOT NULL,
"PARENT_SNAPSHOT_ID" INTEGER,
"STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
"PURGE_STATUS" INTEGER,
@@ -67,7 +67,7 @@ CREATE TABLE "SNAPSHOTS" (
"VERSION" VARCHAR(500),
"PATH" VARCHAR(500),
"DEPTH" INTEGER,
- "ROOT_PROJECT_ID" INTEGER,
+ "ROOT_COMPONENT_UUID" VARCHAR(50) NOT NULL,
"PERIOD1_MODE" VARCHAR(100),
"PERIOD1_PARAM" VARCHAR(100),
"PERIOD1_DATE" BIGINT,
@@ -577,7 +577,7 @@ CREATE INDEX "SNAPSHOTS_ROOT" ON "SNAPSHOTS" ("ROOT_SNAPSHOT_ID");
CREATE INDEX "SNAPSHOTS_PARENT" ON "SNAPSHOTS" ("PARENT_SNAPSHOT_ID");
-CREATE INDEX "SNAPSHOT_PROJECT_ID" ON "SNAPSHOTS" ("PROJECT_ID");
+CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
CREATE INDEX "RULES_PARAMETERS_RULE_ID" ON "RULES_PARAMETERS" ("RULE_ID");
@@ -643,7 +643,7 @@ CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN");
CREATE INDEX "USERS_UPDATED_AT" ON "USERS" ("UPDATED_AT");
-CREATE INDEX "SNAPSHOTS_ROOT_PROJECT_ID" ON "SNAPSHOTS" ("ROOT_PROJECT_ID");
+CREATE INDEX "SNAPSHOTS_ROOT_COMPONENT" ON "SNAPSHOTS" ("ROOT_COMPONENT_UUID");
CREATE UNIQUE INDEX "UNIQ_GROUP_ROLES" ON "GROUP_ROLES" ("GROUP_ID", "RESOURCE_ID", "ROLE");
diff --git a/sonar-db/src/test/java/org/sonar/db/DbTester.java b/sonar-db/src/test/java/org/sonar/db/DbTester.java
index 4f08378acf9..e96507352cf 100644
--- a/sonar-db/src/test/java/org/sonar/db/DbTester.java
+++ b/sonar-db/src/test/java/org/sonar/db/DbTester.java
@@ -141,6 +141,8 @@ public class DbTester extends ExternalResource {
/**
* Very simple helper method to insert some data into a table.
* It's the responsibility of the caller to convert column values to string.
+ *
+ * @param valuesByColumn column name and value pairs, if any value is null, the associated column won't be inserted
*/
public void executeInsert(String table, String... valuesByColumn) {
executeInsert(table, mapOf(valuesByColumn));
@@ -149,7 +151,11 @@ public class DbTester extends ExternalResource {
private static Map<String, String> mapOf(String... values) {
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
for (int i = 0; i < values.length; i++) {
- builder.put(values[i], values[i + 1]);
+ String key = values[i];
+ String value = values[i + 1];
+ if (value != null) {
+ builder.put(key, value);
+ }
i++;
}
return builder.build();
diff --git a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
index 5de4c3e4fdf..9b8567733ea 100644
--- a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
+++ b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
@@ -29,6 +29,6 @@ public class MigrationStepModuleTest {
public void verify_count_of_added_MigrationStep_types() {
ComponentContainer container = new ComponentContainer();
new MigrationStepModule().configure(container);
- assertThat(container.size()).isEqualTo(71);
+ assertThat(container.size()).isEqualTo(76);
}
}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v60/AddUuidColumnsToSnapshotsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v60/AddUuidColumnsToSnapshotsTest.java
new file mode 100644
index 00000000000..17063d56218
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v60/AddUuidColumnsToSnapshotsTest.java
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static java.lang.String.valueOf;
+
+public class AddUuidColumnsToSnapshotsTest {
+
+ public static final String SNAPSHOTS_TABLE = "snapshots";
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, AddUuidColumnsToSnapshotsTest.class, "old_snapshots.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddUuidColumnsToSnapshots underTest = new AddUuidColumnsToSnapshots(db.database());
+
+ @Test
+ public void migration_adds_columns_to_empty_table() throws SQLException {
+ underTest.execute();
+
+ verifyAddedColumns();
+ }
+
+ @Test
+ public void migration_adds_columns_to_populated_table() throws SQLException {
+ for (int i = 0; i < 9; i++) {
+ db.executeInsert(
+ SNAPSHOTS_TABLE,
+ "PROJECT_ID", valueOf(i),
+ "ISLAST", "TRUE");
+ }
+ db.commit();
+
+ underTest.execute();
+
+ verifyAddedColumns();
+ }
+
+ private void verifyAddedColumns() {
+ db.assertColumnDefinition(SNAPSHOTS_TABLE, "component_uuid", Types.VARCHAR, 50, true);
+ db.assertColumnDefinition(SNAPSHOTS_TABLE, "root_component_uuid", Types.VARCHAR, 50, true);
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Fail to execute ");
+ underTest.execute();
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v60/CleanOrphanRowsInSnapshotsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v60/CleanOrphanRowsInSnapshotsTest.java
new file mode 100644
index 00000000000..8e9d3e88d1d
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v60/CleanOrphanRowsInSnapshotsTest.java
@@ -0,0 +1,163 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CleanOrphanRowsInSnapshotsTest {
+
+ public static final String SNAPSHOTS_TABLE = "snapshots";
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, CleanOrphanRowsInSnapshotsTest.class,
+ "in_progress_snapshots_and_children_tables.sql");
+
+ private CleanOrphanRowsInSnapshots underTest = new CleanOrphanRowsInSnapshots(db.database());
+
+ @Test
+ public void migration_has_no_effect_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable(SNAPSHOTS_TABLE)).isEqualTo(0);
+ }
+
+ @Test
+ public void migration_deletes_any_row_with_a_null_uuid() throws SQLException {
+ insertSnapshots(1, true, true);
+ insertSnapshots(2, false, false);
+ insertSnapshots(3, true, false);
+ insertSnapshots(4, false, true);
+ insertSnapshots(5, true, true);
+ db.commit();
+
+ underTest.execute();
+
+ assertThat(idsOfRowsInSnapshots()).containsOnly(1l, 5l);
+ }
+
+ @Test
+ public void migration_deletes_rows_in_children_tables_referencing_snapshots_with_at_least_null_uuid() throws SQLException {
+ insertSnapshots(1, true, true);
+ insertSnapshots(2, false, true);
+ insertSnapshots(3, true, false);
+ insertDuplicationIndex(1, 1);
+ insertDuplicationIndex(30, 1);
+ insertDuplicationIndex(1, 40);
+ insertDuplicationIndex(50, 2);
+ insertDuplicationIndex(2, 2);
+ insertDuplicationIndex(2, 60);
+ insertDuplicationIndex(3, 3);
+ insertDuplicationIndex(70, 3);
+ insertDuplicationIndex(3, 90);
+ insertProjectMeasure(1);
+ insertProjectMeasure(2);
+ insertProjectMeasure(3);
+ insertCeActivity(1);
+ insertCeActivity(2);
+ insertCeActivity(3);
+ insertEvents(1);
+ insertEvents(2);
+ insertEvents(3);
+ db.commit();
+
+ underTest.execute();
+
+ verifyLineCountsPerSnapshot(1, 1, 3, 1, 1, 1);
+ verifyLineCountsPerSnapshot(2, 0, 0, 0, 0, 0);
+ verifyLineCountsPerSnapshot(3, 0, 0, 0, 0, 0);
+ }
+
+ private void verifyLineCountsPerSnapshot(int snapshotId, int snapshotCount, int duplicationIndexCount, int projectMeasureCount, int ceActivityCount, int eventCount) {
+ assertThat(count("SNAPSHOTS where id=" + snapshotId)).isEqualTo(snapshotCount);
+ assertThat(count("DUPLICATIONS_INDEX where snapshot_id=" + snapshotId + " or project_snapshot_id=" +snapshotId)).isEqualTo(duplicationIndexCount);
+ assertThat(count("PROJECT_MEASURES where snapshot_id=" + snapshotId)).isEqualTo(projectMeasureCount);
+ assertThat(count("CE_ACTIVITY where snapshot_id=" + snapshotId)).isEqualTo(ceActivityCount);
+ assertThat(count("EVENTS where snapshot_id=" + snapshotId)).isEqualTo(eventCount);
+ }
+
+ private long count(String tableAndWhereClause) {
+ return (Long) db.selectFirst("select count(*) from " + tableAndWhereClause).entrySet().iterator().next().getValue();
+ }
+
+
+ private void insertDuplicationIndex(int snapshotId, int parentSnapshotId) {
+ db.executeInsert(
+ "DUPLICATIONS_INDEX",
+ "SNAPSHOT_ID", valueOf(snapshotId),
+ "PROJECT_SNAPSHOT_ID", valueOf(parentSnapshotId),
+ "HASH", "hash_" + snapshotId + "-" + parentSnapshotId,
+ "INDEX_IN_FILE", valueOf(snapshotId + parentSnapshotId),
+ "START_LINE", "1",
+ "END_LINE", "1");
+ }
+
+ private void insertProjectMeasure(int snapshotId) {
+ db.executeInsert(
+ "PROJECT_MEASURES",
+ "SNAPSHOT_ID", valueOf(snapshotId),
+ "METRIC_ID", "111");
+ }
+
+ private void insertCeActivity(int snapshotId) {
+ db.executeInsert(
+ "CE_ACTIVITY",
+ "UUID", valueOf(snapshotId + 10),
+ "TASK_TYPE", "REPORT",
+ "STATUS", "OK",
+ "COMPONENT_UUID", valueOf(snapshotId + 20),
+ "SNAPSHOT_ID", valueOf(snapshotId),
+ "IS_LAST", "true",
+ "IS_LAST_KEY", "key",
+ "SUBMITTED_AT", "984651",
+ "CREATED_AT", "984651",
+ "UPDATED_AT", "984651");
+ }
+
+ private void insertEvents(int snapshotId) {
+ db.executeInsert(
+ "EVENTS",
+ "SNAPSHOT_ID", valueOf(snapshotId),
+ "EVENT_DATE", "984651",
+ "CREATED_AT", "984651");
+ }
+
+ private void insertSnapshots(long id, boolean hasComponentUiid, boolean hasRootComponentUuid) {
+ db.executeInsert(
+ SNAPSHOTS_TABLE,
+ "ID", valueOf(id),
+ "ISLAST", "TRUE",
+ "PROJECT_ID", valueOf(id + 300),
+ "COMPONENT_UUID", hasComponentUiid ? "uuid_" + id : null,
+ "ROOT_COMPONENT_UUID", hasRootComponentUuid ? "root_uuid_" + id : null);
+ }
+
+ private List<Long> idsOfRowsInSnapshots() {
+ return db.select("select ID from snapshots").stream().map(map -> (Long) map.get("ID")).collect(Collectors.toList());
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v60/DropIdColumnsFromSnapshotsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v60/DropIdColumnsFromSnapshotsTest.java
new file mode 100644
index 00000000000..bf045d34f62
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v60/DropIdColumnsFromSnapshotsTest.java
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import org.junit.Test;
+import org.sonar.db.Database;
+import org.sonar.db.dialect.PostgreSql;
+import org.sonar.db.version.DdlChange;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DropIdColumnsFromSnapshotsTest {
+
+ private Database database = mock(Database.class);
+
+ private DropIdColumnsFromSnapshots underTest = new DropIdColumnsFromSnapshots(database);
+
+ @Test
+ public void verify_generated_sql_on_postgresql() throws SQLException {
+ when(database.getDialect()).thenReturn(new PostgreSql());
+
+ DdlChange.Context context = mock(DdlChange.Context.class);
+ underTest.execute(context);
+
+ verify(context).execute("ALTER TABLE snapshots DROP COLUMN project_id, DROP COLUMN root_project_id");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshotsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshotsTest.java
new file mode 100644
index 00000000000..da9b16c2f9a
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshotsTest.java
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static java.lang.String.valueOf;
+
+public class MakeUuidColumnsNotNullOnSnapshotsTest {
+
+ private static final String SNAPSHOTS_TABLE = "snapshots";
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, MakeUuidColumnsNotNullOnSnapshotsTest.class,
+ "in_progress_snapshots.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private MakeUuidColumnsNotNullOnSnapshots underTest = new MakeUuidColumnsNotNullOnSnapshots(db.database());
+
+ @Test
+ public void migration_sets_uuid_columns_not_nullable_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ verifyColumnDefinitions();
+ }
+
+ @Test
+ public void migration_sets_uuid_columns_not_nullable_on_populated_table() throws SQLException {
+ insertSnapshots(1, true, true);
+ insertSnapshots(2, true, true);
+
+ underTest.execute();
+
+ verifyColumnDefinitions();
+ }
+
+ @Test
+ public void migration_fails_if_some_uuid_columns_are_null() throws SQLException {
+ insertSnapshots(1, false, true);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Fail to execute");
+
+ underTest.execute();
+ }
+
+ private void verifyColumnDefinitions() {
+ db.assertColumnDefinition(SNAPSHOTS_TABLE, "component_uuid", Types.VARCHAR, 50, false);
+ db.assertColumnDefinition(SNAPSHOTS_TABLE, "root_component_uuid", Types.VARCHAR, 50, false);
+ }
+
+ private void insertSnapshots(long id, boolean hasComponentUiid, boolean hasRootComponentUuid) {
+ db.executeInsert(
+ SNAPSHOTS_TABLE,
+ "ID", valueOf(id),
+ "ISLAST", "TRUE",
+ "PROJECT_ID", valueOf(id + 300),
+ "COMPONENT_UUID", hasComponentUiid ? "uuid_" + id : null,
+ "ROOT_COMPONENT_UUID", hasRootComponentUuid ? "root_uuid_" + id : null);
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshotsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshotsTest.java
new file mode 100644
index 00000000000..141ce50cd39
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshotsTest.java
@@ -0,0 +1,122 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v60;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PopulateUuidColumnsOfSnapshotsTest {
+
+ private static final String SNAPSHOTS_TABLE = "snapshots";
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, PopulateUuidColumnsOfSnapshotsTest.class,
+ "in_progress_snapshots_with_projects.sql");
+
+ private PopulateUuidColumnsOfSnapshots underTest = new PopulateUuidColumnsOfSnapshots(db.database());
+
+ @Test
+ public void migration_has_no_effect_on_empty_tables() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable(SNAPSHOTS_TABLE)).isEqualTo(0);
+ assertThat(db.countRowsOfTable("projects")).isEqualTo(0);
+ }
+
+ @Test
+ public void migration_updates_uuid_columns_with_values_from_table_projects_when_they_exist() throws SQLException {
+ String uuid1 = insertComponent(40);
+ String uuid2 = insertComponent(50);
+ String uuid3 = insertComponent(60);
+ String uuid4 = insertComponent(70);
+ String uuid5 = insertComponent(80);
+
+ insertSnapshots(1, 40, 50L);
+ insertSnapshots(2, 60, 70L);
+ insertSnapshots(3, 90, 70L); // 90 does not exist
+ insertSnapshots(4, 40, 100L); // 100 does not exist
+ insertSnapshots(5, 110, 100L); // 110 and 100 do not exist
+ insertSnapshots(6, 80, null); // no root
+ insertSnapshots(7, 120, null); // no root and 120 does not exist
+ db.commit();
+
+ underTest.execute();
+
+ verifySnapshots(1, 40, uuid1, 50L, uuid2);
+ verifySnapshots(2, 60, uuid3, 70L, uuid4);
+ verifySnapshots(3, 90, null, 70L, uuid4);
+ verifySnapshots(4, 40, uuid1, 100L, null);
+ verifySnapshots(5, 110, null, 100L, null);
+ verifySnapshots(6, 80, uuid5, null, null);
+ verifySnapshots(7, 120, null, null, null);
+ }
+
+ @Test
+ public void migration_is_reentrant() throws SQLException {
+ String uuid1 = insertComponent(40);
+ String uuid2 = insertComponent(50);
+ insertSnapshots(1, 40, 50L);
+
+ underTest.execute();
+ verifySnapshots(1, 40, uuid1, 50L, uuid2);
+
+ underTest.execute();
+ verifySnapshots(1, 40, uuid1, 50L, uuid2);
+ }
+
+ private void insertSnapshots(long id, long projectId, @Nullable Long rootId) {
+ db.executeInsert(
+ SNAPSHOTS_TABLE,
+ "ID", valueOf(id),
+ "ISLAST", "TRUE",
+ "PROJECT_ID", valueOf(projectId),
+ "ROOT_PROJECT_ID", rootId == null ? null : valueOf(rootId));
+ }
+
+ private String insertComponent(long id) {
+ String uuid = "uuid_" + id;
+ db.executeInsert(
+ "projects",
+ "ID", valueOf(id),
+ "UUID", uuid);
+ return uuid;
+ }
+
+ private void verifySnapshots(long id, long resourceId, @Nullable String componentUuid, @Nullable Long rootProjectId, @Nullable String rootComponentUuid) {
+ List<Map<String, Object>> rows = db.select("select PROJECT_ID, COMPONENT_UUID, ROOT_PROJECT_ID, ROOT_COMPONENT_UUID from snapshots where ID=" + id);
+ assertThat(rows).hasSize(1);
+ Map<String, Object> row = rows.get(0);
+ assertThat(row.get("PROJECT_ID")).isEqualTo(resourceId);
+ assertThat(row.get("COMPONENT_UUID")).isEqualTo(componentUuid);
+ assertThat(row.get("ROOT_PROJECT_ID")).isEqualTo(rootProjectId);
+ assertThat(row.get("ROOT_COMPONENT_UUID")).isEqualTo(rootComponentUuid);
+ }
+
+
+}
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v60/AddUuidColumnsToSnapshotsTest/old_snapshots.sql b/sonar-db/src/test/resources/org/sonar/db/version/v60/AddUuidColumnsToSnapshotsTest/old_snapshots.sql
new file mode 100644
index 00000000000..832463b246c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v60/AddUuidColumnsToSnapshotsTest/old_snapshots.sql
@@ -0,0 +1,32 @@
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" BIGINT,
+ "BUILD_DATE" BIGINT,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" BIGINT,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" BIGINT,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" BIGINT,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" BIGINT,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v60/CleanOrphanRowsInSnapshotsTest/in_progress_snapshots_and_children_tables.sql b/sonar-db/src/test/resources/org/sonar/db/version/v60/CleanOrphanRowsInSnapshotsTest/in_progress_snapshots_and_children_tables.sql
new file mode 100644
index 00000000000..d77a2eab205
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v60/CleanOrphanRowsInSnapshotsTest/in_progress_snapshots_and_children_tables.sql
@@ -0,0 +1,101 @@
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" BIGINT,
+ "BUILD_DATE" BIGINT,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50),
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "ROOT_COMPONENT_UUID" VARCHAR(50),
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" BIGINT,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" BIGINT,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" BIGINT,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" BIGINT,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" BIGINT
+);
+
+CREATE TABLE "DUPLICATIONS_INDEX" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_SNAPSHOT_ID" INTEGER NOT NULL,
+ "SNAPSHOT_ID" INTEGER NOT NULL,
+ "HASH" VARCHAR(50) NOT NULL,
+ "INDEX_IN_FILE" INTEGER NOT NULL,
+ "START_LINE" INTEGER NOT NULL,
+ "END_LINE" INTEGER NOT NULL
+);
+
+CREATE TABLE "PROJECT_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "VALUE" DOUBLE,
+ "METRIC_ID" INTEGER NOT NULL,
+ "SNAPSHOT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "RULES_CATEGORY_ID" INTEGER,
+ "TEXT_VALUE" VARCHAR(4000),
+ "TENDENCY" INTEGER,
+ "MEASURE_DATE" TIMESTAMP,
+ "PROJECT_ID" INTEGER,
+ "ALERT_STATUS" VARCHAR(5),
+ "ALERT_TEXT" VARCHAR(4000),
+ "URL" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(4000),
+ "RULE_PRIORITY" INTEGER,
+ "CHARACTERISTIC_ID" INTEGER,
+ "PERSON_ID" INTEGER,
+ "VARIATION_VALUE_1" DOUBLE,
+ "VARIATION_VALUE_2" DOUBLE,
+ "VARIATION_VALUE_3" DOUBLE,
+ "VARIATION_VALUE_4" DOUBLE,
+ "VARIATION_VALUE_5" DOUBLE,
+ "MEASURE_DATA" BINARY(167772150)
+);
+
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "SNAPSHOT_ID" INTEGER NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "SUBMITTER_LOGIN" VARCHAR(255) NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL
+);
+
+CREATE TABLE "EVENTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(400),
+ "COMPONENT_UUID" VARCHAR(50),
+ "SNAPSHOT_ID" INTEGER,
+ "CATEGORY" VARCHAR(50),
+ "EVENT_DATE" BIGINT NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "DESCRIPTION" VARCHAR(4000),
+ "EVENT_DATA" VARCHAR(4000)
+);
+
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshotsTest/in_progress_snapshots.sql b/sonar-db/src/test/resources/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshotsTest/in_progress_snapshots.sql
new file mode 100644
index 00000000000..89c13c7e102
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v60/MakeUuidColumnsNotNullOnSnapshotsTest/in_progress_snapshots.sql
@@ -0,0 +1,34 @@
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" BIGINT,
+ "BUILD_DATE" BIGINT,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50),
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "ROOT_COMPONENT_UUID" VARCHAR(50),
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" BIGINT,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" BIGINT,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" BIGINT,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" BIGINT,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshotsTest/in_progress_snapshots_with_projects.sql b/sonar-db/src/test/resources/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshotsTest/in_progress_snapshots_with_projects.sql
new file mode 100644
index 00000000000..64128b7e4c2
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v60/PopulateUuidColumnsOfSnapshotsTest/in_progress_snapshots_with_projects.sql
@@ -0,0 +1,57 @@
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" BIGINT,
+ "BUILD_DATE" BIGINT,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50),
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "ROOT_COMPONENT_UUID" VARCHAR(50),
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" BIGINT,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" BIGINT,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" BIGINT,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" BIGINT,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" BIGINT
+);
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(2000),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);