]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9334 make EVENTS.COMPONENT_UUID not nullable and clean orphans
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 29 May 2017 09:32:11 +0000 (11:32 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 31 May 2017 12:55:38 +0000 (14:55 +0200)
17 files changed:
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java
server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldDeleteResource.xml
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuid.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuid.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuid.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuidTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuidTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuidTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuidTest/events.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullableTest/events.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuidTest/events_and_snapshots.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuidTest/events.sql [new file with mode: 0644]

index 6ed8be21276c301c21bce71274c4af8468f50fc9..2e487af0391243ae4bfd53f9c067fb4b0019e575 100644 (file)
@@ -172,7 +172,7 @@ CREATE TABLE "EVENTS" (
   "UUID" VARCHAR(40) NOT NULL,
   "NAME" VARCHAR(400),
   "ANALYSIS_UUID" VARCHAR(50) NOT NULL,
-  "COMPONENT_UUID" VARCHAR(50),
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
   "CATEGORY" VARCHAR(50),
   "EVENT_DATE" BIGINT NOT NULL,
   "CREATED_AT" BIGINT NOT NULL,
index c2586d6c7b3657968e9908feff63984a59d19fa7..2a33ea8c06d66c8eb0360a1656842b1ab86a77ec 100644 (file)
@@ -90,7 +90,7 @@ public class PurgeCommandsTest {
 
     assertThat(dbTester.countRowsOfTable("projects")).isZero();
     assertThat(dbTester.countRowsOfTable("snapshots")).isEqualTo(1);
-    assertThat(dbTester.countRowsOfTable("events")).isEqualTo(2);
+    assertThat(dbTester.countRowsOfTable("events")).isZero();
     assertThat(dbTester.countRowsOfTable("issues")).isZero();
     assertThat(dbTester.countRowsOfTable("issue_changes")).isZero();
   }
index 1a269703e3c0c4cedf2b0d97ee89b4d3a2cec02b..2d521a1fded64c9526c4f47aa59f958ec2980db1 100644 (file)
@@ -58,7 +58,7 @@
   <events id="2"
           uuid="P2"
           analysis_uuid="u1"
-          component_uuid="[null]"
+          component_uuid="uuid_1"
           name="Version 1.0"
           category="VERSION"
           description="[null]"
@@ -69,7 +69,7 @@
   <events id="3"
           uuid="P3"
           analysis_uuid="u1"
-          component_uuid="[null]"
+          component_uuid="uuid_1"
           name="Version 1.0"
           category="VERSION"
           description="[null]"
index df3ac853930b122a2c6fb7336d72a9fafdb1401c..a4261fd13fa7075658155d1a3fba6d91d86065cb 100644 (file)
@@ -29,6 +29,10 @@ public class DbVersion65 implements DbVersion {
     registry
       .add(1700, "Drop table AUTHORS", DropTableAuthors.class)
       .add(1701, "Clean orphans from USER_ROLES", CleanOrphanRowsInUserRoles.class)
-      .add(1702, "Clean orphans from GROUP_ROLES", CleanOrphanRowsInGroupRoles.class);
+      .add(1702, "Clean orphans from GROUP_ROLES", CleanOrphanRowsInGroupRoles.class)
+      .add(1703, "Populate EVENTS.COMPONENT_UUID", PopulateEventsComponentUuid.class)
+      .add(1704, "Drop index EVENTS_COMPONENT_UUID", DropIndexEventsComponentUuid.class)
+      .add(1705, "Make EVENTS.COMPONENT_UUID not nullable", MakeEventsComponentUuidNotNullable.class)
+      .add(1706, "Recreate index EVENTS_COMPONENT_UUID", RecreateIndexEventsComponentUuid.class);
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuid.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuid.java
new file mode 100644 (file)
index 0000000..3a37d2f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v65;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.DropIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class DropIndexEventsComponentUuid extends DdlChange {
+
+  public DropIndexEventsComponentUuid(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new DropIndexBuilder(getDialect())
+      .setTable("events")
+      .setName("events_component_uuid")
+      .build());
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullable.java
new file mode 100644 (file)
index 0000000..5fe11cf
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v65;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class MakeEventsComponentUuidNotNullable extends DdlChange {
+
+  public MakeEventsComponentUuidNotNullable(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new AlterColumnsBuilder(getDialect(), "events")
+      .updateColumn(newVarcharColumnDefBuilder()
+        .setColumnName("component_uuid")
+        .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
+        .setIsNullable(false)
+        .build())
+      .build());
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuid.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuid.java
new file mode 100644 (file)
index 0000000..90fee0f
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v65;
+
+import java.sql.SQLException;
+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 PopulateEventsComponentUuid extends DataChange {
+  public PopulateEventsComponentUuid(Database db) {
+    super(db);
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    populateColumnOrFixInconsistencies(context);
+
+    deleteOrphans(context);
+  }
+
+  private static void populateColumnOrFixInconsistencies(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("select" +
+      "   e.id, s.component_uuid" +
+      " from events e" +
+      " inner join snapshots s on" +
+      "   s.uuid = e.analysis_uuid" +
+      " where" +
+      "   e.component_uuid is null" +
+      "   or e.component_uuid <> s.uuid");
+    massUpdate.update("update events set component_uuid = ? where id = ?");
+    massUpdate.rowPluralName("events without component_uuid");
+    massUpdate.execute(PopulateEventsComponentUuid::handlePopulate);
+  }
+
+  private static boolean handlePopulate(Select.Row row, SqlStatement update) throws SQLException {
+    long id = row.getLong(1);
+    String projectUuid = row.getString(2);
+
+    update.setString(1, projectUuid);
+    update.setLong(2, id);
+
+    return true;
+  }
+
+  private static void deleteOrphans(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("select id from events e where" +
+      " e.component_uuid is null" +
+      " or not exists (select id from snapshots s where s.uuid = e.analysis_uuid)");
+    massUpdate.update("delete from events where id = ?");
+    massUpdate.rowPluralName("delete orphan events");
+    massUpdate.execute(PopulateEventsComponentUuid::handleDelete);
+  }
+
+  private static boolean handleDelete(Select.Row row, SqlStatement update) throws SQLException {
+    long id = row.getLong(1);
+
+    update.setLong(1, id);
+
+    return true;
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuid.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuid.java
new file mode 100644 (file)
index 0000000..71d91b8
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v65;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class RecreateIndexEventsComponentUuid extends DdlChange {
+
+  public RecreateIndexEventsComponentUuid(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable("events")
+      .setName("events_component_uuid")
+      .setUnique(false)
+      .addColumn(newVarcharColumnDefBuilder()
+        .setColumnName("component_uuid")
+        .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
+        .setIsNullable(false)
+        .build())
+      .build());
+  }
+}
index fa2bfdb7dcbd797614ff934a7dc00984f089ac30..1ff63d12cb9d69c9bbd08bd58f9df2e787f8c878 100644 (file)
@@ -35,6 +35,6 @@ public class DbVersion65Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 3);
+    verifyMigrationCount(underTest, 7);
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuidTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuidTest.java
new file mode 100644 (file)
index 0000000..dda3a87
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v65;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+public class DropIndexEventsComponentUuidTest {
+  private static final String TABLE_EVENTS = "events";
+  private static final String INDEX_EVENTS_COMPONENT_UUID = "events_component_uuid";
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(DropIndexEventsComponentUuidTest.class, "events.sql");
+
+  private DropIndexEventsComponentUuid underTest = new DropIndexEventsComponentUuid(db.database());
+
+  @Test
+  public void execute_drops_index_EVENTS_COMPONENT_UUID() throws SQLException {
+    db.assertIndex(TABLE_EVENTS, INDEX_EVENTS_COMPONENT_UUID, "component_uuid");
+
+    underTest.execute();
+
+    db.assertIndexDoesNotExist(TABLE_EVENTS, INDEX_EVENTS_COMPONENT_UUID);
+  }
+}
+
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullableTest.java
new file mode 100644 (file)
index 0000000..d762665
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v65;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+
+public class MakeEventsComponentUuidNotNullableTest {
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(MakeEventsComponentUuidNotNullableTest.class, "events.sql");
+
+  private final Random random = new Random();
+  private MakeEventsComponentUuidNotNullable underTest = new MakeEventsComponentUuidNotNullable(db.database());
+
+  @Test
+  public void execute_makes_column_component_uuid_not_nullable_on_empty_table() throws SQLException {
+    underTest.execute();
+
+    verifyColumn();
+  }
+
+  @Test
+  public void execute_makes_column_component_uuid_not_nullable_on_populated_table() throws SQLException {
+    insertEvent();
+    insertEvent();
+    insertEvent();
+
+    underTest.execute();
+
+    verifyColumn();
+  }
+
+  private void verifyColumn() {
+    db.assertColumnDefinition("events", "component_uuid", Types.VARCHAR, 50, false);
+  }
+
+  private void insertEvent() {
+    db.executeInsert(
+      "events",
+      "UUID", randomAlphabetic(5),
+      "ANALYSIS_UUID", randomAlphabetic(5),
+      "COMPONENT_UUID", randomAlphabetic(5),
+      "EVENT_DATE", random.nextInt(),
+      "CREATED_AT", random.nextInt());
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuidTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuidTest.java
new file mode 100644 (file)
index 0000000..031ca96
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v65;
+
+import java.sql.SQLException;
+import java.util.Random;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PopulateEventsComponentUuidTest {
+  private static final String TABLE_EVENTS = "events";
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(PopulateEventsComponentUuidTest.class, "events_and_snapshots.sql");
+
+  private final Random random = new Random();
+
+  private PopulateEventsComponentUuid underTest = new PopulateEventsComponentUuid(db.database());
+
+  @Test
+  public void execute_has_no_effect_if_table_is_empty() throws SQLException {
+    underTest.execute();
+  }
+
+  @Test
+  public void execute_deletes_all_events_if_snapshots_is_empty() throws SQLException {
+    insertEvent(randomAlphabetic(5), null);
+    insertEvent(randomAlphabetic(5), randomAlphabetic(10));
+
+    underTest.execute();
+
+    assertThat(getAllEventUuids()).isEmpty();
+  }
+
+  @Test
+  public void execute_populates_components_uuid_of_events_which_analysis_exists() throws SQLException {
+    String componentUuid = "foo";
+    String existingAnalysisUuid = insertSnapshot(componentUuid);
+    String missingAnalysisUuid = randomAlphabetic(5);
+    String uuid = insertEvent(existingAnalysisUuid, null);
+    insertEvent(missingAnalysisUuid, null);
+
+    underTest.execute();
+
+    assertThat(getAllEventUuids()).containsOnly(uuid);
+    assertThat(getComponentUuidOf(uuid)).isEqualTo(componentUuid);
+  }
+
+  @Test
+  public void execute_fixes_inconsistent_component_uuid_of_event_which_analysis_exists() throws SQLException {
+    String analysisUuid1 = insertSnapshot("foo");
+    String analysisUuid2 = insertSnapshot("bar");
+    String eventUuid1 = insertEvent(analysisUuid1, "foo");
+    String eventUuid2 = insertEvent(analysisUuid2, "moh");
+
+    underTest.execute();
+
+    assertThat(getAllEventUuids()).containsOnly(eventUuid1, eventUuid2);
+    assertThat(getComponentUuidOf(eventUuid1)).isEqualTo("foo");
+    assertThat(getComponentUuidOf(eventUuid2)).isEqualTo("bar");
+  }
+
+  @Test
+  public void execute_deletes_events_without_component_uuid_which_analysis_does_not_exist() throws SQLException {
+    String analysisUuid1 = insertSnapshot("foo");
+    String eventUuid1 = insertEvent(analysisUuid1, "foo");
+    insertEvent(randomAlphabetic(3), "moh");
+    insertEvent(randomAlphabetic(3), null);
+
+    underTest.execute();
+
+    assertThat(getAllEventUuids()).containsOnly(eventUuid1);
+  }
+
+  private String insertSnapshot(String componentUuid) {
+    String uuid = randomAlphabetic(10);
+    db.executeInsert(
+      "snapshots",
+      "UUID", uuid,
+      "COMPONENT_UUID", componentUuid,
+      "STATUS", "U",
+      "ISLAST", String.valueOf(true));
+    return uuid;
+  }
+
+  private String insertEvent(String analysisUuid, @Nullable String componentUuid) {
+    String uuid = randomAlphabetic(5);
+    db.executeInsert(
+      TABLE_EVENTS,
+      "UUID", uuid,
+      "ANALYSIS_UUID", analysisUuid,
+      "COMPONENT_UUID", componentUuid,
+      "EVENT_DATE", random.nextInt(),
+      "CREATED_AT", random.nextInt());
+    return uuid;
+  }
+
+  private Stream<String> getAllEventUuids() {
+    return db.select("select uuid as \"UUID\" from events").stream().map(row -> (String) row.get("UUID"));
+  }
+
+  private String getComponentUuidOf(String eventUuid) {
+    return (String) db.selectFirst("select component_uuid as \"COMPONENT_UUID\" from events where uuid = '" + eventUuid + "'")
+        .get("COMPONENT_UUID");
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuidTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuidTest.java
new file mode 100644 (file)
index 0000000..f294cc7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v65;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+public class RecreateIndexEventsComponentUuidTest {
+  private static final String TABLE_EVENTS = "events";
+  private static final String INDEX_EVENTS_COMPONENT_UUID = "events_component_uuid";
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(RecreateIndexEventsComponentUuidTest.class, "events.sql");
+
+  private RecreateIndexEventsComponentUuid underTest = new RecreateIndexEventsComponentUuid(db.database());
+
+  @Test
+  public void execute_adds_index_EVENTS_COMPONENT_UUID() throws SQLException {
+    db.assertIndexDoesNotExist(TABLE_EVENTS, INDEX_EVENTS_COMPONENT_UUID);
+
+    underTest.execute();
+
+    db.assertIndex(TABLE_EVENTS, INDEX_EVENTS_COMPONENT_UUID, "component_uuid");
+  }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuidTest/events.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/DropIndexEventsComponentUuidTest/events.sql
new file mode 100644 (file)
index 0000000..a4d0d1f
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE TABLE "EVENTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(40) NOT NULL,
+  "NAME" VARCHAR(400),
+  "ANALYSIS_UUID" VARCHAR(50) NOT NULL,
+  "COMPONENT_UUID" VARCHAR(50),
+  "CATEGORY" VARCHAR(50),
+  "EVENT_DATE" BIGINT NOT NULL,
+  "CREATED_AT" BIGINT NOT NULL,
+  "DESCRIPTION" VARCHAR(4000),
+  "EVENT_DATA"  VARCHAR(4000)
+);
+CREATE INDEX "EVENTS_ANALYSIS" ON "EVENTS" ("ANALYSIS_UUID");
+CREATE INDEX "EVENTS_COMPONENT_UUID" ON "EVENTS" ("COMPONENT_UUID");
+CREATE UNIQUE INDEX "EVENTS_UUID" ON "EVENTS" ("UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullableTest/events.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/MakeEventsComponentUuidNotNullableTest/events.sql
new file mode 100644 (file)
index 0000000..a4d0d1f
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE TABLE "EVENTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(40) NOT NULL,
+  "NAME" VARCHAR(400),
+  "ANALYSIS_UUID" VARCHAR(50) NOT NULL,
+  "COMPONENT_UUID" VARCHAR(50),
+  "CATEGORY" VARCHAR(50),
+  "EVENT_DATE" BIGINT NOT NULL,
+  "CREATED_AT" BIGINT NOT NULL,
+  "DESCRIPTION" VARCHAR(4000),
+  "EVENT_DATA"  VARCHAR(4000)
+);
+CREATE INDEX "EVENTS_ANALYSIS" ON "EVENTS" ("ANALYSIS_UUID");
+CREATE INDEX "EVENTS_COMPONENT_UUID" ON "EVENTS" ("COMPONENT_UUID");
+CREATE UNIQUE INDEX "EVENTS_UUID" ON "EVENTS" ("UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuidTest/events_and_snapshots.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/PopulateEventsComponentUuidTest/events_and_snapshots.sql
new file mode 100644 (file)
index 0000000..1aff12a
--- /dev/null
@@ -0,0 +1,44 @@
+CREATE TABLE "EVENTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(40) NOT NULL,
+  "NAME" VARCHAR(400),
+  "ANALYSIS_UUID" VARCHAR(50) NOT NULL,
+  "COMPONENT_UUID" VARCHAR(50),
+  "CATEGORY" VARCHAR(50),
+  "EVENT_DATE" BIGINT NOT NULL,
+  "CREATED_AT" BIGINT NOT NULL,
+  "DESCRIPTION" VARCHAR(4000),
+  "EVENT_DATA"  VARCHAR(4000)
+);
+CREATE INDEX "EVENTS_ANALYSIS" ON "EVENTS" ("ANALYSIS_UUID");
+CREATE INDEX "EVENTS_COMPONENT_UUID" ON "EVENTS" ("COMPONENT_UUID");
+CREATE UNIQUE INDEX "EVENTS_UUID" ON "EVENTS" ("UUID");
+
+CREATE TABLE "SNAPSHOTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(50) NOT NULL,
+  "CREATED_AT" BIGINT,
+  "BUILD_DATE" BIGINT,
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+  "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+  "PURGE_STATUS" INTEGER,
+  "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+  "VERSION" VARCHAR(500),
+  "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 INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
+CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuidTest/events.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/RecreateIndexEventsComponentUuidTest/events.sql
new file mode 100644 (file)
index 0000000..08f78db
--- /dev/null
@@ -0,0 +1,14 @@
+CREATE TABLE "EVENTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(40) NOT NULL,
+  "NAME" VARCHAR(400),
+  "ANALYSIS_UUID" VARCHAR(50) NOT NULL,
+  "COMPONENT_UUID" VARCHAR(50),
+  "CATEGORY" VARCHAR(50),
+  "EVENT_DATE" BIGINT NOT NULL,
+  "CREATED_AT" BIGINT NOT NULL,
+  "DESCRIPTION" VARCHAR(4000),
+  "EVENT_DATA"  VARCHAR(4000)
+);
+CREATE INDEX "EVENTS_ANALYSIS" ON "EVENTS" ("ANALYSIS_UUID");
+CREATE UNIQUE INDEX "EVENTS_UUID" ON "EVENTS" ("UUID");