]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10175 db migration clean orphan Quality Gate links from projects 3123/head
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 12 Mar 2018 14:50:58 +0000 (15:50 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 15 Mar 2018 11:08:14 +0000 (12:08 +0100)
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferences.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferencesTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferencesTest/properties_and_quality_gates.sql [new file with mode: 0644]

diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferences.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferences.java
new file mode 100644 (file)
index 0000000..70a4911
--- /dev/null
@@ -0,0 +1,53 @@
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+public class CleanBrokenProjectToQGReferences extends DataChange {
+
+  private static final String PROPERTY_SONAR_QUALITYGATE = "sonar.qualitygate";
+
+  public CleanBrokenProjectToQGReferences(Database db) {
+    super(db);
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    Set<String> qualityGateIds = new HashSet<>();
+    context.prepareSelect("select id from quality_gates")
+      .scroll(s -> qualityGateIds.add(String.valueOf(s.getInt(1))));
+
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("select" +
+      " distinct text_value" +
+      " from properties" +
+      " where" +
+      "  prop_key=?" +
+      "  and text_value is not null" +
+      "  and resource_id is not null")
+      .setString(1, PROPERTY_SONAR_QUALITYGATE);
+    massUpdate.update("delete from properties" +
+      " where" +
+      "  prop_key=?" +
+      "  and resource_id is not null" +
+      "  and text_value=?");
+    massUpdate.execute((row, update) -> handle(row, update, qualityGateIds));
+  }
+
+  private static boolean handle(Select.Row row, SqlStatement update, Set<String> qualityGateIds) throws SQLException {
+    String qgId = row.getString(1);
+    if (qualityGateIds.contains(qgId)) {
+      return false;
+    }
+
+    update.setString(1, PROPERTY_SONAR_QUALITYGATE);
+    update.setString(2, qgId);
+    return true;
+  }
+}
index d8f5d82738c4908d928fe606d69f1fbf91c5e886..0a3ae187728fb007497360ed885a09fc9583c7d9 100644 (file)
@@ -49,6 +49,7 @@ public class DbVersion71 implements DbVersion {
       .add(2019, "Make key_type not nullable in PROJECT_BRANCHES", MakeKeyTypeNotNullableInProjectBranches.class)
       .add(2020, "Replace index in PROJECT_BRANCHES", ReplaceIndexInProjectBranches.class)
       .add(2021, "Add pull_request_data in PROJECT_BRANCHES", AddPullRequestBinaryInProjectBranches.class)
+      .add(2022, "Clean broken project to QG references", CleanBrokenProjectToQGReferences.class)
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferencesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferencesTest.java
new file mode 100644 (file)
index 0000000..233956f
--- /dev/null
@@ -0,0 +1,135 @@
+package org.sonar.server.platform.db.migration.version.v71;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.sql.SQLException;
+import java.util.Random;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class CleanBrokenProjectToQGReferencesTest {
+
+  private static final String PROPERTY_SONAR_QUALITYGATE = "sonar.qualitygate";
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(CleanBrokenProjectToQGReferencesTest.class, "properties_and_quality_gates.sql");
+
+  private CleanBrokenProjectToQGReferences underTest = new CleanBrokenProjectToQGReferences(db.database());
+
+  @Test
+  public void do_nothing_when_no_data() throws SQLException {
+    assertThat(db.countRowsOfTable("PROPERTIES")).isEqualTo(0);
+
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable("PROPERTIES")).isEqualTo(0);
+  }
+
+  @Test
+  public void execute_deletes_all_qualitygate_component_properties_when_there_is_no_qualitygate() throws SQLException {
+    insertProperty(PROPERTY_SONAR_QUALITYGATE, 30, "12");
+    insertProperty(PROPERTY_SONAR_QUALITYGATE, 42, "val1");
+    insertProperty(PROPERTY_SONAR_QUALITYGATE, null, "val2");
+
+    underTest.execute();
+
+    assertThat(selectPropertyValues()).containsOnly("val2");
+    assertThat(db.countRowsOfTable("PROPERTIES")).isEqualTo(1);
+  }
+
+  @Test
+  @UseDataProvider("DP_execute_deletes_qualitygate_component_properties_for_non_existing_qualitygate")
+  public void execute_deletes_qualitygate_component_properties_for_non_existing_qualitygate(int existingQualityGateCount, int missingQualityGateCount) throws SQLException {
+    String[] qualityGateIds = IntStream.range(0, existingQualityGateCount)
+      .mapToObj(i -> insertQualityGate())
+      .map(s -> {
+        int componentId = 2 + s;
+        String qualityGateId = String.valueOf(s);
+        insertProperty(PROPERTY_SONAR_QUALITYGATE, componentId, qualityGateId);
+        return qualityGateId;
+      })
+      .toArray(String[]::new);
+    IntStream.range(0, missingQualityGateCount)
+      .forEach(i -> {
+        int componentId = 3_000 + i;
+        insertProperty(PROPERTY_SONAR_QUALITYGATE, componentId, "non_existing_" + i);
+      });
+
+    underTest.execute();
+
+    assertThat(selectPropertyValues()).containsOnly(qualityGateIds);
+    assertThat(db.countRowsOfTable("PROPERTIES")).isEqualTo(qualityGateIds.length);
+  }
+
+  @DataProvider
+  public static Object[][] DP_execute_deletes_qualitygate_component_properties_for_non_existing_qualitygate() {
+    Random random = new Random();
+    return new Object[][] {
+      {1, 1},
+      {1, 2},
+      {2, 1},
+      {2 + random.nextInt(5), 1 + random.nextInt(5)},
+    };
+  }
+
+  @Test
+  public void execute_deletes_only_project_qualitygate_property() throws SQLException {
+    String qualityGateId = String.valueOf(insertQualityGate());
+    insertProperty(PROPERTY_SONAR_QUALITYGATE, 84651, qualityGateId);
+    insertProperty(PROPERTY_SONAR_QUALITYGATE, 7_323, "does_not_exist");
+    insertProperty(PROPERTY_SONAR_QUALITYGATE, null, "not a project property");
+    insertProperty(PROPERTY_SONAR_QUALITYGATE, null, "not_a_qualitygate_id_either");
+
+    underTest.execute();
+
+    assertThat(selectPropertyValues()).containsExactly(qualityGateId, "not a project property", "not_a_qualitygate_id_either");
+    assertThat(db.countRowsOfTable("PROPERTIES")).isEqualTo(3);
+  }
+
+  @Test
+  public void execute_deletes_only_qualitygate_property_for_project() throws SQLException {
+    String qualityGateId = String.valueOf(insertQualityGate());
+    insertProperty(PROPERTY_SONAR_QUALITYGATE, 84651, qualityGateId);
+    insertProperty("FOO", 84651, "does_not_exist");
+
+    underTest.execute();
+
+    assertThat(selectPropertyValues()).containsExactly(qualityGateId, "does_not_exist");
+    assertThat(db.countRowsOfTable("PROPERTIES")).isEqualTo(2);
+  }
+
+  private Stream<String> selectPropertyValues() {
+    return db.select("select text_value as \"value\" from properties").stream().map(s -> (String) s.get("value"));
+  }
+
+  private void insertProperty(String key, @Nullable Integer componentId, String value) {
+    db.executeInsert(
+      "PROPERTIES",
+      "PROP_KEY", key,
+      "RESOURCE_ID", componentId,
+      "IS_EMPTY", value.isEmpty(),
+      "TEXT_VALUE", value);
+  }
+
+  private static int qualityGateIdGenerator = 2_999_567 + new Random().nextInt(56);
+
+  private int insertQualityGate() {
+    int id = qualityGateIdGenerator++;
+    db.executeInsert(
+      "QUALITY_GATES",
+      "ID", id,
+      "UUID", "uuid_" + id,
+      "NAME", "name_" + id,
+      "IS_BUILT_IN", new Random().nextBoolean());
+    return id;
+  }
+}
index f50e8910b81d20ace172178fa81115ec85b49936..404df04b07e30789dc36f1624df66788ee21a97d 100644 (file)
@@ -36,7 +36,7 @@ public class DbVersion71Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 22);
+    verifyMigrationCount(underTest, 23);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferencesTest/properties_and_quality_gates.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/CleanBrokenProjectToQGReferencesTest/properties_and_quality_gates.sql
new file mode 100644 (file)
index 0000000..7e565f1
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE TABLE "PROPERTIES" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "PROP_KEY" VARCHAR(512) NOT NULL,
+  "RESOURCE_ID" INTEGER,
+  "USER_ID" INTEGER,
+  "IS_EMPTY" BOOLEAN NOT NULL,
+  "TEXT_VALUE" VARCHAR(4000),
+  "CLOB_VALUE" CLOB,
+  "CREATED_AT" BIGINT
+);
+CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY");
+
+CREATE TABLE "QUALITY_GATES" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(40) NOT NULL,
+  "NAME" VARCHAR(100) NOT NULL,
+  "IS_BUILT_IN" BOOLEAN NOT NULL,
+  "CREATED_AT" TIMESTAMP,
+  "UPDATED_AT" TIMESTAMP,
+);
+CREATE UNIQUE INDEX "UNIQ_QUALITY_GATES_UUID" ON "QUALITY_GATES" ("UUID");