diff options
Diffstat (limited to 'server/sonar-db-migration/src')
6 files changed, 370 insertions, 0 deletions
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java index 54bb7543d30..47d6f2d7f20 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java @@ -32,6 +32,7 @@ import org.sonar.server.platform.db.migration.version.v100.DbVersion100; import org.sonar.server.platform.db.migration.version.v101.DbVersion101; import org.sonar.server.platform.db.migration.version.v102.DbVersion102; import org.sonar.server.platform.db.migration.version.v103.DbVersion103; +import org.sonar.server.platform.db.migration.version.v104.DbVersion104; public class MigrationConfigurationModule extends Module { @Override @@ -44,6 +45,7 @@ public class MigrationConfigurationModule extends Module { DbVersion101.class, DbVersion102.class, DbVersion103.class, + DbVersion104.class, // migration steps MigrationStepRegistryImpl.class, diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104.java new file mode 100644 index 00000000000..9ea5eb0fc90 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v104; + +import org.sonar.server.platform.db.migration.step.MigrationStepRegistry; +import org.sonar.server.platform.db.migration.version.DbVersion; + +// ignoring bad number formatting, as it's indented that we align the migration numbers to SQ versions +@SuppressWarnings("java:S3937") +public class DbVersion104 implements DbVersion { + + /** + * We use the start of the 10.X cycle as an opportunity to align migration numbers with the SQ version number. + * Please follow this pattern: + * 10_0_000 + * 10_0_001 + * 10_0_002 + * 10_1_000 + * 10_1_001 + * 10_1_002 + * 10_2_000 + */ + + @Override + public void addSteps(MigrationStepRegistry registry) { + registry + .add(10_4_000, "Delete redundant Failed Alerts for Applications", DeleteRedundantFailedAlertsForApplications.class); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplications.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplications.java new file mode 100644 index 00000000000..484b48de258 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplications.java @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v104; + +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; + +public class DeleteRedundantFailedAlertsForApplications extends DataChange { + + private static final String SELECT_QUERY = """ + SELECT E.uuid + FROM events E + JOIN components C ON E.component_uuid = C.uuid + WHERE E.name = 'Failed' + AND E.category = 'Alert' + AND E.event_data = '{ stillFailing: false, status: "ERROR" }' + AND C.qualifier = 'APP'"""; + + private static final String DELETE_EVENTS_STATEMENT = "DELETE FROM events WHERE uuid = ?"; + private static final String DELETE_EVENTS_COMPONENT_CHANGES_STATEMENT = "DELETE FROM event_component_changes WHERE event_uuid = ?"; + + public DeleteRedundantFailedAlertsForApplications(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + var select = massUpdate.select(SELECT_QUERY); + var deleteEventComponentChanges = massUpdate.update(DELETE_EVENTS_COMPONENT_CHANGES_STATEMENT); + var deleteEvents = massUpdate.update(DELETE_EVENTS_STATEMENT); + try (select; deleteEventComponentChanges; deleteEvents) { + massUpdate.execute((row, delete, index) -> { + // both updates use the same select, so no need to differentiate for the 2 update indexes + String uuid = row.getString(1); + delete.setString(1, uuid); + return true; + }); + } + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104Test.java new file mode 100644 index 00000000000..39fa506fde3 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104Test.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v104; + +import org.junit.Test; + +import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationNotEmpty; +import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber; + +public class DbVersion104Test { + + private final DbVersion104 underTest = new DbVersion104(); + + @Test + public void migrationNumber_starts_at_10_4_000() { + verifyMinimumMigrationNumber(underTest, 10_4_000); + } + + @Test + public void verify_migration_is_not_empty() { + verifyMigrationNotEmpty(underTest); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest.java new file mode 100644 index 00000000000..3c5f92551bf --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest.java @@ -0,0 +1,157 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v104; + +import java.sql.SQLException; +import java.util.Map; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DataChange; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DeleteRedundantFailedAlertsForApplicationsTest { + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(DeleteRedundantFailedAlertsForApplicationsTest.class, "schema.sql"); + private final DataChange underTest = new DeleteRedundantFailedAlertsForApplications(db.database()); + + @Before + public void setUp() { + // cleanup db + db.executeUpdateSql("truncate table events"); + db.executeUpdateSql("truncate table event_component_changes"); + db.executeUpdateSql("truncate table components"); + } + + @Test + public void givenFailedAlertsForApplication_whenExecuted_thenFailedAlertsAreDeleted() throws SQLException { + // given + insertComponent("app1", "appUuid1", "appUuid1", "APP"); + + // event that should be deleted + insertEvent("eventUuid1", "appUuid1", "Failed", "Alert", "{ stillFailing: false, status: \"ERROR\" }"); + insertEventChanges("eventChangeUuid1", "eventUuid1", "appUuid1"); + insertEventChanges("eventChangeUuid2", "eventUuid1", "appUuid1"); + + // events that should not be deleted + insertEvent("eventUuid2", "appUuid1", "Passed", "Alert", "{ stillFailing: false, status: \"ERROR\" }"); + insertEventChanges("eventChangeUuid3", "eventUuid2", "appUuid1"); + insertEvent("eventUuid3", "appUuid1", "Failed", "Alert", "{ stillFailing: false, status: \"PASSED\" }"); + insertEventChanges("eventChangeUuid4", "eventUuid3", "appUuid1"); + + // when + underTest.execute(); + + // then + assertThat(db.countRowsOfTable("events")).isEqualTo(2); + assertThat(db.countSql("select count(1) from events where uuid = 'eventUuid1'")).isZero(); + + assertThat(db.countRowsOfTable("event_component_changes")).isEqualTo(2); + assertThat(db.countSql("select count(1) from event_component_changes where uuid = 'eventUuid1'")).isZero(); + } + + @Test + public void givenFailedAlertsForProject_whenExecute_thenTheEventsAreNotDeleted() throws SQLException { + // given + insertComponent("project1", "projectUuid1", "projectUuid1", "TRK"); + + // event that should not be deleted + insertEvent("eventUuid1", "projectUuid1", "Failed", "Alert", "{ stillFailing: false, status: \"ERROR\" }"); + insertEventChanges("eventChangeUuid1", "eventUuid1", "projectUuid1"); + insertEventChanges("eventChangeUuid2", "eventUuid1", "projectUuid1"); + + // when + underTest.execute(); + + // then + assertThat(db.countSql("select count(1) from events where uuid = 'eventUuid1'")).isEqualTo(1); + assertThat(db.countSql("select count(1) from event_component_changes where event_uuid = 'eventUuid1'")).isEqualTo(2); + } + + @Test + public void givenMigration_whenExecutedMoreThanOnce_thenNoError() throws SQLException { + // given + insertComponent("app1", "appUuid1", "appUuid1", "APP"); + + // event that should be deleted + insertEvent("eventUuid1", "appUuid1", "Failed", "Alert", "{ stillFailing: false, status: \"ERROR\" }"); + insertEventChanges("eventChangeUuid1", "eventUuid1", "appUuid1"); + insertEventChanges("eventChangeUuid2", "eventUuid1", "appUuid1"); + + // when + underTest.execute(); + underTest.execute(); + + // then + assertThat(db.countSql("select count(1) from events where uuid = 'eventUuid1'")).isZero(); + assertThat(db.countSql("select count(1) from event_component_changes where uuid = 'eventUuid1'")).isZero(); + } + + private void insertComponent(String key, String uuid, String branchUuid, String qualifier) { + Map<String, Object> map = Map.ofEntries( + Map.entry("UUID", uuid), + Map.entry("KEE", key), + Map.entry("BRANCH_UUID", branchUuid), + Map.entry("UUID_PATH", "." + uuid + "."), + Map.entry("QUALIFIER", qualifier), + Map.entry("ENABLED", true), + Map.entry("PRIVATE", true) + ); + + db.executeInsert("components", map); + } + + private void insertEvent(String uuid, String componentUuid, String name, String category, String eventData) { + Map<String, Object> map = Map.ofEntries( + Map.entry("UUID", uuid), + Map.entry("NAME", name), + Map.entry("ANALYSIS_UUID", "analysisUuid"), + Map.entry("CATEGORY", category), + Map.entry("CREATED_AT", 1_500_000_000_000L), + Map.entry("EVENT_DATE", 1_500_000_000_000L), + Map.entry("COMPONENT_UUID", componentUuid), + Map.entry("EVENT_DATA", eventData) + ); + + db.executeInsert("events", map); + } + + private void insertEventChanges(String uuid, String eventUuid, String componentUuid) { + Map<String, Object> map = Map.ofEntries( + Map.entry("UUID", uuid), + Map.entry("EVENT_UUID", eventUuid), + Map.entry("EVENT_COMPONENT_UUID", componentUuid), + Map.entry("EVENT_ANALYSIS_UUID", "analysisUuid"), + Map.entry("CHANGE_CATEGORY", "FAILED_QG"), + Map.entry("COMPONENT_UUID", uuid), + Map.entry("COMPONENT_KEY", "app"), + Map.entry("COMPONENT_NAME", "app"), + Map.entry("COMPONENT_BRANCH_KEY", 1_500_000_000_000L), + Map.entry("CREATED_AT", 1_500_000_000_000L) + ); + + db.executeInsert("event_component_changes", map); + } + + +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest/schema.sql new file mode 100644 index 00000000000..20cd163ec66 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest/schema.sql @@ -0,0 +1,64 @@ +CREATE TABLE "COMPONENTS"( + "UUID" CHARACTER VARYING(50) NOT NULL, + "KEE" CHARACTER VARYING(1000), + "DEPRECATED_KEE" CHARACTER VARYING(400), + "NAME" CHARACTER VARYING(2000), + "LONG_NAME" CHARACTER VARYING(2000), + "DESCRIPTION" CHARACTER VARYING(2000), + "ENABLED" BOOLEAN DEFAULT TRUE NOT NULL, + "SCOPE" CHARACTER VARYING(3), + "QUALIFIER" CHARACTER VARYING(10), + "PRIVATE" BOOLEAN NOT NULL, + "LANGUAGE" CHARACTER VARYING(20), + "COPY_COMPONENT_UUID" CHARACTER VARYING(50), + "PATH" CHARACTER VARYING(2000), + "UUID_PATH" CHARACTER VARYING(1500) NOT NULL, + "BRANCH_UUID" CHARACTER VARYING(50) NOT NULL, + "B_CHANGED" BOOLEAN, + "B_NAME" CHARACTER VARYING(500), + "B_LONG_NAME" CHARACTER VARYING(500), + "B_DESCRIPTION" CHARACTER VARYING(2000), + "B_ENABLED" BOOLEAN, + "B_QUALIFIER" CHARACTER VARYING(10), + "B_LANGUAGE" CHARACTER VARYING(20), + "B_COPY_COMPONENT_UUID" CHARACTER VARYING(50), + "B_PATH" CHARACTER VARYING(2000), + "B_UUID_PATH" CHARACTER VARYING(1500), + "CREATED_AT" TIMESTAMP +); +CREATE INDEX "PROJECTS_QUALIFIER" ON "COMPONENTS"("QUALIFIER" NULLS FIRST); +CREATE UNIQUE NULLS NOT DISTINCT INDEX "COMPONENTS_UUID" ON "COMPONENTS"("UUID" NULLS FIRST); +CREATE INDEX "COMPONENTS_BRANCH_UUID" ON "COMPONENTS"("BRANCH_UUID" NULLS FIRST); +CREATE UNIQUE NULLS NOT DISTINCT INDEX "COMPONENTS_KEE_BRANCH_UUID" ON "COMPONENTS"("KEE" NULLS FIRST, "BRANCH_UUID" NULLS FIRST); + +CREATE TABLE "EVENT_COMPONENT_CHANGES"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "EVENT_UUID" CHARACTER VARYING(40) NOT NULL, + "EVENT_COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL, + "EVENT_ANALYSIS_UUID" CHARACTER VARYING(50) NOT NULL, + "CHANGE_CATEGORY" CHARACTER VARYING(12) NOT NULL, + "COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL, + "COMPONENT_KEY" CHARACTER VARYING(400) NOT NULL, + "COMPONENT_NAME" CHARACTER VARYING(2000) NOT NULL, + "COMPONENT_BRANCH_KEY" CHARACTER VARYING(255), + "CREATED_AT" BIGINT NOT NULL +); +ALTER TABLE "EVENT_COMPONENT_CHANGES" ADD CONSTRAINT "PK_EVENT_COMPONENT_CHANGES" PRIMARY KEY("UUID"); +CREATE UNIQUE NULLS NOT DISTINCT INDEX "EVENT_COMPONENT_CHANGES_UNIQUE" ON "EVENT_COMPONENT_CHANGES"("EVENT_UUID" NULLS FIRST, "CHANGE_CATEGORY" NULLS FIRST, "COMPONENT_UUID" NULLS FIRST); +CREATE INDEX "EVENT_CPNT_CHANGES_CPNT" ON "EVENT_COMPONENT_CHANGES"("EVENT_COMPONENT_UUID" NULLS FIRST); +CREATE INDEX "EVENT_CPNT_CHANGES_ANALYSIS" ON "EVENT_COMPONENT_CHANGES"("EVENT_ANALYSIS_UUID" NULLS FIRST); + +CREATE TABLE "EVENTS"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "ANALYSIS_UUID" CHARACTER VARYING(50) NOT NULL, + "NAME" CHARACTER VARYING(400), + "CATEGORY" CHARACTER VARYING(50), + "DESCRIPTION" CHARACTER VARYING(4000), + "EVENT_DATA" CHARACTER VARYING(4000), + "EVENT_DATE" BIGINT NOT NULL, + "CREATED_AT" BIGINT NOT NULL, + "COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL +); +ALTER TABLE "EVENTS" ADD CONSTRAINT "PK_EVENTS" PRIMARY KEY("UUID"); +CREATE INDEX "EVENTS_ANALYSIS" ON "EVENTS"("ANALYSIS_UUID" NULLS FIRST); +CREATE INDEX "EVENTS_COMPONENT_UUID" ON "EVENTS"("COMPONENT_UUID" NULLS FIRST); |