aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-db-migration
diff options
context:
space:
mode:
authorClaire Villard <60586848+claire-villard-sonarsource@users.noreply.github.com>2024-09-27 09:57:19 +0200
committersonartech <sonartech@sonarsource.com>2024-10-09 20:02:47 +0000
commite8ef7e9b39e0ea580d82df8b7b4fabbcc6e41f01 (patch)
treed5a488342abfecf96e914f18cf735c769901b9eb /server/sonar-db-migration
parenta79666b02068f247ea26944d3ea0d0181365c3b7 (diff)
downloadsonarqube-e8ef7e9b39e0ea580d82df8b7b4fabbcc6e41f01.tar.gz
sonarqube-e8ef7e9b39e0ea580d82df8b7b4fabbcc6e41f01.zip
SONAR-22880 Migrate 'live_measures' to 'measures'
Diffstat (limited to 'server/sonar-db-migration')
-rw-r--r--server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToPortfoliosTableIT.java50
-rw-r--r--server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToProjectBranchesTableIT.java50
-rw-r--r--server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnPortfoliosMeasuresMigratedIT.java53
-rw-r--r--server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnProjectBranchesMeasuresMigratedIT.java54
-rw-r--r--server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/MigrateBranchesLiveMeasuresToMeasuresIT.java184
-rw-r--r--server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v107/MigratePortfoliosLiveMeasuresToMeasuresIT.java182
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MassUpdate.java4
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractAddMeasuresMigratedColumnToTable.java56
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractCreateIndexOnMeasuresMigrated.java53
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AbstractMigrateLiveMeasuresToMeasures.java204
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToPortfoliosTable.java31
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/AddMeasuresMigratedColumnToProjectBranchesTable.java31
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnPortfoliosMeasuresMigrated.java32
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/CreateIndexOnProjectBranchesMeasuresMigrated.java32
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/DbVersion107.java11
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/MigrateBranchesLiveMeasuresToMeasures.java30
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v107/MigratePortfoliosLiveMeasuresToMeasures.java30
17 files changed, 1082 insertions, 5 deletions
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");
+ }
+}