From 1fcc06ab16625cf86cf44985079134d914214d9c Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Mon, 29 May 2017 17:31:39 +0200 Subject: [PATCH] SONAR-9326 ensure ISSUES.PROJECT_UUID is consistent with component --- .../db/migration/version/v65/DbVersion65.java | 3 +- ...reIssueProjectUuidConsistencyOnIssues.java | 57 +++++++++ .../version/v65/DbVersion65Test.java | 2 +- ...sueProjectUuidConsistencyOnIssuesTest.java | 115 ++++++++++++++++++ .../issues_and_projects.sql | 83 +++++++++++++ 5 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssues.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssuesTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssuesTest/issues_and_projects.sql diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65.java index a4261fd13fa..b5dcb1b5cb9 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65.java @@ -33,6 +33,7 @@ public class DbVersion65 implements DbVersion { .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); + .add(1706, "Recreate index EVENTS_COMPONENT_UUID", RecreateIndexEventsComponentUuid.class) + .add(1707, "Ensure ISSUE.PROJECT_UUID is consistent", EnsureIssueProjectUuidConsistencyOnIssues.class); } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssues.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssues.java new file mode 100644 index 00000000000..b40f0914180 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssues.java @@ -0,0 +1,57 @@ +/* + * 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 EnsureIssueProjectUuidConsistencyOnIssues extends DataChange { + public EnsureIssueProjectUuidConsistencyOnIssues(Database db) { + super(db); + } + + @Override + protected void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("select" + + " i.id, p.project_uuid" + + " from issues i" + + " inner join projects p on" + + " p.uuid = i.component_uuid" + + " and i.project_uuid <> p.project_uuid"); + massUpdate.update("update issues set project_uuid = ? where id = ?"); + massUpdate.rowPluralName("issues with inconsistent project_uuid"); + massUpdate.execute(EnsureIssueProjectUuidConsistencyOnIssues::handle); + } + + private static boolean handle(Select.Row row, SqlStatement update) throws SQLException { + long issueId = row.getLong(1); + String projectUuid = row.getString(2); + + update.setString(1, projectUuid); + update.setLong(2, issueId); + + return true; + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65Test.java index 1ff63d12cb9..f63f9942ec0 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/DbVersion65Test.java @@ -35,6 +35,6 @@ public class DbVersion65Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 7); + verifyMigrationCount(underTest, 8); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssuesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssuesTest.java new file mode 100644 index 00000000000..6096ff2984c --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssuesTest.java @@ -0,0 +1,115 @@ +/* + * 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 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 EnsureIssueProjectUuidConsistencyOnIssuesTest { + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(EnsureIssueProjectUuidConsistencyOnIssuesTest.class, "issues_and_projects.sql"); + + private final Random random = new Random(); + private EnsureIssueProjectUuidConsistencyOnIssues underTest = new EnsureIssueProjectUuidConsistencyOnIssues(db.database()); + + @Test + public void execute_has_no_effect_if_tables_are_empty() throws SQLException { + underTest.execute(); + } + + @Test + public void execute_fixes_project_uuid_of_issue_when_inconsistent_with_PROJECTS_PROJECT_UUID() throws SQLException { + String projectUuid = randomAlphabetic(5); + String componentUuid = insertComponent(projectUuid); + String inconsistentIssueKey = insertIssue(componentUuid, randomAlphabetic(9)); + String consistentIssueKey = insertIssue(componentUuid, projectUuid); + + underTest.execute(); + + assertThat(getProjectUuid(inconsistentIssueKey)).isEqualTo(projectUuid); + assertThat(getProjectUuid(consistentIssueKey)).isEqualTo(projectUuid); + } + + @Test + public void execute_ignores_issues_which_component_does_not_exist() throws SQLException { + String projectUuid = randomAlphabetic(3); + String issueKey = insertIssue(randomAlphabetic(8), projectUuid); + + underTest.execute(); + + assertThat(getProjectUuid(issueKey)).isEqualTo(projectUuid); + } + + @Test + public void execute_ignores_issues_which_null_project_uuid() throws SQLException { + String issueKey = insertIssue(randomAlphabetic(8), null); + + underTest.execute(); + + assertThat(getProjectUuid(issueKey)).isNull(); + } + + @Test + public void execute_ignores_issues_which_null_component_uuid() throws SQLException { + String projectUuid = randomAlphabetic(5); + String issueKey = insertIssue(null, projectUuid); + + underTest.execute(); + + assertThat(getProjectUuid(issueKey)).isEqualTo(projectUuid); + } + + private String getProjectUuid(String issueKey) { + return (String) db.selectFirst("select project_uuid as \"PROJECT_UUID\" from issues where kee = '" + issueKey + "'") + .get("PROJECT_UUID"); + } + + private String insertIssue(@Nullable String componentUuid, @Nullable String projectUuid) { + String issueKey = randomAlphabetic(3); + db.executeInsert( + "ISSUES", + "KEE", issueKey, + "COMPONENT_UUID", componentUuid, + "PROJECT_UUID", projectUuid, + "MANUAL_SEVERITY", String.valueOf(random.nextBoolean())); + return issueKey; + } + + private String insertComponent(String projectUuid) { + String uuid = randomAlphabetic(5); + db.executeInsert( + "PROJECTS", + "ORGANIZATION_UUID", randomAlphabetic(5), + "UUID", uuid, + "UUID_PATH", "path_" + uuid, + "ROOT_UUID", "root_uuid_" + uuid, + "PROJECT_UUID", projectUuid, + "PRIVATE", String.valueOf(random.nextBoolean()), + "ENABLED", String.valueOf(random.nextBoolean())); + return uuid; + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssuesTest/issues_and_projects.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssuesTest/issues_and_projects.sql new file mode 100644 index 00000000000..6722d72cd58 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v65/EnsureIssueProjectUuidConsistencyOnIssuesTest/issues_and_projects.sql @@ -0,0 +1,83 @@ +CREATE TABLE "ISSUES" ( + "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "KEE" VARCHAR(50) UNIQUE NOT NULL, + "COMPONENT_UUID" VARCHAR(50), + "PROJECT_UUID" VARCHAR(50), + "RULE_ID" INTEGER, + "SEVERITY" VARCHAR(10), + "MANUAL_SEVERITY" BOOLEAN NOT NULL, + "MESSAGE" VARCHAR(4000), + "LINE" INTEGER, + "GAP" DOUBLE, + "EFFORT" INTEGER, + "STATUS" VARCHAR(20), + "RESOLUTION" VARCHAR(20), + "CHECKSUM" VARCHAR(1000), + "REPORTER" VARCHAR(255), + "ASSIGNEE" VARCHAR(255), + "AUTHOR_LOGIN" VARCHAR(255), + "ACTION_PLAN_KEY" VARCHAR(50) NULL, + "ISSUE_ATTRIBUTES" VARCHAR(4000), + "TAGS" VARCHAR(4000), + "ISSUE_CREATION_DATE" BIGINT, + "ISSUE_CLOSE_DATE" BIGINT, + "ISSUE_UPDATE_DATE" BIGINT, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT, + "LOCATIONS" BLOB, + "ISSUE_TYPE" TINYINT +); +CREATE UNIQUE INDEX "ISSUES_KEE" ON "ISSUES" ("KEE"); +CREATE INDEX "ISSUES_COMPONENT_UUID" ON "ISSUES" ("COMPONENT_UUID"); +CREATE INDEX "ISSUES_PROJECT_UUID" ON "ISSUES" ("PROJECT_UUID"); +CREATE INDEX "ISSUES_RULE_ID" ON "ISSUES" ("RULE_ID"); +CREATE INDEX "ISSUES_RESOLUTION" ON "ISSUES" ("RESOLUTION"); +CREATE INDEX "ISSUES_ASSIGNEE" ON "ISSUES" ("ASSIGNEE"); +CREATE INDEX "ISSUES_CREATION_DATE" ON "ISSUES" ("ISSUE_CREATION_DATE"); +CREATE INDEX "ISSUES_UPDATED_AT" ON "ISSUES" ("UPDATED_AT"); + +CREATE TABLE "PROJECTS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "ORGANIZATION_UUID" VARCHAR(40) NOT NULL, + "KEE" VARCHAR(400), + "UUID" VARCHAR(50) NOT NULL, + "UUID_PATH" VARCHAR(1500) NOT NULL, + "ROOT_UUID" VARCHAR(50) NOT NULL, + "PROJECT_UUID" VARCHAR(50) NOT NULL, + "MODULE_UUID" VARCHAR(50), + "MODULE_UUID_PATH" VARCHAR(1500), + "NAME" VARCHAR(2000), + "DESCRIPTION" VARCHAR(2000), + "PRIVATE" BOOLEAN NOT NULL, + "TAGS" VARCHAR(500), + "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE, + "SCOPE" VARCHAR(3), + "QUALIFIER" VARCHAR(10), + "DEPRECATED_KEE" VARCHAR(400), + "PATH" VARCHAR(2000), + "LANGUAGE" VARCHAR(20), + "COPY_COMPONENT_UUID" VARCHAR(50), + "LONG_NAME" VARCHAR(2000), + "DEVELOPER_UUID" VARCHAR(50), + "CREATED_AT" TIMESTAMP, + "AUTHORIZATION_UPDATED_AT" BIGINT, + "B_CHANGED" BOOLEAN, + "B_COPY_COMPONENT_UUID" VARCHAR(50), + "B_DESCRIPTION" VARCHAR(2000), + "B_ENABLED" BOOLEAN, + "B_UUID_PATH" VARCHAR(1500), + "B_LANGUAGE" VARCHAR(20), + "B_LONG_NAME" VARCHAR(500), + "B_MODULE_UUID" VARCHAR(50), + "B_MODULE_UUID_PATH" VARCHAR(1500), + "B_NAME" VARCHAR(500), + "B_PATH" VARCHAR(2000), + "B_QUALIFIER" VARCHAR(10) +); +CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID"); +CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE"); +CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID"); +CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID"); +CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID"); +CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID"); +CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER"); -- 2.39.5