From add64436be2fc275aa2578dc6a4eeab427b41208 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Mon, 15 Sep 2014 15:54:45 +0200 Subject: [PATCH] SONAR-5580 Migration issue to SQ 4.4 when the quality profile used to analyze the last version of a project has been removed before the migration --- .../db/migrations/DatabaseMigrations.java | 4 +- .../v44/ConvertProfileMeasuresMigration.java | 13 +++- ...eteMeasuresOnDeletedProfilesMigration.java | 59 ++++++++++++++ .../ConvertProfileMeasuresMigrationTest.java | 22 ++++++ ...easuresOnDeletedProfilesMigrationTest.java | 68 ++++++++++++++++ .../measure_on_deleted_profile.xml | 31 ++++++++ .../before.xml | 23 ++++++ .../schema.sql | 77 +++++++++++++++++++ ...584_delete_measures_on_deleted_profiles.rb | 30 ++++++++ .../core/persistence/DatabaseVersion.java | 2 +- .../migration/v44/Migration44Mapper.java | 4 + .../migration/v44/Migration44Mapper.xml | 12 +++ .../org/sonar/core/persistence/rows-h2.sql | 1 + 13 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigration.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigrationTest/measure_on_deleted_profile.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/before.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/schema.sql create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/584_delete_measures_on_deleted_profiles.rb diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java index e42b945d93e..5d280b4f7a2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java @@ -36,6 +36,7 @@ import org.sonar.server.db.migrations.v44.FeedQProfileKeysMigration; import org.sonar.server.db.migrations.v44.IssueActionPlanKeyMigration; import org.sonar.server.db.migrations.v44.MeasureDataMigration; import org.sonar.server.db.migrations.v45.AddMissingRuleParameterDefaultValuesMigration; +import org.sonar.server.db.migrations.v45.DeleteMeasuresOnDeletedProfilesMigration; import java.util.List; @@ -65,7 +66,8 @@ public interface DatabaseMigrations { ConvertProfileMeasuresMigration.class, // 4.5 - AddMissingRuleParameterDefaultValuesMigration.class + AddMissingRuleParameterDefaultValuesMigration.class, + DeleteMeasuresOnDeletedProfilesMigration.class ); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigration.java index 0c2f6f522fc..994e8f15f4a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigration.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigration.java @@ -54,6 +54,7 @@ public class ConvertProfileMeasuresMigration implements DatabaseMigration { Date now = new Date(); Migration44Mapper mapper = session.getMapper(Migration44Mapper.class); for (ProfileMeasure profileMeasure : mapper.selectProfileMeasures()) { + boolean updated = false; Integer version = mapper.selectProfileVersion(profileMeasure.getSnapshotId()); QProfileDto44 profile = mapper.selectProfileById(profileMeasure.getProfileId()); if (profile != null) { @@ -76,10 +77,14 @@ public class ConvertProfileMeasuresMigration implements DatabaseMigration { .endArray() .close(); mapper.updateProfileMeasure(profileMeasure.getId(), writer.toString()); - if (i % 100 == 0) { - session.commit(); - i++; - } + updated = true; + } + if (!updated) { + mapper.deleteProfileMeasure(profileMeasure.getId()); + } + if (i % 100 == 0) { + session.commit(); + i++; } } session.commit(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigration.java new file mode 100644 index 00000000000..0a79bd6beda --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigration.java @@ -0,0 +1,59 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.server.db.migrations.v45; + +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.migration.v44.Migration44Mapper; +import org.sonar.server.db.DbClient; +import org.sonar.server.db.migrations.DatabaseMigration; + +/** + * See http://jira.codehaus.org/browse/SONAR-5580 + * {@link org.sonar.server.db.migrations.v44.ConvertProfileMeasuresMigration} + * introduced a regression in 4.4. Measures on orphan profiles were kept + * in old format (no JSON data but numeric value of profile id) + * + * @see org.sonar.server.db.migrations.v44.ConvertProfileMeasuresMigration + * @since 4.5 + */ +public class DeleteMeasuresOnDeletedProfilesMigration implements DatabaseMigration { + + private final DbClient db; + + public DeleteMeasuresOnDeletedProfilesMigration(DbClient db) { + this.db = db; + } + + @Override + public void execute() { + DbSession session = db.openSession(false); + try { + Migration44Mapper mapper = session.getMapper(Migration44Mapper.class); + for (Long measureId : mapper.selectMeasuresOnDeletedQualityProfiles()) { + mapper.deleteProfileMeasure(measureId); + } + session.commit(); + + } finally { + session.close(); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigrationTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigrationTest.java index 0614f58a36d..d521bded457 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigrationTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigrationTest.java @@ -89,4 +89,26 @@ public class ConvertProfileMeasuresMigrationTest { connection.close(); } } + + /** + * http://jira.codehaus.org/browse/SONAR-5580 + */ + @Test + public void delete_measure_when_profile_does_not_exist() throws Exception { + db.prepareDbUnit(getClass(), "measure_on_deleted_profile.xml"); + + migration.execute(); + + Connection connection = db.openConnection(); + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("select * from project_measures where id=2"); + try { + // measure is deleted + assertThat(rs.next()).isFalse(); + } finally { + rs.close(); + stmt.close(); + connection.close(); + } + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest.java new file mode 100644 index 00000000000..4910e6b08f5 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest.java @@ -0,0 +1,68 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.server.db.migrations.v45; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.core.persistence.TestDatabase; +import org.sonar.server.db.DbClient; +import org.sonar.server.db.migrations.DatabaseMigration; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; + +import static org.fest.assertions.Assertions.assertThat; + +public class DeleteMeasuresOnDeletedProfilesMigrationTest { + + @ClassRule + public static TestDatabase db = new TestDatabase().schema(DeleteMeasuresOnDeletedProfilesMigrationTest.class, "schema.sql"); + + DatabaseMigration migration; + + @Before + public void setUp() throws Exception { + DbClient dbClient = new DbClient(db.database(), db.myBatis()); + migration = new DeleteMeasuresOnDeletedProfilesMigration(dbClient); + } + + @Test + public void delete_measures_with_no_json_data() throws Exception { + db.prepareDbUnit(getClass(), "before.xml"); + + migration.execute(); + + Connection connection = db.openConnection(); + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("select id from project_measures"); + try { + assertThat(rs.next()).isTrue(); + assertThat(rs.getInt(1)).isEqualTo(2); + assertThat(rs.next()).isFalse(); + } finally { + rs.close(); + stmt.close(); + connection.close(); + } + } +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigrationTest/measure_on_deleted_profile.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigrationTest/measure_on_deleted_profile.xml new file mode 100644 index 00000000000..53071165f0f --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v44/ConvertProfileMeasuresMigrationTest/measure_on_deleted_profile.xml @@ -0,0 +1,31 @@ + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/before.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/before.xml new file mode 100644 index 00000000000..224c28d5fab --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/before.xml @@ -0,0 +1,23 @@ + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/schema.sql new file mode 100644 index 00000000000..912fd080b0d --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/schema.sql @@ -0,0 +1,77 @@ +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 "METRICS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "NAME" VARCHAR(64) NOT NULL, + "DESCRIPTION" VARCHAR(255), + "DIRECTION" INTEGER NOT NULL DEFAULT 0, + "DOMAIN" VARCHAR(64), + "SHORT_NAME" VARCHAR(64), + "QUALITATIVE" BOOLEAN NOT NULL DEFAULT FALSE, + "VAL_TYPE" VARCHAR(8), + "USER_MANAGED" BOOLEAN DEFAULT FALSE, + "ENABLED" BOOLEAN DEFAULT TRUE, + "ORIGIN" VARCHAR(3), + "WORST_VALUE" DOUBLE, + "BEST_VALUE" DOUBLE, + "OPTIMIZED_BEST_VALUE" BOOLEAN, + "HIDDEN" BOOLEAN, + "DELETE_HISTORICAL_DATA" BOOLEAN +); + +CREATE TABLE "SNAPSHOTS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "CREATED_AT" TIMESTAMP, + "BUILD_DATE" TIMESTAMP, + "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" TIMESTAMP, + "PERIOD2_MODE" VARCHAR(100), + "PERIOD2_PARAM" VARCHAR(100), + "PERIOD2_DATE" TIMESTAMP, + "PERIOD3_MODE" VARCHAR(100), + "PERIOD3_PARAM" VARCHAR(100), + "PERIOD3_DATE" TIMESTAMP, + "PERIOD4_MODE" VARCHAR(100), + "PERIOD4_PARAM" VARCHAR(100), + "PERIOD4_DATE" TIMESTAMP, + "PERIOD5_MODE" VARCHAR(100), + "PERIOD5_PARAM" VARCHAR(100), + "PERIOD5_DATE" TIMESTAMP +); diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/584_delete_measures_on_deleted_profiles.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/584_delete_measures_on_deleted_profiles.rb new file mode 100644 index 00000000000..f414494f2ba --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/584_delete_measures_on_deleted_profiles.rb @@ -0,0 +1,30 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2014 SonarSource +# mailto:contact AT sonarsource DOT com +# +# SonarQube 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. +# +# SonarQube 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. +# + +# +# SonarQube 4.5 +# +class DeleteMeasuresOnDeletedProfiles < ActiveRecord::Migration + + def self.up + execute_java_migration 'org.sonar.server.db.migrations.v45.DeleteMeasuresOnDeletedProfilesMigration' + end + +end diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java index ee7e376fa52..105c8c5b58f 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java @@ -33,7 +33,7 @@ import java.util.List; */ public class DatabaseVersion implements BatchComponent, ServerComponent { - public static final int LAST_VERSION = 583; + public static final int LAST_VERSION = 584; public static enum Status { UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/Migration44Mapper.java b/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/Migration44Mapper.java index 19075d6e9db..8bffe19dd17 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/Migration44Mapper.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/Migration44Mapper.java @@ -40,6 +40,8 @@ public interface Migration44Mapper { void updateProfileMeasure(@Param("measureId") long measureId, @Param("json") String json); + void deleteProfileMeasure(long profileMeasureId); + @CheckForNull QProfileDto44 selectProfileById(int id); @@ -56,4 +58,6 @@ public interface Migration44Mapper { // migrate changeLog to Activities List selectActiveRuleChange(@Nullable @Param("enabled") Boolean enabled); + + List selectMeasuresOnDeletedQualityProfiles(); } diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/migration/v44/Migration44Mapper.xml b/sonar-core/src/main/resources/org/sonar/core/persistence/migration/v44/Migration44Mapper.xml index f2c0c203b09..eeb24110187 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/migration/v44/Migration44Mapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/migration/v44/Migration44Mapper.xml @@ -35,6 +35,18 @@ where id=#{measureId} + + delete from project_measures where id=#{id} + + + +