diff options
author | Claire Villard <60586848+claire-villard-sonarsource@users.noreply.github.com> | 2024-09-27 09:57:19 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-10-09 20:02:47 +0000 |
commit | e8ef7e9b39e0ea580d82df8b7b4fabbcc6e41f01 (patch) | |
tree | d5a488342abfecf96e914f18cf735c769901b9eb | |
parent | a79666b02068f247ea26944d3ea0d0181365c3b7 (diff) | |
download | sonarqube-e8ef7e9b39e0ea580d82df8b7b4fabbcc6e41f01.tar.gz sonarqube-e8ef7e9b39e0ea580d82df8b7b4fabbcc6e41f01.zip |
SONAR-22880 Migrate 'live_measures' to 'measures'
28 files changed, 1231 insertions, 15 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistMeasuresStepTest.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistMeasuresStepIT.java index 7bcbd114609..dccaf1e4920 100644 --- a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistMeasuresStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistMeasuresStepIT.java @@ -21,6 +21,7 @@ package org.sonar.ce.task.projectanalysis.step; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,11 +40,15 @@ import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule; import org.sonar.ce.task.step.TestComputationStepContext; import org.sonar.db.DbClient; import org.sonar.db.DbTester; +import org.sonar.db.component.BranchDto; +import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.PortfolioData; import org.sonar.db.measure.MeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.server.project.Project; +import static java.lang.String.format; import static java.util.Collections.emptyList; import static java.util.Map.entry; import static org.assertj.core.api.Assertions.assertThat; @@ -61,7 +66,7 @@ import static org.sonar.ce.task.projectanalysis.component.Component.Type.SUBVIEW import static org.sonar.ce.task.projectanalysis.component.Component.Type.VIEW; import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder; -class PersistMeasuresStepTest { +class PersistMeasuresStepIT { private static final Metric<?> STRING_METRIC = new Metric.Builder("string-metric", "String metric", Metric.ValueType.STRING).create(); private static final Metric<?> INT_METRIC = new Metric.Builder("int-metric", "int metric", Metric.ValueType.INT).create(); @@ -126,6 +131,7 @@ class PersistMeasuresStepTest { .hasValueSatisfying(measure -> assertThat(measure.getMetricValues()).containsEntry(STRING_METRIC.getKey(), "dir-value")); assertThat(selectMeasure("file-uuid")) .hasValueSatisfying(measure -> assertThat(measure.getMetricValues()).containsEntry(STRING_METRIC.getKey(), "file-value")); + assertThat(getBranchMigratedFlag("project-uuid")).isTrue(); verifyInsertsOrUpdates(3); } @@ -147,6 +153,7 @@ class PersistMeasuresStepTest { .hasValueSatisfying(measure -> assertThat(measure.getMetricValues()).containsEntry(STRING_METRIC.getKey(), "subview-value")); assertThat(selectMeasure("project-uuid")) .hasValueSatisfying(measure -> assertThat(measure.getMetricValues()).containsEntry(STRING_METRIC.getKey(), "project-value")); + assertThat(getPortfolioMigratedFlag("view-uuid")).isTrue(); verifyInsertsOrUpdates(3); } @@ -162,6 +169,7 @@ class PersistMeasuresStepTest { Component project = ReportComponent.builder(Component.Type.PROJECT, -1).setUuid("project-uuid") .addChildren(files.toArray(Component[]::new)) .build(); + insertBranch(); treeRootHolder.setRoot(project); analysisMetadataHolder.setBaseAnalysis(new Analysis.Builder().setUuid("uuid").setCreatedAt(1L).build()); insertMeasure("file-uuid0", "project-uuid", STRING_METRIC, valuePrefix + "0"); @@ -177,6 +185,7 @@ class PersistMeasuresStepTest { // all measures are persisted, for project and all files assertThat(db.countRowsOfTable("measures")).isEqualTo(num + 1); + assertThat(getBranchMigratedFlag("project-uuid")).isTrue(); verifyInsertsOrUpdates(num - 1); verifyUnchanged(1); verify(computeDuplicationDataMeasure, times(num)).compute(any(Component.class)); @@ -283,6 +292,7 @@ class PersistMeasuresStepTest { step().execute(context); + assertThat(getBranchMigratedFlag("project-uuid")).isTrue(); verifyInsertsOrUpdates(0); verifyUnchanged(1); } @@ -333,6 +343,9 @@ class PersistMeasuresStepTest { insertComponent("project-key", "project-uuid"); insertComponent("dir-key", "dir-uuid"); insertComponent("file-key", "file-uuid"); + + // branch is persisted in db + insertBranch(); } private void preparePortfolio() { @@ -348,16 +361,21 @@ class PersistMeasuresStepTest { treeRootHolder.setRoot(portfolio); // components as persisted in db - ComponentDto portfolioDto = insertComponent("view-key", "view-uuid"); + PortfolioData portfolioDto1 = db.components().insertPrivatePortfolioData(c -> c.setUuid("view-uuid").setKey("view-key").setBranchUuid("view-uuid")); insertComponent("subview-key", "subview-uuid"); insertComponent("project-key", "project-uuid"); - analysisMetadataHolder.setProject(Project.from(portfolioDto)); + analysisMetadataHolder.setProject(Project.from(portfolioDto1.getPortfolioDto())); } private Optional<MeasureDto> selectMeasure(String componentUuid) { return dbClient.measureDao().selectByComponentUuid(db.getSession(), componentUuid); } + private void insertBranch() { + dbClient.branchDao().insert(db.getSession(), new BranchDto().setUuid("project-uuid").setProjectUuid("project-uuid").setKey("branch").setBranchType(BranchType.BRANCH).setIsMain(true)); + db.commit(); + } + private ComponentDto insertComponent(String key, String uuid) { ComponentDto componentDto = new ComponentDto() .setKey(key) @@ -372,6 +390,20 @@ class PersistMeasuresStepTest { return new PersistMeasuresStep(dbClient, metricRepository, treeRootHolder, measureRepository, computeDuplicationDataMeasure); } + private boolean getBranchMigratedFlag(String branch) { + List<Map<String, Object>> result = db.select(format("select measures_migrated as \"MIGRATED\" from project_branches where uuid = '%s'", branch)); + assertThat(result).hasSize(1); + + return (boolean) result.get(0).get("MIGRATED"); + } + + private boolean getPortfolioMigratedFlag(String portfolio) { + List<Map<String, Object>> result = db.select(format("select measures_migrated as \"MIGRATED\" from portfolios where uuid = '%s'", portfolio)); + assertThat(result).hasSize(1); + + return (boolean) result.get(0).get("MIGRATED"); + } + private void verifyInsertsOrUpdates(int expectedInsertsOrUpdates) { context.getStatistics().assertValue("insertsOrUpdates", expectedInsertsOrUpdates); } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistMeasuresStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistMeasuresStep.java index d0d0513e85f..bf5ae6856f6 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistMeasuresStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistMeasuresStep.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nullable; import org.sonar.ce.task.projectanalysis.component.Component; +import org.sonar.ce.task.projectanalysis.component.Component.Type; import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit; import org.sonar.ce.task.projectanalysis.component.DepthTraversalTypeAwareCrawler; import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; @@ -133,12 +134,36 @@ public class PersistMeasuresStep implements ComputationStep { } } persist(inserts, updates); + updateMeasureMigratedFlag(); context.getStatistics() .add("insertsOrUpdates", insertsOrUpdates) .add("unchanged", unchanged); } + private void updateMeasureMigratedFlag() { + Type type = treeRootHolder.getRoot().getType(); + if (type == Type.PROJECT) { + persistBranchFlag(); + } else if (type == Type.VIEW) { + persistPortfolioFlag(); + } + } + + private void persistBranchFlag() { + try (DbSession dbSession = dbClient.openSession(false)) { + dbClient.branchDao().updateMeasuresMigrated(dbSession, treeRootHolder.getRoot().getUuid(), true); + dbSession.commit(); + } + } + + private void persistPortfolioFlag() { + try (DbSession dbSession = dbClient.openSession(false)) { + dbClient.portfolioDao().updateMeasuresMigrated(dbSession, treeRootHolder.getRoot().getUuid(), true); + dbSession.commit(); + } + } + private Set<MeasureHash> getDBMeasureHashes() { try (DbSession dbSession = dbClient.openSession(false)) { return dbClient.measureDao().selectMeasureHashesForBranch(dbSession, treeRootHolder.getRoot().getUuid()); @@ -153,7 +178,7 @@ public class PersistMeasuresStep implements ComputationStep { Map<String, Measure> measures = measureRepository.getRawMeasures(component); for (Map.Entry<String, Measure> measuresByMetricKey : measures.entrySet()) { String metricKey = measuresByMetricKey.getKey(); - if (NOT_TO_PERSIST_ON_FILE_METRIC_KEYS.contains(metricKey) && component.getType() == Component.Type.FILE) { + if (NOT_TO_PERSIST_ON_FILE_METRIC_KEYS.contains(metricKey) && component.getType() == Type.FILE) { continue; } Metric metric = metricRepository.getByKey(metricKey); @@ -166,7 +191,7 @@ public class PersistMeasuresStep implements ComputationStep { .filter(Objects::nonNull) .forEach(value -> measureDto.addValue(metric.getKey(), value)); - if (component.getType() == Component.Type.FILE) { + if (component.getType() == Type.FILE) { if (computeDuplicationDataMeasure == null) { throw new IllegalStateException("ComputeDuplicationDataMeasure not initialized in container"); } diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/component/BranchDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/component/BranchDaoIT.java index f5096c9a414..77ac376f26b 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/component/BranchDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/component/BranchDaoIT.java @@ -42,6 +42,7 @@ import org.sonar.db.metric.MetricDto; import org.sonar.db.project.ProjectDto; import org.sonar.db.protobuf.DbProjectBranches; +import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -751,6 +752,26 @@ class BranchDaoIT { } @Test + void updateMeasuresMigrated() { + ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + String uuid1 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH)).uuid(); + String uuid2 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH)).uuid(); + + underTest.updateMeasuresMigrated(dbSession, uuid1, true); + underTest.updateMeasuresMigrated(dbSession, uuid2, false); + + assertThat(getMeasuresMigrated(uuid1)).isTrue(); + assertThat(getMeasuresMigrated(uuid2)).isFalse(); + } + + private boolean getMeasuresMigrated(String uuid1) { + List<Map<String, Object>> select = db.select(dbSession, format("select measures_migrated from project_branches where uuid = '%s'", uuid1)); + + assertThat(select).hasSize(1); + return (boolean) select.get(0).get("measures_migrated"); + } + + @Test void doAnyOfComponentsNeedIssueSync() { assertThat(underTest.doAnyOfComponentsNeedIssueSync(dbSession, emptyList())).isFalse(); diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/portfolio/PortfolioDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/portfolio/PortfolioDaoIT.java index 42c1052f2fb..eb544fde07a 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/portfolio/PortfolioDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/portfolio/PortfolioDaoIT.java @@ -21,11 +21,13 @@ package org.sonar.db.portfolio; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.sonar.api.impl.utils.AlwaysIncreasingSystem2; import org.sonar.api.utils.System2; +import org.sonar.core.util.SequenceUuidFactory; import org.sonar.core.util.UuidFactoryFast; import org.sonar.db.DbSession; import org.sonar.db.DbTester; @@ -37,6 +39,7 @@ import org.sonar.db.component.ProjectData; import org.sonar.db.project.ApplicationProjectDto; import org.sonar.db.project.ProjectDto; +import static java.lang.String.format; import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; @@ -50,6 +53,8 @@ import static org.mockito.Mockito.verify; class PortfolioDaoIT { private final System2 system2 = new AlwaysIncreasingSystem2(1L, 1); + private final SequenceUuidFactory uuidFactory = new SequenceUuidFactory(); + @RegisterExtension private final DbTester db = DbTester.create(system2); private final AuditPersister audit = mock(AuditPersister.class); @@ -246,6 +251,25 @@ class PortfolioDaoIT { } @Test + void update_measures_migrated() { + PortfolioDto portfolio1 = db.components().insertPrivatePortfolioDto("name1"); + PortfolioDto portfolio2 = db.components().insertPrivatePortfolioDto("name2"); + + portfolioDao.updateMeasuresMigrated(session, portfolio1.getUuid(), true); + portfolioDao.updateMeasuresMigrated(session, portfolio2.getUuid(), false); + + assertThat(getMeasuresMigrated(portfolio1.getUuid())).isTrue(); + assertThat(getMeasuresMigrated(portfolio2.getUuid())).isFalse(); + } + + private boolean getMeasuresMigrated(String uuid1) { + List<Map<String, Object>> select = db.select(session, format("select measures_migrated from portfolios where uuid = '%s'", uuid1)); + + assertThat(select).hasSize(1); + return (boolean) select.get(0).get("measures_migrated"); + } + + @Test void selectAllReferencesToPortfolios() { db.components().insertPrivatePortfolioDto("portfolio1"); db.components().insertPrivatePortfolioDto("portfolio2"); @@ -523,7 +547,8 @@ class PortfolioDaoIT { @Test void deleteReferencesTo_with_non_existing_reference_doesnt_fail() { - portfolioDao.deleteReferencesTo(session, "portfolio3"); + assertThatCode(() -> portfolioDao.deleteReferencesTo(session, "portfolio3")) + .doesNotThrowAnyException(); } @Test @@ -737,7 +762,7 @@ class PortfolioDaoIT { } private PortfolioDto addPortfolio(PortfolioDto parent) { - return addPortfolio(parent, UuidFactoryFast.getInstance().create()); + return addPortfolio(parent, uuidFactory.create()); } private PortfolioDto addPortfolio(PortfolioDto parent, String uuid) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java index bf0d7bf582f..31479d4b5a3 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java @@ -177,11 +177,17 @@ public class BranchDao implements Dao { long now = system2.now(); return mapper(dbSession).updateNeedIssueSync(branchUuid, needIssueSync, now); } + public long updateIsMain(DbSession dbSession, String branchUuid, boolean isMain) { long now = system2.now(); return mapper(dbSession).updateIsMain(branchUuid, isMain, now); } + public long updateMeasuresMigrated(DbSession dbSession, String branchUuid, boolean measuresMigrated) { + long now = system2.now(); + return mapper(dbSession).updateMeasuresMigrated(branchUuid, measuresMigrated, now); + } + public boolean doAnyOfComponentsNeedIssueSync(DbSession session, List<String> components) { if (!components.isEmpty()) { List<Boolean> result = new LinkedList<>(); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java index 65fc908814b..536d3cb7701 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java @@ -66,7 +66,7 @@ public interface BranchMapper { long updateAllNeedIssueSyncForProject(@Param("projectUuid") String projectUuid, @Param("now") long now); - long updateNeedIssueSync(@Param("uuid") String uuid, @Param("needIssueSync")boolean needIssueSync,@Param("now") long now); + long updateNeedIssueSync(@Param("uuid") String uuid, @Param("needIssueSync") boolean needIssueSync, @Param("now") long now); long updateIsMain(@Param("uuid") String uuid, @Param("isMain") boolean isMain, @Param("now") long now); @@ -78,4 +78,5 @@ public interface BranchMapper { List<BranchMeasuresDto> selectBranchMeasuresWithCaycMetric(long yesterday); + int updateMeasuresMigrated(@Param("uuid") String uuid, @Param("measuresMigrated") boolean measuresMigrated, @Param("now") long now); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioDao.java index 2f88082ed13..cd3152a7ad9 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioDao.java @@ -248,6 +248,11 @@ public class PortfolioDao implements Dao { return uuid; } + public long updateMeasuresMigrated(DbSession dbSession, String branchUuid, boolean measuresMigrated) { + long now = system2.now(); + return mapper(dbSession).updateMeasuresMigrated(branchUuid, measuresMigrated, now); + } + public void deleteProjects(DbSession dbSession, String portfolioUuid) { mapper(dbSession).deleteProjects(portfolioUuid); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioMapper.java index 05c6225719e..218ecc688bf 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioMapper.java @@ -116,4 +116,6 @@ public interface PortfolioMapper { List<PortfolioDto> selectRootOfReferencersToAppBranch(@Param("appUuid") String appUuid, @Param("appBranchKey") String appBranchKey); List<KeyWithUuidDto> selectUuidsByKey(@Param("rootKey") String rootKey); + + int updateMeasuresMigrated(@Param("uuid") String uuid, @Param("measuresMigrated") boolean measuresMigrated, @Param("now") long now); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml index b2b3993d2bf..d90e2846960 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml @@ -278,6 +278,15 @@ uuid = #{uuid, jdbcType=VARCHAR} </update> + <update id="updateMeasuresMigrated"> + update project_branches + set + measures_migrated = #{measuresMigrated, jdbcType=BOOLEAN}, + updated_at = #{now, jdbcType=BIGINT} + where + uuid = #{uuid, jdbcType=VARCHAR} + </update> + <sql id="doAnyOfComponentsNeedIssueSyncSql"> select case when exists diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/portfolio/PortfolioMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/portfolio/PortfolioMapper.xml index 2071d83028c..22f0c0ff611 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/portfolio/PortfolioMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/portfolio/PortfolioMapper.xml @@ -560,4 +560,13 @@ WHERE uuid = #{uuid,jdbcType=VARCHAR} </update> + + <update id="updateMeasuresMigrated"> + update portfolios + set + measures_migrated = #{measuresMigrated, jdbcType=BOOLEAN}, + updated_at = #{now, jdbcType=BIGINT} + where + uuid = #{uuid, jdbcType=VARCHAR} + </update> </mapper> diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index f19c8ed7a70..9eea010c9f6 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -699,10 +699,12 @@ CREATE TABLE "PORTFOLIOS"( "SELECTION_EXPRESSION" CHARACTER VARYING(4000), "CREATED_AT" BIGINT NOT NULL, "UPDATED_AT" BIGINT NOT NULL, - "BRANCH_KEY" CHARACTER VARYING(255) + "BRANCH_KEY" CHARACTER VARYING(255), + "MEASURES_MIGRATED" BOOLEAN DEFAULT FALSE NOT NULL ); ALTER TABLE "PORTFOLIOS" ADD CONSTRAINT "PK_PORTFOLIOS" PRIMARY KEY("UUID"); CREATE UNIQUE NULLS NOT DISTINCT INDEX "UNIQ_PORTFOLIOS_KEE" ON "PORTFOLIOS"("KEE" NULLS FIRST); +CREATE INDEX "PORTFOLIOS_MEASURES_MIGRATED" ON "PORTFOLIOS"("MEASURES_MIGRATED" NULLS FIRST); CREATE TABLE "PROJECT_ALM_SETTINGS"( "UUID" CHARACTER VARYING(40) NOT NULL, @@ -742,11 +744,13 @@ CREATE TABLE "PROJECT_BRANCHES"( "UPDATED_AT" BIGINT NOT NULL, "EXCLUDE_FROM_PURGE" BOOLEAN DEFAULT FALSE NOT NULL, "NEED_ISSUE_SYNC" BOOLEAN NOT NULL, - "IS_MAIN" BOOLEAN NOT NULL + "IS_MAIN" BOOLEAN NOT NULL, + "MEASURES_MIGRATED" BOOLEAN DEFAULT FALSE NOT NULL ); ALTER TABLE "PROJECT_BRANCHES" ADD CONSTRAINT "PK_PROJECT_BRANCHES" PRIMARY KEY("UUID"); CREATE UNIQUE NULLS NOT DISTINCT INDEX "UNIQ_PROJECT_BRANCHES" ON "PROJECT_BRANCHES"("BRANCH_TYPE" NULLS FIRST, "PROJECT_UUID" NULLS FIRST, "KEE" NULLS FIRST); CREATE INDEX "PROJECT_BRANCHES_PROJECT_UUID" ON "PROJECT_BRANCHES"("PROJECT_UUID" NULLS FIRST); +CREATE INDEX "PB_MEASURES_MIGRATED" ON "PROJECT_BRANCHES"("MEASURES_MIGRATED" NULLS FIRST); CREATE TABLE "PROJECT_LINKS"( "UUID" CHARACTER VARYING(40) NOT NULL, diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToPortfoliosTableIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToPortfoliosTableIT.java new file mode 100644 index 00000000000..000681ae3b6 --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToPortfoliosTableIT.java @@ -0,0 +1,50 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.sonar.server.platform.db.migration.version.v107.AddMeasuresMigratedColumnToPortfoliosTable.MIGRATION_FLAG_COLUMN_NAME; +import static org.sonar.server.platform.db.migration.version.v107.AddMeasuresMigratedColumnToPortfoliosTable.PORTFOLIOS_TABLE_NAME; + +class AddMeasuresMigratedColumnToPortfoliosTableIT { + + @RegisterExtension + public final MigrationDbTester db = MigrationDbTester.createForMigrationStep(AddMeasuresMigratedColumnToPortfoliosTable.class); + private final AddMeasuresMigratedColumnToPortfoliosTable underTest = new AddMeasuresMigratedColumnToPortfoliosTable(db.database()); + + @Test + void execute_whenColumnDoesNotExist_shouldCreateColumn() throws SQLException { + db.assertColumnDoesNotExist(PORTFOLIOS_TABLE_NAME, MIGRATION_FLAG_COLUMN_NAME); + underTest.execute(); + db.assertColumnDefinition(PORTFOLIOS_TABLE_NAME, MIGRATION_FLAG_COLUMN_NAME, Types.BOOLEAN, null, false); + } + + @Test + void execute_whenColumnAlreadyExists_shouldNotFail() throws SQLException { + underTest.execute(); + assertThatCode(underTest::execute).doesNotThrowAnyException(); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToProjectBranchesTableIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToProjectBranchesTableIT.java new file mode 100644 index 00000000000..8971111f7ee --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToProjectBranchesTableIT.java @@ -0,0 +1,50 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; +import static org.sonar.server.platform.db.migration.version.v107.AddMeasuresMigratedColumnToProjectBranchesTable.MIGRATION_FLAG_COLUMN_NAME; +import static org.sonar.server.platform.db.migration.version.v107.AddMeasuresMigratedColumnToProjectBranchesTable.PROJECT_BRANCHES_TABLE_NAME; + +class AddMeasuresMigratedColumnToProjectBranchesTableIT { + + @RegisterExtension + public final MigrationDbTester db = MigrationDbTester.createForMigrationStep(AddMeasuresMigratedColumnToProjectBranchesTable.class); + private final AddMeasuresMigratedColumnToProjectBranchesTable underTest = new AddMeasuresMigratedColumnToProjectBranchesTable(db.database()); + + @Test + void execute_whenColumnDoesNotExist_shouldCreateColumn() throws SQLException { + db.assertColumnDoesNotExist(PROJECT_BRANCHES_TABLE_NAME, MIGRATION_FLAG_COLUMN_NAME); + underTest.execute(); + db.assertColumnDefinition(PROJECT_BRANCHES_TABLE_NAME, MIGRATION_FLAG_COLUMN_NAME, Types.BOOLEAN, null, false); + } + + @Test + void execute_whenColumnAlreadyExists_shouldNotFail() throws SQLException { + underTest.execute(); + assertThatCode(underTest::execute).doesNotThrowAnyException(); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnPortfoliosMeasuresMigratedIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnPortfoliosMeasuresMigratedIT.java new file mode 100644 index 00000000000..691c105d8ff --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnPortfoliosMeasuresMigratedIT.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; + +import static org.sonar.server.platform.db.migration.version.v107.CreateIndexOnPortfoliosMeasuresMigrated.COLUMN_NAME; +import static org.sonar.server.platform.db.migration.version.v107.CreateIndexOnPortfoliosMeasuresMigrated.TABLE_NAME; + +class CreateIndexOnPortfoliosMeasuresMigratedIT { + + private static final String INDEX_NAME = "portfolios_measures_migrated"; + @RegisterExtension + public final MigrationDbTester db = MigrationDbTester.createForMigrationStep(CreateIndexOnPortfoliosMeasuresMigrated.class); + private final CreateIndexOnPortfoliosMeasuresMigrated underTest = new CreateIndexOnPortfoliosMeasuresMigrated(db.database()); + + @Test + void migration_should_create_index() throws SQLException { + db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME); + + underTest.execute(); + + db.assertIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME); + } + + @Test + void migration_should_be_reentrant() throws SQLException { + underTest.execute(); + underTest.execute(); + + db.assertIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnProjectBranchesMeasuresMigratedIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnProjectBranchesMeasuresMigratedIT.java new file mode 100644 index 00000000000..3e95ed5611d --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnProjectBranchesMeasuresMigratedIT.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; + +import static org.sonar.server.platform.db.migration.version.v107.CreateIndexOnProjectBranchesMeasuresMigrated.COLUMN_NAME; +import static org.sonar.server.platform.db.migration.version.v107.CreateIndexOnProjectBranchesMeasuresMigrated.TABLE_NAME; + +class CreateIndexOnProjectBranchesMeasuresMigratedIT { + + private static final String INDEX_NAME = "pb_measures_migrated"; + + @RegisterExtension + public final MigrationDbTester db = MigrationDbTester.createForMigrationStep(CreateIndexOnProjectBranchesMeasuresMigrated.class); + private final CreateIndexOnProjectBranchesMeasuresMigrated underTest = new CreateIndexOnProjectBranchesMeasuresMigrated(db.database()); + + @Test + void migration_should_create_index() throws SQLException { + db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME); + + underTest.execute(); + + db.assertIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME); + } + + @Test + void migration_should_be_reentrant() throws SQLException { + underTest.execute(); + underTest.execute(); + + db.assertIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/MigrateBranchesLiveMeasuresToMeasuresIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/MigrateBranchesLiveMeasuresToMeasuresIT.java new file mode 100644 index 00000000000..fda3df82af9 --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/MigrateBranchesLiveMeasuresToMeasuresIT.java @@ -0,0 +1,184 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.api.utils.System2; +import org.sonar.core.util.SequenceUuidFactory; +import org.sonar.db.MigrationDbTester; +import org.sonar.server.platform.db.migration.step.DataChange; + +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.Mockito.mock; + +class MigrateBranchesLiveMeasuresToMeasuresIT { + + private static final String MEASURES_MIGRATED_COLUMN = "measures_migrated"; + public static final String SELECT_MEASURE = "select component_uuid, branch_uuid, json_value, json_value_hash, created_at, updated_at " + + "from measures where component_uuid = '%s'"; + + @RegisterExtension + public final MigrationDbTester db = MigrationDbTester.createForMigrationStep(MigrateBranchesLiveMeasuresToMeasures.class); + + private final SequenceUuidFactory uuidFactory = new SequenceUuidFactory(); + private final System2 system2 = mock(); + private final DataChange underTest = new MigrateBranchesLiveMeasuresToMeasures(db.database(), system2); + + @Test + void shall_complete_when_tables_are_empty() throws SQLException { + underTest.execute(); + + assertThat(db.countRowsOfTable("measures")).isZero(); + } + + @Test + void shall_not_migrate_when_branch_is_already_flagged() throws SQLException { + String nclocMetricUuid = insertMetric("ncloc", "INT"); + String qgStatusMetricUuid = insertMetric("quality_gate_status", "STRING"); + String metricWithDataUuid = insertMetric("metric_with_data", "DATA"); + String branch1 = "branch_1"; + insertMigratedBranch(branch1); + insertMeasure(branch1, nclocMetricUuid, Map.of("value", 120)); + insertMeasure(branch1, qgStatusMetricUuid, Map.of("text_value", "ok")); + insertMeasure(branch1, metricWithDataUuid, Map.of("measure_data", "some data".getBytes(StandardCharsets.UTF_8))); + + insertMigratedBranch("branch_2"); + insertMeasure("branch_2", nclocMetricUuid, Map.of("value", 14220)); + + underTest.execute(); + + assertThat(db.countRowsOfTable("measures")).isZero(); + } + + @Test + void should_flag_branch_with_no_measures() throws SQLException { + String branch = "branch_3"; + insertNotMigratedBranch(branch); + + underTest.execute(); + + assertBranchMigrated(branch); + assertThat(db.countRowsOfTable("measures")).isZero(); + } + + @Test + void should_migrate_branch_with_measures() throws SQLException { + String nclocMetricUuid = insertMetric("ncloc", "INT"); + String qgStatusMetricUuid = insertMetric("quality_gate_status", "STRING"); + String metricWithDataUuid = insertMetric("metric_with_data", "DATA"); + + String branch1 = "branch_4"; + insertNotMigratedBranch(branch1); + String component1 = uuidFactory.create(); + String component2 = uuidFactory.create(); + insertMeasure(branch1, component1, nclocMetricUuid, Map.of("value", 120)); + insertMeasure(branch1, component1, qgStatusMetricUuid, Map.of("text_value", "ok")); + insertMeasure(branch1, component2, metricWithDataUuid, Map.of("measure_data", "some data".getBytes(StandardCharsets.UTF_8))); + + String branch2 = "branch_5"; + insertNotMigratedBranch(branch2); + insertMeasure(branch2, nclocMetricUuid, Map.of("value", 64)); + + String migratedBranch = "branch_6"; + insertMigratedBranch(migratedBranch); + insertMeasure(migratedBranch, nclocMetricUuid, Map.of("value", 3684)); + + underTest.execute(); + + assertBranchMigrated(branch1); + assertBranchMigrated(branch2); + assertThat(db.countRowsOfTable("measures")).isEqualTo(3); + + assertThat(db.select(format(SELECT_MEASURE, component1))) + .hasSize(1) + .extracting(t -> t.get("component_uuid"), t -> t.get("branch_uuid"), t -> t.get("json_value"), t -> t.get("json_value_hash")) + .containsOnly(tuple(component1, branch1, "{\"ncloc\":120.0,\"quality_gate_status\":\"ok\"}", 6033012287291512746L)); + + assertThat(db.select(format(SELECT_MEASURE, component2))) + .hasSize(1) + .extracting(t -> t.get("component_uuid"), t -> t.get("branch_uuid"), t -> t.get("json_value"), t -> t.get("json_value_hash")) + .containsOnly(tuple(component2, branch1, "{\"metric_with_data\":\"some data\"}", -4524184678167636687L)); + } + + private void assertBranchMigrated(String branch) { + List<Map<String, Object>> result = db.select(format("select %s as \"MIGRATED\" from project_branches where uuid = '%s'", MEASURES_MIGRATED_COLUMN, branch)); + assertThat(result) + .hasSize(1) + .extracting(t -> t.get("MIGRATED")) + .containsOnly(true); + } + + private String insertMetric(String metricName, String valueType) { + String metricUuid = uuidFactory.create(); + db.executeInsert("metrics", + "uuid", metricUuid, + "name", metricName, + "val_type", valueType); + return metricUuid; + } + + private void insertMeasure(String branchUuid, String metricUuid, Map<String, Object> data) { + insertMeasure(branchUuid, uuidFactory.create(), metricUuid, data); + } + + private void insertMeasure(String branchUuid, String componentUuid, String metricUuid, Map<String, Object> data) { + Map<String, Object> dataMap = new HashMap<>(data); + dataMap.put("uuid", uuidFactory.create()); + dataMap.put("component_uuid", componentUuid); + dataMap.put("project_uuid", branchUuid); + dataMap.put("metric_uuid", metricUuid); + dataMap.put("created_at", 12L); + dataMap.put("updated_at", 12L); + + db.executeInsert("live_measures", dataMap); + } + + private void insertNotMigratedBranch(String branchUuid) { + insertBranch(branchUuid, false); + } + + private void insertMigratedBranch(String branchUuid) { + insertBranch(branchUuid, true); + } + + private void insertBranch(String branchUuid, boolean migrated) { + db.executeInsert("project_branches", + "uuid", branchUuid, + "kee", branchUuid, + "branch_type", "LONG", + "project_uuid", uuidFactory.create(), + MEASURES_MIGRATED_COLUMN, migrated, + "need_issue_sync", false, + "is_main", true, + "created_at", 12L, + "updated_at", 12L + ); + } + + +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/MigratePortfoliosLiveMeasuresToMeasuresIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/MigratePortfoliosLiveMeasuresToMeasuresIT.java new file mode 100644 index 00000000000..50519cde383 --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/MigratePortfoliosLiveMeasuresToMeasuresIT.java @@ -0,0 +1,182 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.api.utils.System2; +import org.sonar.core.util.SequenceUuidFactory; +import org.sonar.db.MigrationDbTester; +import org.sonar.server.platform.db.migration.step.DataChange; + +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.Mockito.mock; + +class MigratePortfoliosLiveMeasuresToMeasuresIT { + + private static final String MEASURES_MIGRATED_COLUMN = "measures_migrated"; + public static final String SELECT_MEASURE = "select component_uuid, branch_uuid, json_value, json_value_hash, created_at, updated_at " + + "from measures where component_uuid = '%s'"; + + @RegisterExtension + public final MigrationDbTester db = MigrationDbTester.createForMigrationStep(MigratePortfoliosLiveMeasuresToMeasures.class); + + private final SequenceUuidFactory uuidFactory = new SequenceUuidFactory(); + private final System2 system2 = mock(); + private final DataChange underTest = new MigratePortfoliosLiveMeasuresToMeasures(db.database(), system2); + + @Test + void shall_complete_when_tables_are_empty() throws SQLException { + underTest.execute(); + + assertThat(db.countRowsOfTable("measures")).isZero(); + } + + @Test + void shall_not_migrate_when_portfolio_is_already_flagged() throws SQLException { + String nclocMetricUuid = insertMetric("ncloc", "INT"); + String qgStatusMetricUuid = insertMetric("quality_gate_status", "STRING"); + String metricWithDataUuid = insertMetric("metric_with_data", "DATA"); + String portfolio1 = "portfolio_1"; + insertMigratedPortfolio(portfolio1); + insertMeasure(portfolio1, nclocMetricUuid, Map.of("value", 120)); + insertMeasure(portfolio1, qgStatusMetricUuid, Map.of("text_value", "ok")); + insertMeasure(portfolio1, metricWithDataUuid, Map.of("measure_data", "some data".getBytes(StandardCharsets.UTF_8))); + + insertMigratedPortfolio("portfolio_2"); + insertMeasure("portfolio_2", nclocMetricUuid, Map.of("value", 14220)); + + underTest.execute(); + + assertThat(db.countRowsOfTable("measures")).isZero(); + } + + @Test + void should_flag_portfolio_with_no_measures() throws SQLException { + String portfolio = "portfolio_3"; + insertNotMigratedPortfolio(portfolio); + + underTest.execute(); + + assertPortfolioMigrated(portfolio); + assertThat(db.countRowsOfTable("measures")).isZero(); + } + + @Test + void should_migrate_portfolio_with_measures() throws SQLException { + String nclocMetricUuid = insertMetric("ncloc", "INT"); + String qgStatusMetricUuid = insertMetric("quality_gate_status", "STRING"); + String metricWithDataUuid = insertMetric("metric_with_data", "DATA"); + + String portfolio1 = "portfolio_4"; + insertNotMigratedPortfolio(portfolio1); + String component1 = uuidFactory.create(); + String component2 = uuidFactory.create(); + insertMeasure(portfolio1, component1, nclocMetricUuid, Map.of("value", 120)); + insertMeasure(portfolio1, component1, qgStatusMetricUuid, Map.of("text_value", "ok")); + insertMeasure(portfolio1, component2, metricWithDataUuid, Map.of("measure_data", "some data".getBytes(StandardCharsets.UTF_8))); + + String portfolio2 = "portfolio_5"; + insertNotMigratedPortfolio(portfolio2); + insertMeasure(portfolio2, nclocMetricUuid, Map.of("value", 64)); + + String migratedPortfolio = "portfolio_6"; + insertMigratedPortfolio(migratedPortfolio); + insertMeasure(migratedPortfolio, nclocMetricUuid, Map.of("value", 3684)); + + underTest.execute(); + + assertPortfolioMigrated(portfolio1); + assertPortfolioMigrated(portfolio2); + assertThat(db.countRowsOfTable("measures")).isEqualTo(3); + + assertThat(db.select(format(SELECT_MEASURE, component1))) + .hasSize(1) + .extracting(t -> t.get("component_uuid"), t -> t.get("branch_uuid"), t -> t.get("json_value"), t -> t.get("json_value_hash")) + .containsOnly(tuple(component1, portfolio1, "{\"ncloc\":120.0,\"quality_gate_status\":\"ok\"}", 6033012287291512746L)); + + assertThat(db.select(format(SELECT_MEASURE, component2))) + .hasSize(1) + .extracting(t -> t.get("component_uuid"), t -> t.get("branch_uuid"), t -> t.get("json_value"), t -> t.get("json_value_hash")) + .containsOnly(tuple(component2, portfolio1, "{\"metric_with_data\":\"some data\"}", -4524184678167636687L)); + } + + private void assertPortfolioMigrated(String portfolio) { + List<Map<String, Object>> result = db.select(format("select %s as \"MIGRATED\" from portfolios where uuid = '%s'", MEASURES_MIGRATED_COLUMN, portfolio)); + assertThat(result) + .hasSize(1) + .extracting(t -> t.get("MIGRATED")) + .containsOnly(true); + } + + private String insertMetric(String metricName, String valueType) { + String metricUuid = uuidFactory.create(); + db.executeInsert("metrics", + "uuid", metricUuid, + "name", metricName, + "val_type", valueType); + return metricUuid; + } + + private void insertMeasure(String portfolioUuid, String metricUuid, Map<String, Object> data) { + insertMeasure(portfolioUuid, uuidFactory.create(), metricUuid, data); + } + + private void insertMeasure(String portfolioUuid, String componentUuid, String metricUuid, Map<String, Object> data) { + Map<String, Object> dataMap = new HashMap<>(data); + dataMap.put("uuid", uuidFactory.create()); + dataMap.put("component_uuid", componentUuid); + dataMap.put("project_uuid", portfolioUuid); + dataMap.put("metric_uuid", metricUuid); + dataMap.put("created_at", 12L); + dataMap.put("updated_at", 12L); + + db.executeInsert("live_measures", dataMap); + } + + private void insertNotMigratedPortfolio(String portfolioUuid) { + insertPortfolio(portfolioUuid, false); + } + + private void insertMigratedPortfolio(String portfolioUuid) { + insertPortfolio(portfolioUuid, true); + } + + private void insertPortfolio(String portfolioUuid, boolean migrated) { + db.executeInsert("portfolios", + "uuid", portfolioUuid, + "kee", portfolioUuid, + "name", portfolioUuid, + "private", true, + "root_uuid", portfolioUuid, + "selection_mode", "MANUAL", + MEASURES_MIGRATED_COLUMN, migrated, + "created_at", 12L, + "updated_at", 12L + ); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MassUpdate.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MassUpdate.java index 428c641d98a..a6b25e68a36 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MassUpdate.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MassUpdate.java @@ -38,7 +38,7 @@ public class MassUpdate { * * @return true if the row must be updated, else false. If false, then the update parameter must not be touched. */ - boolean handle(Select.Row row, SqlStatement update) throws SQLException; + boolean handle(Select.Row row, Upsert update) throws SQLException; } @FunctionalInterface @@ -49,7 +49,7 @@ public class MassUpdate { * @param updateIndex 0-based * @return true if the row must be updated, else false. If false, then the update parameter must not be touched. */ - boolean handle(Select.Row row, SqlStatement update, int updateIndex) throws SQLException; + boolean handle(Select.Row row, Upsert update, int updateIndex) throws SQLException; } private final Database db; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractAddMeasuresMigratedColumnToTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractAddMeasuresMigratedColumnToTable.java new file mode 100644 index 00000000000..0c3ed45418c --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractAddMeasuresMigratedColumnToTable.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.def.BooleanColumnDef; +import org.sonar.server.platform.db.migration.def.ColumnDef; +import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class AbstractAddMeasuresMigratedColumnToTable extends DdlChange { + + public static final String MIGRATION_FLAG_COLUMN_NAME = "measures_migrated"; + private final String tableName; + + public AbstractAddMeasuresMigratedColumnToTable(Database db, String tableName) { + super(db); + this.tableName = tableName; + } + + @Override + public void execute(Context context) throws SQLException { + try (Connection connection = getDatabase().getDataSource().getConnection()) { + if (!DatabaseUtils.tableColumnExists(connection, tableName, MIGRATION_FLAG_COLUMN_NAME)) { + ColumnDef columnDef = BooleanColumnDef.newBooleanColumnDefBuilder() + .setColumnName(MIGRATION_FLAG_COLUMN_NAME) + .setIsNullable(false) + .setDefaultValue(false) + .build(); + context.execute(new AddColumnsBuilder(getDialect(), tableName) + .addColumn(columnDef) + .build()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractCreateIndexOnMeasuresMigrated.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractCreateIndexOnMeasuresMigrated.java new file mode 100644 index 00000000000..120e395ce8c --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractCreateIndexOnMeasuresMigrated.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class AbstractCreateIndexOnMeasuresMigrated extends DdlChange { + + static final String COLUMN_NAME = "measures_migrated"; + private final String tableName; + private final String indexName; + + public AbstractCreateIndexOnMeasuresMigrated(Database db, String tableName, String indexName) { + super(db); + this.tableName = tableName; + this.indexName = indexName; + } + + @Override + public void execute(Context context) throws SQLException { + try (Connection connection = getDatabase().getDataSource().getConnection()) { + if (!DatabaseUtils.indexExistsIgnoreCase(tableName, indexName, connection)) { + context.execute(new CreateIndexBuilder(getDialect()) + .setTable(tableName) + .setName(indexName) + .addColumn(COLUMN_NAME, false) + .build()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractMigrateLiveMeasuresToMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractMigrateLiveMeasuresToMeasures.java new file mode 100644 index 00000000000..cdb53badd79 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractMigrateLiveMeasuresToMeasures.java @@ -0,0 +1,204 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import com.google.gson.Gson; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nullable; +import org.apache.commons.codec.digest.MurmurHash3; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.utils.System2; +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.Upsert; + +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; + +public abstract class AbstractMigrateLiveMeasuresToMeasures extends DataChange { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractMigrateLiveMeasuresToMeasures.class); + + private static final Set<String> TEXT_VALUE_TYPES = Set.of("STRING", "LEVEL", "DATA", "DISTRIB"); + private static final Gson GSON = new Gson(); + + private static final String SELECT_QUERY = """ + SELECT lm.component_uuid, + m.name, + m.val_type, + lm.value, + lm.text_value, + lm.measure_data + FROM live_measures lm + INNER JOIN metrics m ON m.uuid = lm.metric_uuid + WHERE lm.project_uuid = ? + ORDER BY lm.component_uuid + """; + + private static final String INSERT_QUERY = """ + insert into measures (component_uuid, branch_uuid, json_value, json_value_hash, created_at, updated_at) + values ( ?, ?, ?, ?, ?, ?) + """; + + private final String tableName; + private final String item; + private final System2 system2; + + protected AbstractMigrateLiveMeasuresToMeasures(Database db, System2 system2, String tableName, String item) { + super(db); + this.system2 = system2; + this.tableName = tableName; + this.item = item; + } + + private String getSelectUuidQuery() { + return format(""" + SELECT uuid + FROM %s + WHERE measures_migrated = ? + """, tableName); + } + + private String getCountQuery() { + return format(""" + SELECT count(uuid) + FROM %s + """, tableName); + } + + private String getUpdateFlagQuery() { + return format(""" + UPDATE %s + SET measures_migrated = ? + WHERE uuid = ? + """, tableName); + } + + @Override + protected void execute(Context context) throws SQLException { + List<String> uuids = context.prepareSelect(getSelectUuidQuery()) + .setBoolean(1, false) + .list(row -> row.getString(1)); + + Long total = context.prepareSelect(getCountQuery()) + .get(row -> row.getLong(1)); + + LOGGER.info("Starting the migration of {} {}s (total number of {}s: {})", uuids.size(), item, item, total); + int migrated = 0; + + for (String uuid : uuids) { + migrateItem(uuid, context); + + migrated++; + if (migrated % 100 == 0) { + LOGGER.info("{} {}s migrated", migrated, item); + } + } + } + + private void migrateItem(String uuid, Context context) throws SQLException { + LOGGER.debug("Migrating {} {}...", item, uuid); + + Map<String, Object> measureValues = new HashMap<>(); + AtomicReference<String> componentUuid = new AtomicReference<>(null); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select(SELECT_QUERY).setString(1, uuid); + massUpdate.update(INSERT_QUERY); + massUpdate.execute((row, update) -> { + boolean shouldUpdate = false; + String rowComponentUuid = row.getString(1); + if (componentUuid.get() == null || !rowComponentUuid.equals(componentUuid.get())) { + if (!measureValues.isEmpty()) { + preparePersistMeasure(uuid, update, componentUuid, measureValues); + shouldUpdate = true; + } + + LOGGER.debug("Starting processing of component {}...", rowComponentUuid); + componentUuid.set(rowComponentUuid); + measureValues.clear(); + readMeasureValue(row, measureValues); + } else { + readMeasureValue(row, measureValues); + } + return shouldUpdate; + }); + // insert the last component + if (!measureValues.isEmpty()) { + Upsert measureInsert = context.prepareUpsert(INSERT_QUERY); + preparePersistMeasure(uuid, measureInsert, componentUuid, measureValues); + measureInsert + .execute() + .commit(); + } + + LOGGER.debug("Flagging migration done for {} {}...", item, uuid); + + context.prepareUpsert(getUpdateFlagQuery()) + .setBoolean(1, true) + .setString(2, uuid) + .execute() + .commit(); + + LOGGER.debug("Migration finished for {} {}", item, uuid); + } + + private void preparePersistMeasure(String uuid, Upsert update, AtomicReference<String> componentUuid, Map<String, Object> measureValues) throws SQLException { + LOGGER.debug("Persisting measures for component {}...", componentUuid.get()); + String jsonValue = GSON.toJson(measureValues); + + long jsonHash = MurmurHash3.hash128(jsonValue.getBytes(UTF_8))[0]; + + update.setString(1, componentUuid.get()); + update.setString(2, uuid); + update.setString(3, jsonValue); + update.setLong(4, jsonHash); + update.setLong(5, system2.now()); + update.setLong(6, system2.now()); + } + + private static void readMeasureValue(Select.Row row, Map<String, Object> measureValues) throws SQLException { + String metricName = row.getString(2); + String valueType = row.getString(3); + Double numericValue = row.getDouble(4); + String textValue = row.getString(5); + byte[] data = row.getBytes(6); + + Object metricValue = getMetricValue(data, textValue, valueType, numericValue); + if (metricValue != null) { + measureValues.put(metricName, metricValue); + } + } + + private static Object getMetricValue(@Nullable byte[] data, @Nullable String textValue, String valueType, Double numericValue) { + return TEXT_VALUE_TYPES.contains(valueType) ? getTextValue(data, textValue) : numericValue; + } + + private static String getTextValue(@Nullable byte[] data, @Nullable String textValue) { + return data != null ? new String(data, UTF_8) : textValue; + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToPortfoliosTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToPortfoliosTable.java new file mode 100644 index 00000000000..7a16c70a198 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToPortfoliosTable.java @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import org.sonar.db.Database; + +public class AddMeasuresMigratedColumnToPortfoliosTable extends AbstractAddMeasuresMigratedColumnToTable { + + static final String PORTFOLIOS_TABLE_NAME = "portfolios"; + + public AddMeasuresMigratedColumnToPortfoliosTable(Database db) { + super(db, PORTFOLIOS_TABLE_NAME); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToProjectBranchesTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToProjectBranchesTable.java new file mode 100644 index 00000000000..fa77640db0e --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToProjectBranchesTable.java @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import org.sonar.db.Database; + +public class AddMeasuresMigratedColumnToProjectBranchesTable extends AbstractAddMeasuresMigratedColumnToTable { + + public static final String PROJECT_BRANCHES_TABLE_NAME = "project_branches"; + + public AddMeasuresMigratedColumnToProjectBranchesTable(Database db) { + super(db, PROJECT_BRANCHES_TABLE_NAME); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnPortfoliosMeasuresMigrated.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnPortfoliosMeasuresMigrated.java new file mode 100644 index 00000000000..639c121d131 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnPortfoliosMeasuresMigrated.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import org.sonar.db.Database; + +public class CreateIndexOnPortfoliosMeasuresMigrated extends AbstractCreateIndexOnMeasuresMigrated { + + static final String TABLE_NAME = "portfolios"; + static final String INDEX_NAME = "portfolios_measures_migrated"; + + public CreateIndexOnPortfoliosMeasuresMigrated(Database db) { + super(db, TABLE_NAME, INDEX_NAME); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnProjectBranchesMeasuresMigrated.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnProjectBranchesMeasuresMigrated.java new file mode 100644 index 00000000000..d1a805ab3a5 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnProjectBranchesMeasuresMigrated.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import org.sonar.db.Database; + +public class CreateIndexOnProjectBranchesMeasuresMigrated extends AbstractCreateIndexOnMeasuresMigrated { + + static final String TABLE_NAME = "project_branches"; + static final String INDEX_NAME = "pb_measures_migrated"; + + public CreateIndexOnProjectBranchesMeasuresMigrated(Database db) { + super(db, TABLE_NAME, INDEX_NAME); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/DbVersion107.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/DbVersion107.java index 1c6bbd95afa..742ecd58643 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/DbVersion107.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/DbVersion107.java @@ -58,9 +58,14 @@ public class DbVersion107 implements DbVersion { .add(10_7_014, "Create 'issues_dependency' table", CreateIssuesDependencyTable.class) .add(10_7_015, "Add 'ai_code_assurance' column to 'projects' table", AddAiCodeAssuranceColumnInProjectsTable.class) .add(10_7_016, "Create 'measures' table", CreateMeasuresTable.class) - // TODO data migration - .add(10_7_018, "Create primary key on 'measures' table", CreatePrimaryKeyOnMeasuresTable.class) - .add(10_7_019, "Create index on column 'branch_uuid' in 'measures' table", CreateIndexOnMeasuresTable.class); + .add(10_7_017, "Add 'measures_migrated' column on 'project_branches' table", AddMeasuresMigratedColumnToProjectBranchesTable.class) + .add(10_7_018, "Create index on 'project_branches.measures_migrated'", CreateIndexOnProjectBranchesMeasuresMigrated.class) + .add(10_7_019, "Migrate the content of 'live_measures' to 'measures' for branches", MigrateBranchesLiveMeasuresToMeasures.class) + .add(10_7_020, "Add 'measures_migrated' column on 'portfolios' table", AddMeasuresMigratedColumnToPortfoliosTable.class) + .add(10_7_021, "Create index on 'portfolios.measures_migrated'", CreateIndexOnPortfoliosMeasuresMigrated.class) + .add(10_7_022, "Migrate the content of 'live_measures' to 'measures' for portfolios", MigratePortfoliosLiveMeasuresToMeasures.class) + .add(10_7_023, "Create primary key on 'measures' table", CreatePrimaryKeyOnMeasuresTable.class) + .add(10_7_024, "Create index on column 'branch_uuid' in 'measures' table", CreateIndexOnMeasuresTable.class); } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/MigrateBranchesLiveMeasuresToMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/MigrateBranchesLiveMeasuresToMeasures.java new file mode 100644 index 00000000000..135f887ebc9 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/MigrateBranchesLiveMeasuresToMeasures.java @@ -0,0 +1,30 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import org.sonar.api.utils.System2; +import org.sonar.db.Database; + +public class MigrateBranchesLiveMeasuresToMeasures extends AbstractMigrateLiveMeasuresToMeasures { + + public MigrateBranchesLiveMeasuresToMeasures(Database db, System2 system2) { + super(db, system2, "project_branches", "branch"); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/MigratePortfoliosLiveMeasuresToMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/MigratePortfoliosLiveMeasuresToMeasures.java new file mode 100644 index 00000000000..9ea66fd6382 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/MigratePortfoliosLiveMeasuresToMeasures.java @@ -0,0 +1,30 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.v107; + +import org.sonar.api.utils.System2; +import org.sonar.db.Database; + +public class MigratePortfoliosLiveMeasuresToMeasures extends AbstractMigrateLiveMeasuresToMeasures { + + protected MigratePortfoliosLiveMeasuresToMeasures(Database db, System2 system2) { + super(db, system2, "portfolios", "portfolio"); + } +} |