aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-db-migration/src
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2018-09-26 08:58:59 +0200
committerSonarTech <sonartech@sonarsource.com>2018-10-04 20:20:56 +0200
commit2c540713f9289d8cfd14a65f3b4c3c33a4696e20 (patch)
tree6f880db9407f37ccaf479cc79a8bf1c777a92b15 /server/sonar-db-migration/src
parente80c0f3d1e5cd459f88b7e0c41a2d9a7519e260f (diff)
downloadsonarqube-2c540713f9289d8cfd14a65f3b4c3c33a4696e20.tar.gz
sonarqube-2c540713f9289d8cfd14a65f3b4c3c33a4696e20.zip
SONAR-11310 add temporary columns to CE tables
- add main_component_uuid temporary columns to CE_QUEUE - add main_last_key and main_component_uuid columns to CE_ACTIVITY - back to initial paradigm in Compute Engine: even for branches/PRs, the row in table PROJECTS a task belongs to is created in api/ce/submit - add main component concept to CeTask - improved consistency check when processing a report task to account for row in PROJECTS now being the one of the branche/PR - stronger validation of characteristics passed to api/ce/submit - add api/system/migrate_data for SonarCloud online data migration
Diffstat (limited to 'server/sonar-db-migration/src')
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivity.java31
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueue.java31
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeTable.java70
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivity.java105
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java6
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivity.java104
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueue.java135
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivity.java83
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest.java56
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest.java56
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest.java62
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java2
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest.java233
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest.java216
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest.java118
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/Row.java67
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest/ce_activity.sql26
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest/ce_queue.sql16
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest/ce_activity.sql30
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest/ce_activity.sql87
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest/ce_queue.sql77
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest/ce_activity.sql38
22 files changed, 1648 insertions, 1 deletions
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivity.java
new file mode 100644
index 00000000000..f394c338378
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivity.java
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+
+@SupportsBlueGreen
+public class AddTmpColumnsToCeActivity extends AddTmpColumnsToCeTable {
+
+ public AddTmpColumnsToCeActivity(Database db) {
+ super(db, "ce_activity");
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueue.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueue.java
new file mode 100644
index 00000000000..0cb136643b2
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueue.java
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+
+@SupportsBlueGreen
+public class AddTmpColumnsToCeQueue extends AddTmpColumnsToCeTable {
+
+ public AddTmpColumnsToCeQueue(Database db) {
+ super(db, "ce_queue");
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeTable.java
new file mode 100644
index 00000000000..3a2581897d0
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeTable.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+abstract class AddTmpColumnsToCeTable extends DdlChange {
+ private static final VarcharColumnDef COLUMN_TMP_COMPONENT_UUID = newVarcharColumnDefBuilder()
+ .setColumnName("tmp_component_uuid")
+ .setLimit(VarcharColumnDef.UUID_SIZE)
+ .setIsNullable(true)
+ .build();
+ private static final VarcharColumnDef COLUMN_TMP_MAIN_COMPONENT_UUID = newVarcharColumnDefBuilder()
+ .setColumnName("tmp_main_component_uuid")
+ .setLimit(VarcharColumnDef.UUID_SIZE)
+ .setIsNullable(true)
+ .build();
+ private final String tableName;
+
+ public AddTmpColumnsToCeTable(Database db, String tableName) {
+ super(db);
+ this.tableName = tableName;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDialect(), tableName)
+ .addColumn(COLUMN_TMP_MAIN_COMPONENT_UUID)
+ .addColumn(COLUMN_TMP_COMPONENT_UUID)
+ .build());
+
+ // create indexes
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(tableName)
+ .setName(tableName + "_tmp_cpnt_uuid")
+ .addColumn(COLUMN_TMP_COMPONENT_UUID)
+ .setUnique(false)
+ .build());
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(tableName)
+ .setName(tableName + "_tmp_main_cpnt_uuid")
+ .addColumn(COLUMN_TMP_MAIN_COMPONENT_UUID)
+ .setUnique(false)
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivity.java
new file mode 100644
index 00000000000..63980918339
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivity.java
@@ -0,0 +1,105 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.def.BooleanColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BooleanColumnDef.newBooleanColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+@SupportsBlueGreen
+public class AddTmpLastKeyColumnsToCeActivity extends DdlChange {
+ private static final String TABLE_NAME = "ce_activity";
+ private static final int TASK_TYPE_COLUMN_SIZE = 15;
+ private static final BooleanColumnDef COLUMN_TMP_IS_LAST = newBooleanColumnDefBuilder()
+ .setColumnName("tmp_is_last")
+ .setIsNullable(true)
+ .build();
+ private static final VarcharColumnDef COLUMN_TMP_IS_LAST_KEY = newVarcharColumnDefBuilder()
+ .setColumnName("tmp_is_last_key")
+ .setLimit(UUID_SIZE + TASK_TYPE_COLUMN_SIZE)
+ .setIsNullable(true)
+ .build();
+ private static final BooleanColumnDef COLUMN_TMP_MAIN_IS_LAST = newBooleanColumnDefBuilder()
+ .setColumnName("tmp_main_is_last")
+ .setIsNullable(true)
+ .build();
+ private static final VarcharColumnDef COLUMN_TMP_MAIN_IS_LAST_KEY = newVarcharColumnDefBuilder()
+ .setColumnName("tmp_main_is_last_key")
+ .setLimit(UUID_SIZE + TASK_TYPE_COLUMN_SIZE)
+ .setIsNullable(true)
+ .build();
+ private static final VarcharColumnDef COLUMN_STATUS = newVarcharColumnDefBuilder()
+ .setColumnName("status")
+ .setLimit(15)
+ .setIsNullable(false)
+ .build();
+
+ public AddTmpLastKeyColumnsToCeActivity(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME)
+ .addColumn(COLUMN_TMP_IS_LAST)
+ .addColumn(COLUMN_TMP_IS_LAST_KEY)
+ .addColumn(COLUMN_TMP_MAIN_IS_LAST)
+ .addColumn(COLUMN_TMP_MAIN_IS_LAST_KEY)
+ .build());
+
+ // create indexes
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(TABLE_NAME + "_t_islast_key")
+ .addColumn(COLUMN_TMP_IS_LAST_KEY)
+ .setUnique(false)
+ .build());
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(TABLE_NAME + "_t_islast")
+ .addColumn(COLUMN_TMP_IS_LAST)
+ .addColumn(COLUMN_STATUS)
+ .setUnique(false)
+ .build());
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(TABLE_NAME + "_t_main_islast_key")
+ .addColumn(COLUMN_TMP_MAIN_IS_LAST_KEY)
+ .setUnique(false)
+ .build());
+ context.execute(new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(TABLE_NAME + "_t_main_islast")
+ .addColumn(COLUMN_TMP_MAIN_IS_LAST)
+ .addColumn(COLUMN_STATUS)
+ .setUnique(false)
+ .build());
+
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java
index 4d45e1d0765..82b34413d0b 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java
@@ -32,6 +32,12 @@ public class DbVersion74 implements DbVersion {
.add(2302, "Populate IS_AD_HOC in RULES", PopulateIsAdHocOnRules.class)
.add(2303, "Set IS_EXTERNAL and IS_AD_HOC not nullable in RULES", SetIsExternalAndIsAdHocNotNullableInRules.class)
.add(2304, "Add ad hoc related columns in RULES_METADATA", AddAdHocColumnsInInRulesMetadata.class)
+ .add(2305, "Add CE_QUEUE.MAIN_COMPONENT_UUID 1/5", AddTmpColumnsToCeQueue.class)
+ .add(2306, "Add CE_ACTIVITY.MAIN_COMPONENT_UUID 1/5", AddTmpColumnsToCeActivity.class)
+ .add(2307, "Populate CE_QUEUE.MAIN_COMPONENT_UUID 2/5", PopulateTmpColumnsToCeQueue.class)
+ .add(2308, "Populate CE_ACTIVITY.MAIN_COMPONENT_UUID 2/5", PopulateTmpColumnsToCeActivity.class)
+ .add(2309, "Add CE_ACTIVITY.MAIN_LAST_KEY 1/6", AddTmpLastKeyColumnsToCeActivity.class)
+ .add(2310, "Populate CE_ACTIVITY.MAIN_LAST_KEY 2/6", PopulateTmpLastKeyColumnsToCeActivity.class)
;
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivity.java
new file mode 100644
index 00000000000..bcf08d5981e
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivity.java
@@ -0,0 +1,104 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import org.sonar.api.config.Configuration;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+@SupportsBlueGreen
+public class PopulateTmpColumnsToCeActivity extends DataChange {
+ private final Configuration configuration;
+
+ public PopulateTmpColumnsToCeActivity(Database db, Configuration configuration) {
+ super(db);
+ this.configuration = configuration;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ if (configuration.getBoolean("sonar.sonarcloud.enabled").orElse(false)) {
+ // data migration will be done in background so that interruption of service
+ // is reduced during upgrade
+ return;
+ }
+
+ // activity of long and short branches
+ populateCeActivityTmpColumns(context, "Archived tasks of branches",
+ "select" +
+ " cea.uuid, p.uuid, cea.component_uuid" +
+ " from ce_activity cea" +
+ " inner join projects mp on mp.uuid = cea.component_uuid" +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cea.uuid and ctc1.kee = 'branchType'" +
+ " inner join ce_task_characteristics ctc2 on ctc2.task_uuid = cea.uuid and ctc2.kee = 'branch'" +
+ " inner join projects p on p.kee = concat(mp.kee, ':BRANCH:', ctc2.text_value)" +
+ " where" +
+ " cea.component_uuid is not null" +
+ " and (cea.tmp_component_uuid is null or cea.tmp_main_component_uuid is null)");
+
+ // activity of PRs
+ populateCeActivityTmpColumns(context, "Archived tasks of PRs",
+ "select" +
+ " cea.uuid, p.uuid, cea.component_uuid" +
+ " from ce_activity cea" +
+ " inner join projects mp on mp.uuid = cea.component_uuid " +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cea.uuid and ctc1.kee = 'pullRequest'" +
+ " inner join projects p on p.kee = concat(mp.kee, ':PULL_REQUEST:', ctc1.text_value)" +
+ " where" +
+ " cea.component_uuid is not null" +
+ " and (cea.tmp_component_uuid is null or cea.tmp_main_component_uuid is null)");
+
+ // all activities which tmp columns are not populated yet (will include main and deprecated branches)
+ // both tmp columns will be set to CE_ACTIVITY.COMPONENT_UUID
+ // do not join on PROJECTS to also catch orphans
+ populateCeActivityTmpColumns(context, "Archived tasks of main and deprecated branches",
+ "select" +
+ " cea.uuid, cea.component_uuid, cea.component_uuid" +
+ " from ce_activity cea" +
+ " where" +
+ " cea.component_uuid is not null" +
+ " and (cea.tmp_component_uuid is null or cea.tmp_main_component_uuid is null)");
+ }
+
+ private static void populateCeActivityTmpColumns(Context context, String rowPluralName, String sql) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select(sql);
+ massUpdate.update("update ce_activity set tmp_component_uuid=?, tmp_main_component_uuid=? where uuid=?");
+ massUpdate.rowPluralName(rowPluralName);
+ massUpdate.execute(PopulateTmpColumnsToCeActivity::handleUpdate);
+ }
+
+ private static boolean handleUpdate(Select.Row row, SqlStatement update) throws SQLException {
+ String uuid = row.getString(1);
+ String componentUuuid = row.getString(2);
+ String mainComponentUuuid = row.getString(3);
+
+ update.setString(1, componentUuuid);
+ update.setString(2, mainComponentUuuid);
+ update.setString(3, uuid);
+
+ return true;
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueue.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueue.java
new file mode 100644
index 00000000000..e19e274bfd0
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueue.java
@@ -0,0 +1,135 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+@SupportsBlueGreen
+public class PopulateTmpColumnsToCeQueue extends DataChange {
+ public PopulateTmpColumnsToCeQueue(Database db) {
+ super(db);
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ // queued task of long and short branches which have already been analyzed at least once
+ populateCeQueueTmpColumns(context, "queued tasks of branches",
+ "select" +
+ " cq.uuid, p.uuid, cq.component_uuid" +
+ " from ce_queue cq" +
+ " inner join projects mp on mp.uuid = cq.component_uuid" +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cq.uuid and ctc1.kee = 'branchType'" +
+ " inner join ce_task_characteristics ctc2 on ctc2.task_uuid = cq.uuid and ctc2.kee = 'branch'" +
+ " inner join projects p on p.kee = concat(mp.kee, ':BRANCH:', ctc2.text_value)" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)");
+
+ // queued task of pull request which have already been analyzed at least once
+ populateCeQueueTmpColumns(context, "queued tasks of PRs",
+ " select" +
+ " cq.uuid, p.uuid, cq.component_uuid" +
+ " from ce_queue cq" +
+ " inner join projects mp on mp.uuid = cq.component_uuid " +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cq.uuid and ctc1.kee = 'pullRequest'" +
+ " inner join projects p on p.kee = concat(mp.kee, ':PULL_REQUEST:', ctc1.text_value)" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)");
+
+ // queued task of long and short branches which have never been analyzed must be deleted
+ deleteFromCeQueue(context, "queued tasks of never analyzed branches",
+ "select" +
+ " cq.uuid" +
+ " from ce_queue cq" +
+ " inner join projects mp on mp.uuid = cq.component_uuid" +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cq.uuid and ctc1.kee = 'branchType'" +
+ " inner join ce_task_characteristics ctc2 on ctc2.task_uuid = cq.uuid and ctc2.kee = 'branch'" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)" +
+ " and not exists (select 1 from projects p where p.kee = concat(mp.kee, ':BRANCH:', ctc2.text_value))");
+
+ // queued of pull request which have never been analyzed must be deleted
+ deleteFromCeQueue(context, "queued tasks of never analyzed PRs",
+ "select" +
+ " cq.uuid" +
+ " from ce_queue cq" +
+ " inner join projects mp on mp.uuid = cq.component_uuid " +
+ " inner join ce_task_characteristics ctc1 on ctc1.task_uuid = cq.uuid and ctc1.kee = 'pullRequest'" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)" +
+ " and not exists (select 1 from projects p where p.kee = concat(mp.kee, ':PULL_REQUEST:', ctc1.text_value))");
+
+ // all queue which tmp columns are not populated yet (will include main and deprecated branches)
+ // both tmp columns will be set to CE_QUEUE.COMPONENT_UUID
+ // do not join on PROJECTS to also catch orphans (there are many for branch and PRs due to SONAR-10642)
+ populateCeQueueTmpColumns(context, "queued tasks of main and deprecated branches",
+ "select" +
+ " cq.uuid, cq.component_uuid, cq.component_uuid" +
+ " from ce_queue cq" +
+ " where" +
+ " cq.component_uuid is not null" +
+ " and (cq.tmp_component_uuid is null or cq.tmp_main_component_uuid is null)");
+ }
+
+ private static void populateCeQueueTmpColumns(Context context, String pluralName, String selectSQL) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select(selectSQL);
+ massUpdate.update("update ce_queue set tmp_component_uuid=?, tmp_main_component_uuid=? where uuid=?");
+ massUpdate.rowPluralName(pluralName);
+ massUpdate.execute(PopulateTmpColumnsToCeQueue::handleUpdate);
+ }
+
+ private static boolean handleUpdate(Select.Row row, SqlStatement update) throws SQLException {
+ String uuid = row.getString(1);
+ String componentUuuid = row.getString(2);
+ String mainComponentUuuid = row.getString(3);
+
+ update.setString(1, componentUuuid);
+ update.setString(2, mainComponentUuuid);
+ update.setString(3, uuid);
+
+ return true;
+ }
+
+ private static void deleteFromCeQueue(Context context, String pluralName, String sql) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select(sql);
+ massUpdate.update("delete from ce_queue where uuid = ?");
+ massUpdate.rowPluralName(pluralName);
+ massUpdate.execute(PopulateTmpColumnsToCeQueue::handleDelete);
+ }
+
+ private static boolean handleDelete(Select.Row row, SqlStatement update) throws SQLException {
+ String uuid = row.getString(1);
+
+ update.setString(1, uuid);
+
+ return true;
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivity.java
new file mode 100644
index 00000000000..81b9340a751
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivity.java
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import org.sonar.api.config.Configuration;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+@SupportsBlueGreen
+public class PopulateTmpLastKeyColumnsToCeActivity extends DataChange {
+ private final Configuration configuration;
+
+ public PopulateTmpLastKeyColumnsToCeActivity(Database db, Configuration configuration) {
+ super(db);
+ this.configuration = configuration;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ if (configuration.getBoolean("sonar.sonarcloud.enabled").orElse(false)) {
+ // data migration will be done in background so that interruption of service
+ // is reduced during upgrade
+ return;
+ }
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select" +
+ " cea.uuid, cea.task_type, cea.component_uuid, cea.tmp_component_uuid, cea.tmp_main_component_uuid" +
+ " from ce_activity cea" +
+ " where" +
+ " cea.tmp_is_last is null" +
+ " or cea.tmp_is_last_key is null" +
+ " or cea.tmp_main_is_last is null" +
+ " or cea.tmp_main_is_last_key is null");
+ massUpdate.update("update ce_activity" +
+ " set" +
+ " tmp_is_last=?" +
+ " ,tmp_is_last_key=?" +
+ " ,tmp_main_is_last=?" +
+ " ,tmp_main_is_last_key=?" +
+ " where uuid=?");
+ massUpdate.rowPluralName("rows of ce_activity");
+ massUpdate.execute(PopulateTmpLastKeyColumnsToCeActivity::handleUpdate);
+ }
+
+ private static boolean handleUpdate(Select.Row row, SqlStatement update) throws SQLException {
+ String uuid = row.getString(1);
+ String taskType = row.getString(2);
+ String oldComponentUuid = row.getString(3);
+ String componentUuuid = row.getString(4);
+ String mainComponentUuuid = row.getString(5);
+
+ update.setBoolean(1, false);
+ update.setString(2, oldComponentUuid == null ? taskType : (taskType + componentUuuid));
+ update.setBoolean(3, false);
+ update.setString(4, oldComponentUuid == null ? taskType : (taskType + mainComponentUuuid));
+ update.setString(5, uuid);
+
+ return true;
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest.java
new file mode 100644
index 00000000000..f0f703699d3
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class AddTmpColumnsToCeActivityTest {
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(AddTmpColumnsToCeActivityTest.class, "ce_activity.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddTmpColumnsToCeActivity underTest = new AddTmpColumnsToCeActivity(db.database());
+
+ @Test
+ public void columns_and_indexes_are_added_to_table() throws SQLException {
+ underTest.execute();
+
+ db.assertColumnDefinition("ce_activity", "tmp_component_uuid", VARCHAR, 40, true);
+ db.assertColumnDefinition("ce_activity", "tmp_main_component_uuid", VARCHAR, 40, true);
+ db.assertIndex("ce_activity", "ce_activity_tmp_cpnt_uuid", "tmp_component_uuid");
+ db.assertIndex("ce_activity", "ce_activity_tmp_main_cpnt_uuid", "tmp_main_component_uuid");
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest.java
new file mode 100644
index 00000000000..97b062a8d9e
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class AddTmpColumnsToCeQueueTest {
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(AddTmpColumnsToCeQueueTest.class, "ce_queue.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddTmpColumnsToCeQueue underTest = new AddTmpColumnsToCeQueue(db.database());
+
+ @Test
+ public void columns_and_indexes_are_added_to_table() throws SQLException {
+ underTest.execute();
+
+ db.assertColumnDefinition("ce_queue", "tmp_component_uuid", VARCHAR, 40, true);
+ db.assertColumnDefinition("ce_queue", "tmp_main_component_uuid", VARCHAR, 40, true);
+ db.assertIndex("ce_queue", "ce_queue_tmp_cpnt_uuid", "tmp_component_uuid");
+ db.assertIndex("ce_queue", "ce_queue_tmp_main_cpnt_uuid", "tmp_main_component_uuid");
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest.java
new file mode 100644
index 00000000000..de71958cc25
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.BOOLEAN;
+import static java.sql.Types.VARCHAR;
+
+public class AddTmpLastKeyColumnsToCeActivityTest {
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(AddTmpLastKeyColumnsToCeActivityTest.class, "ce_activity.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddTmpLastKeyColumnsToCeActivity underTest = new AddTmpLastKeyColumnsToCeActivity(db.database());
+
+ @Test
+ public void columns_and_indexes_are_added_to_table() throws SQLException {
+ underTest.execute();
+
+ db.assertColumnDefinition("ce_activity", "tmp_is_last", BOOLEAN, null, true);
+ db.assertColumnDefinition("ce_activity", "tmp_is_last_key", VARCHAR, 55, true);
+ db.assertColumnDefinition("ce_activity", "tmp_main_is_last", BOOLEAN, null, true);
+ db.assertColumnDefinition("ce_activity", "tmp_main_is_last_key", VARCHAR, 55, true);
+ db.assertIndex("ce_activity", "ce_activity_t_islast_key", "tmp_is_last_key");
+ db.assertIndex("ce_activity", "ce_activity_t_main_islast", "tmp_main_is_last", "status");
+ db.assertIndex("ce_activity", "ce_activity_t_main_islast_key", "tmp_main_is_last_key");
+ db.assertIndex("ce_activity", "ce_activity_t_main_islast", "tmp_main_is_last", "status");
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java
index 28dc704bf77..c0ba8282e1c 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java
@@ -35,6 +35,6 @@ public class DbVersion74Test {
@Test
public void verify_migration_count() {
- verifyMigrationCount(underTest, 5);
+ verifyMigrationCount(underTest, 11);
}
}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest.java
new file mode 100644
index 00000000000..298ae547b9d
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest.java
@@ -0,0 +1,233 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import com.google.common.collect.ImmutableMap;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.db.CoreDbTester;
+
+import static java.util.Arrays.stream;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class PopulateTmpColumnsToCeActivityTest {
+ private static final Map<String, String> NO_CHARACTERISTICS = Collections.emptyMap();
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateTmpColumnsToCeActivityTest.class, "ce_activity.sql");
+
+ private MapSettings settings = new MapSettings();
+ private PopulateTmpColumnsToCeActivity underTest = new PopulateTmpColumnsToCeActivity(db.database(), settings.asConfig());
+
+ @Test
+ public void execute_has_no_effect_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(rowsInCeActivity()).isEmpty();
+ }
+
+ @Test
+ public void execute_has_no_effect_on_empty_table_on_sonarcloud() throws SQLException {
+ settings.setProperty("sonar.sonarcloud.enabled", true);
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity()).isEmpty();
+ }
+
+ @Test
+ @UseDataProvider("characteriticsOfMainBranchesAndPr")
+ public void execute_populates_tmp_columns_with_component_uuid_if_task_has_no_row_in_PROJECTS(Map<String, String> characteristics) throws SQLException {
+ Row[] notUpdatedRows = Stream.of(
+ // not updated because no component_uuid
+ new Row(newUuid(), null, null, null),
+ new Row(newUuid(), null, randomAlphabetic(2), null),
+ new Row(newUuid(), null, randomAlphabetic(3), randomAlphabetic(4)),
+ new Row(newUuid(), null, null, randomAlphabetic(5)),
+ // not updated because both target fields are already set (re-entrance)
+ new Row(newUuid(), randomAlphabetic(14), randomAlphabetic(6), randomAlphabetic(7)))
+ .toArray(Row[]::new);
+ Row[] updatedRows = {
+ new Row(newUuid(), randomAlphabetic(12), null, null),
+ new Row(newUuid(), randomAlphabetic(13), randomAlphabetic(5), null),
+ new Row(newUuid(), randomAlphabetic(14), null, randomAlphabetic(6)),
+ };
+ stream(notUpdatedRows).forEach(row -> insertCeActivity(row, characteristics));
+ stream(updatedRows).forEach(row -> insertCeActivity(row, characteristics));
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity())
+ .hasSize(notUpdatedRows.length + updatedRows.length)
+ .contains(notUpdatedRows)
+ .contains(stream(updatedRows)
+ .map(row -> new Row(row.taskUuid, row.componentUuid, row.componentUuid, row.componentUuid))
+ .toArray(Row[]::new));
+ }
+
+ @DataProvider
+ public static Object[][] characteriticsOfMainBranchesAndPr() {
+ return new Object[][] {
+ {NO_CHARACTERISTICS},
+ {branchCharacteristics("LONG", randomAlphabetic(15))},
+ {branchCharacteristics("SHORT", randomAlphabetic(16))},
+ {branchCharacteristics(randomAlphabetic(17), randomAlphabetic(18))},
+ {prCharacteristics(randomAlphabetic(19))},
+ };
+ }
+
+ @Test
+ public void execute_populates_tmp_columns_with_component_uuid_for_existing_main_branch() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ insertProjects(mainComponentUuid, randomAlphabetic(3));
+ String taskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), NO_CHARACTERISTICS);
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity())
+ .containsOnly(new Row(taskUuid, mainComponentUuid, mainComponentUuid, mainComponentUuid));
+ }
+
+ @Test
+ public void execute_deletes_populates_branches_of_task_without_row_in_PROJECTS_with_COMPONENT_UUID_and_those_with_row_in_PROJECTS_by_KEE() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ String mainComponentKey = randomAlphabetic(3);
+ String branchUuid = randomAlphabetic(4);
+ String branchType1 = randomAlphabetic(5);
+ String branchName1 = randomAlphabetic(6);
+ String branchType2 = randomAlphabetic(7);
+ String branchName2 = randomAlphabetic(8);
+ insertProjects(mainComponentUuid, mainComponentKey);
+ insertProjects(branchUuid, mainComponentKey + ":BRANCH:" + branchName2);
+ String orphanTaskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), branchCharacteristics(branchType1, branchName1));
+ String regularTaskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), branchCharacteristics(branchType2, branchName2));
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity())
+ .containsOnly(
+ new Row(orphanTaskUuid, mainComponentUuid, mainComponentUuid, mainComponentUuid),
+ new Row(regularTaskUuid, mainComponentUuid, branchUuid, mainComponentUuid));
+ }
+
+ @Test
+ public void execute_deletes_populates_prs_of_task_without_row_in_PROJECTS_with_COMPONENT_UUID_and_those_with_row_in_PROJECTS_by_KEE() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ String mainComponentKey = randomAlphabetic(3);
+ String prUuid = randomAlphabetic(4);
+ String prName1 = randomAlphabetic(6);
+ String prName2 = randomAlphabetic(8);
+ insertProjects(mainComponentUuid, mainComponentKey);
+ insertProjects(prUuid, mainComponentKey + ":PULL_REQUEST:" + prName2);
+ String orphanTaskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), prCharacteristics(prName1));
+ String regularTaskUuid = insertCeActivity(new Row(newUuid(), mainComponentUuid, null, null), prCharacteristics(prName2));
+
+ underTest.execute();
+
+ assertThat(rowsInCeActivity())
+ .containsOnly(
+ new Row(orphanTaskUuid, mainComponentUuid, mainComponentUuid, mainComponentUuid),
+ new Row(regularTaskUuid, mainComponentUuid, prUuid, mainComponentUuid));
+ }
+
+ private Stream<Row> rowsInCeActivity() {
+ return db.select("select" +
+ " uuid as \"UUID\", component_uuid as \"COMPONENT_UUID\", tmp_component_uuid as \"TMP_COMPONENT_UUID\", tmp_main_component_UUID as \"TMP_MAIN_COMPONENT_UUID\"" +
+ " from ce_activity")
+ .stream()
+ .map(row -> new Row(
+ (String) row.get("UUID"),
+ (String) row.get("COMPONENT_UUID"),
+ (String) row.get("TMP_COMPONENT_UUID"),
+ (String) row.get("TMP_MAIN_COMPONENT_UUID")));
+ }
+
+ private String insertCeActivity(Row row, Map<String, String> characteristics) {
+ String uuid = insertCeActivity(row.taskUuid, row.componentUuid, row.tmpComponentUuid, row.tmpMainComponentUuid);
+ characteristics.forEach((key, value) -> insertCeCharacteristic(uuid, key, value));
+ return uuid;
+ }
+
+ private String insertCeActivity(String uuid, @Nullable String componentUuid, @Nullable String tmpComponentUuid, @Nullable String tmpMainComponentUuid) {
+ Random random = new Random();
+ db.executeInsert("ce_activity",
+ "UUID", uuid,
+ "TASK_TYPE", randomAlphabetic(6),
+ "COMPONENT_UUID", componentUuid,
+ "TMP_COMPONENT_UUID", tmpComponentUuid,
+ "TMP_MAIN_COMPONENT_UUID", tmpMainComponentUuid,
+ "STATUS", randomAlphabetic(7),
+ "IS_LAST", random.nextBoolean(),
+ "IS_LAST_KEY", random.nextBoolean(),
+ "EXECUTION_COUNT", random.nextInt(500),
+ "SUBMITTED_AT", (long) random.nextInt(500),
+ "CREATED_AT", (long) random.nextInt(500),
+ "UPDATED_AT", (long) random.nextInt(500));
+ return uuid;
+ }
+
+ private void insertCeCharacteristic(String taskUuid, String key, String value) {
+ db.executeInsert(
+ "ce_task_characteristics",
+ "UUID", newUuid(),
+ "TASK_UUID", taskUuid,
+ "KEE", key,
+ "TEXT_VALUE", value);
+ }
+
+ private void insertProjects(String uuid, String key) {
+ db.executeInsert(
+ "PROJECTS",
+ "UUID", uuid,
+ "KEE", key,
+ "ORGANIZATION_UUID", "org_" + uuid,
+ "ROOT_UUID", uuid + "_root",
+ "UUID_PATH", uuid + "_path",
+ "PROJECT_UUID", uuid + "_project",
+ "PRIVATE", new Random().nextBoolean());
+ }
+
+ private int uuidGenerator = new Random().nextInt(9000);
+
+ private String newUuid() {
+ return "uuid_" + uuidGenerator++;
+ }
+
+ private static Map<String, String> branchCharacteristics(String branchType, String branchName) {
+ return ImmutableMap.of("branchType", branchType, "branch", branchName);
+ }
+
+ private static Map<String, String> prCharacteristics(String prName) {
+ return ImmutableMap.of("pullRequest", prName);
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest.java
new file mode 100644
index 00000000000..866eaa49026
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest.java
@@ -0,0 +1,216 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import com.google.common.collect.ImmutableMap;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.db.CoreDbTester;
+
+import static java.util.Arrays.stream;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class PopulateTmpColumnsToCeQueueTest {
+ private static final Map<String, String> NO_CHARACTERISTICS = Collections.emptyMap();
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateTmpColumnsToCeQueueTest.class, "ce_queue.sql");
+
+ private PopulateTmpColumnsToCeQueue underTest = new PopulateTmpColumnsToCeQueue(db.database());
+
+ @Test
+ public void no_action_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable("ce_queue")).isZero();
+ }
+
+ @Test
+ @UseDataProvider("characteriticsOfMainBranchesAndPr")
+ public void execute_populates_tmp_columns_with_component_uuid_if_task_has_no_row_in_PROJECTS(Map<String, String> characteristics) throws SQLException {
+ Row[] notUpdatedRows = Stream.of(
+ // not updated because no component_uuid
+ new Row(newUuid(), null, null, null),
+ new Row(newUuid(), null, randomAlphabetic(2), null),
+ new Row(newUuid(), null, randomAlphabetic(3), randomAlphabetic(4)),
+ new Row(newUuid(), null, null, randomAlphabetic(5)),
+ // not updated because both target fields are already set (re-entrance)
+ new Row(newUuid(), randomAlphabetic(14), randomAlphabetic(6), randomAlphabetic(7)))
+ .toArray(Row[]::new);
+ Row[] updatedRows = {
+ new Row(newUuid(), randomAlphabetic(12), null, null),
+ new Row(newUuid(), randomAlphabetic(13), randomAlphabetic(5), null),
+ new Row(newUuid(), randomAlphabetic(14), null, randomAlphabetic(6)),
+ };
+ stream(notUpdatedRows).forEach(row -> insertCeQueue(row, characteristics));
+ stream(updatedRows).forEach(row -> insertCeQueue(row, characteristics));
+
+ underTest.execute();
+
+ assertThat(rowsInCeQueue())
+ .hasSize(notUpdatedRows.length + updatedRows.length)
+ .contains(notUpdatedRows)
+ .contains(stream(updatedRows)
+ .map(row -> new Row(row.taskUuid, row.componentUuid, row.componentUuid, row.componentUuid))
+ .toArray(Row[]::new));
+ }
+
+ @DataProvider
+ public static Object[][] characteriticsOfMainBranchesAndPr() {
+ return new Object[][] {
+ {NO_CHARACTERISTICS},
+ {branchCharacteristics("LONG", randomAlphabetic(15))},
+ {branchCharacteristics("SHORT", randomAlphabetic(16))},
+ {branchCharacteristics(randomAlphabetic(17), randomAlphabetic(18))},
+ {prCharacteristics(randomAlphabetic(19))},
+ };
+ }
+
+ @Test
+ public void execute_populates_tmp_columns_with_component_uuid_for_existing_main_branch() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ insertProjects(mainComponentUuid, randomAlphabetic(3));
+ String taskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), NO_CHARACTERISTICS);
+
+ underTest.execute();
+
+ assertThat(rowsInCeQueue())
+ .containsOnly(new Row(taskUuid, mainComponentUuid, mainComponentUuid, mainComponentUuid));
+ }
+
+ @Test
+ public void execute_deletes_tasks_of_branches_without_row_in_PROJECTS_and_populates_others_matching_row_in_PROJECTS_by_KEE() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ String mainComponentKey = randomAlphabetic(3);
+ String branchUuid = randomAlphabetic(4);
+ String branchType1 = randomAlphabetic(5);
+ String branchName1 = randomAlphabetic(6);
+ String branchType2 = randomAlphabetic(7);
+ String branchName2 = randomAlphabetic(8);
+ insertProjects(mainComponentUuid, mainComponentKey);
+ insertProjects(branchUuid, mainComponentKey + ":BRANCH:" + branchName2);
+ String deletedTaskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), branchCharacteristics(branchType1, branchName1));
+ String updatedTaskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), branchCharacteristics(branchType2, branchName2));
+
+ underTest.execute();
+
+ assertThat(rowsInCeQueue())
+ .containsOnly(new Row(updatedTaskUuid, mainComponentUuid, branchUuid, mainComponentUuid));
+ }
+
+ @Test
+ public void execute_deletes_tasks_of_prs_without_row_in_PROJECTS_and_populates_others_matching_row_in_PROJECTS_by_KEE() throws SQLException {
+ String mainComponentUuid = randomAlphabetic(2);
+ String mainComponentKey = randomAlphabetic(3);
+ String prUuid = randomAlphabetic(4);
+ String prName1 = randomAlphabetic(6);
+ String prName2 = randomAlphabetic(8);
+ insertProjects(mainComponentUuid, mainComponentKey);
+ insertProjects(prUuid, mainComponentKey + ":PULL_REQUEST:" + prName2);
+ String deletedTaskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), prCharacteristics(prName1));
+ String updatedTaskUuid = insertCeQueue(new Row(newUuid(), mainComponentUuid, null, null), prCharacteristics(prName2));
+
+ underTest.execute();
+
+ assertThat(rowsInCeQueue())
+ .containsOnly(new Row(updatedTaskUuid, mainComponentUuid, prUuid, mainComponentUuid));
+ }
+
+ private Stream<Row> rowsInCeQueue() {
+ return db.select("select" +
+ " uuid as \"UUID\", component_uuid as \"COMPONENT_UUID\", tmp_component_uuid as \"TMP_COMPONENT_UUID\", tmp_main_component_UUID as \"TMP_MAIN_COMPONENT_UUID\"" +
+ " from ce_queue")
+ .stream()
+ .map(row -> new Row(
+ (String) row.get("UUID"),
+ (String) row.get("COMPONENT_UUID"),
+ (String) row.get("TMP_COMPONENT_UUID"),
+ (String) row.get("TMP_MAIN_COMPONENT_UUID")));
+ }
+
+ private String insertCeQueue(Row row, Map<String, String> characteristics) {
+ String uuid = insertCeQueue(row.taskUuid, row.componentUuid, row.tmpComponentUuid, row.tmpMainComponentUuid);
+ characteristics.forEach((key, value) -> insertCeCharacteristic(uuid, key, value));
+ return uuid;
+ }
+
+ private String insertCeQueue(String uuid, @Nullable String componentUuid, @Nullable String tmpComponentUuid, @Nullable String tmpMainComponentUuid) {
+ Random random = new Random();
+ db.executeInsert("ce_queue",
+ "UUID", uuid,
+ "TASK_TYPE", randomAlphabetic(6),
+ "COMPONENT_UUID", componentUuid,
+ "TMP_COMPONENT_UUID", tmpComponentUuid,
+ "TMP_MAIN_COMPONENT_UUID", tmpMainComponentUuid,
+ "STATUS", randomAlphabetic(7),
+ "EXECUTION_COUNT", random.nextInt(500),
+ "CREATED_AT", (long) random.nextInt(500),
+ "UPDATED_AT", (long) random.nextInt(500));
+ return uuid;
+ }
+
+ private void insertCeCharacteristic(String taskUuid, String key, String value) {
+ db.executeInsert(
+ "ce_task_characteristics",
+ "UUID", newUuid(),
+ "TASK_UUID", taskUuid,
+ "KEE", key,
+ "TEXT_VALUE", value);
+ }
+
+ private void insertProjects(String uuid, String key) {
+ db.executeInsert(
+ "PROJECTS",
+ "UUID", uuid,
+ "KEE", key,
+ "ORGANIZATION_UUID", "org_" + uuid,
+ "ROOT_UUID", uuid + "_root",
+ "UUID_PATH", uuid + "_path",
+ "PROJECT_UUID", uuid + "_project",
+ "PRIVATE", new Random().nextBoolean());
+ }
+
+ private int uuidGenerator = new Random().nextInt(9000);
+
+ private String newUuid() {
+ return "uuid_" + uuidGenerator++;
+ }
+
+ private static Map<String, String> branchCharacteristics(String branchType, String branchName) {
+ return ImmutableMap.of("branchType", branchType, "branch", branchName);
+ }
+
+ private static Map<String, String> prCharacteristics(String prName) {
+ return ImmutableMap.of("pullRequest", prName);
+ }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest.java
new file mode 100644
index 00000000000..ae4bd591e82
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest.java
@@ -0,0 +1,118 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Random;
+import javax.annotation.Nullable;
+import org.assertj.core.api.AbstractIterableAssert;
+import org.assertj.core.api.ObjectAssert;
+import org.assertj.core.groups.Tuple;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.CoreDbTester;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class PopulateTmpLastKeyColumnsToCeActivityTest {
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateTmpLastKeyColumnsToCeActivityTest.class, "ce_activity.sql");
+
+ private MapSettings settings = new MapSettings();
+ private PopulateTmpLastKeyColumnsToCeActivity underTest = new PopulateTmpLastKeyColumnsToCeActivity(db.database(), settings.asConfig());
+
+ @Test
+ public void execute_has_no_effect_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable("ce_activity")).isZero();
+ }
+
+ @Test
+ public void execute_populate_tmp_last_key_columns_from_type_and_component_uuid_columns() throws SQLException {
+ String type = randomAlphabetic(6);
+ String oldComponentUuid = randomAlphabetic(7);
+ String tmpComponentUuid = randomAlphabetic(8);
+ String tmpMainComponentUuid = randomAlphabetic(9);
+
+ String taskWithComponentUuid = insertCeActivity(type, oldComponentUuid, tmpComponentUuid, tmpMainComponentUuid);
+ String taskWithInconsistentComponentUuid = insertCeActivity(type, null, tmpComponentUuid, tmpMainComponentUuid);
+ String taskNoComponentUuid = insertCeActivity(type, null, null, null);
+
+ underTest.execute();
+
+ assertThatTmpLastKeyAndMainLastKeyOf(taskWithComponentUuid).containsOnly(tuple(type + tmpComponentUuid, type + tmpMainComponentUuid));
+ assertThatTmpLastKeyAndMainLastKeyOf(taskWithInconsistentComponentUuid).containsOnly(tuple(type, type));
+ assertThatTmpLastKeyAndMainLastKeyOf(taskNoComponentUuid).containsOnly(tuple(type, type));
+
+ assertThatTmpIsLastAndMainIsLastOf(taskWithComponentUuid).containsOnly(tuple(false, false));
+ assertThatTmpIsLastAndMainIsLastOf(taskWithInconsistentComponentUuid).containsOnly(tuple(false, false));
+ assertThatTmpIsLastAndMainIsLastOf(taskNoComponentUuid).containsOnly(tuple(false, false));
+ }
+ @Test
+ public void execute_is_reentrant() throws SQLException {
+ execute_populate_tmp_last_key_columns_from_type_and_component_uuid_columns();
+
+ underTest.execute();
+ }
+
+ private String insertCeActivity(String type,
+ @Nullable String oldComponentUuid,
+ @Nullable String tmpComponentUuid, @Nullable String tmpMainComponentUuid) {
+ checkArgument((tmpComponentUuid == null) == (tmpMainComponentUuid == null));
+
+ String uuid = UuidFactoryFast.getInstance().create();
+
+ Random random = new Random();
+ db.executeInsert(
+ "ce_activity",
+ "UUID", uuid,
+ "TASK_TYPE", type,
+ "COMPONENT_UUID", oldComponentUuid,
+ "TMP_COMPONENT_UUID", tmpComponentUuid,
+ "TMP_MAIN_COMPONENT_UUID", tmpMainComponentUuid,
+ "STATUS", randomAlphabetic(5),
+ "IS_LAST", random.nextBoolean(),
+ "IS_LAST_KEY", randomAlphabetic(12),
+ "EXECUTION_COUNT", random.nextInt(10),
+ "SUBMITTED_AT", (long) random.nextInt(5_999),
+ "CREATED_AT", (long) random.nextInt(5_999),
+ "UPDATED_AT", (long) random.nextInt(5_999));
+
+ return uuid;
+ }
+
+ private AbstractIterableAssert<?, List<? extends Tuple>, Tuple, ObjectAssert<Tuple>> assertThatTmpLastKeyAndMainLastKeyOf(String uuid) {
+ return assertThat(db.select("select tmp_is_last_key as \"LAST_KEY\", tmp_main_is_last_key as \"MAIN_LAST_KEY\" from ce_activity where uuid='" + uuid + "'"))
+ .extracting(t -> (String) t.get("LAST_KEY"), t -> (String) t.get("MAIN_LAST_KEY"));
+ }
+
+ private AbstractIterableAssert<?, List<? extends Tuple>, Tuple, ObjectAssert<Tuple>> assertThatTmpIsLastAndMainIsLastOf(String uuid) {
+ return assertThat(db.select("select tmp_is_last as \"LAST\", tmp_main_is_last as \"MAIN_LAST\" from ce_activity where uuid='" + uuid + "'"))
+ .extracting(t -> (Boolean) t.get("LAST"), t -> (Boolean) t.get("MAIN_LAST"));
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/Row.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/Row.java
new file mode 100644
index 00000000000..5ba77192079
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/Row.java
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v74;
+
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+final class Row {
+ final String taskUuid;
+ final String componentUuid;
+ final String tmpComponentUuid;
+ final String tmpMainComponentUuid;
+
+ Row(String taskUuid, @Nullable String componentUuid, @Nullable String tmpComponentUuid, @Nullable String tmpMainComponentUuid) {
+ this.taskUuid = taskUuid;
+ this.componentUuid = componentUuid;
+ this.tmpComponentUuid = tmpComponentUuid;
+ this.tmpMainComponentUuid = tmpMainComponentUuid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Row row = (Row) o;
+ return Objects.equals(taskUuid, row.taskUuid) &&
+ Objects.equals(componentUuid, row.componentUuid) &&
+ Objects.equals(tmpComponentUuid, row.tmpComponentUuid) &&
+ Objects.equals(tmpMainComponentUuid, row.tmpMainComponentUuid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(taskUuid, componentUuid, tmpComponentUuid, tmpMainComponentUuid);
+ }
+
+ @Override
+ public String toString() {
+ return "Row{" +
+ "uuid='" + taskUuid + '\'' +
+ ", componentUuid='" + componentUuid + '\'' +
+ ", tmpComponentUuid='" + tmpComponentUuid + '\'' +
+ ", tmpMainComponentUuid='" + tmpMainComponentUuid + '\'' +
+ '}';
+ }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest/ce_activity.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest/ce_activity.sql
new file mode 100644
index 00000000000..540c11a8dd5
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeActivityTest/ce_activity.sql
@@ -0,0 +1,26 @@
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "ANALYSIS_UUID" VARCHAR(50) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL,
+ "ERROR_MESSAGE" VARCHAR(1000),
+ "ERROR_STACKTRACE" CLOB,
+ "ERROR_TYPE" VARCHAR(20)
+);
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest/ce_queue.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest/ce_queue.sql
new file mode 100644
index 00000000000..b1282536589
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpColumnsToCeQueueTest/ce_queue.sql
@@ -0,0 +1,16 @@
+CREATE TABLE "CE_QUEUE" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "CE_QUEUE_UUID" ON "CE_QUEUE" ("UUID");
+CREATE INDEX "CE_QUEUE_COMPONENT_UUID" ON "CE_QUEUE" ("COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_STATUS" ON "CE_QUEUE" ("STATUS");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest/ce_activity.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest/ce_activity.sql
new file mode 100644
index 00000000000..fb0d7303aae
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddTmpLastKeyColumnsToCeActivityTest/ce_activity.sql
@@ -0,0 +1,30 @@
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
+ "ANALYSIS_UUID" VARCHAR(50) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL,
+ "ERROR_MESSAGE" VARCHAR(1000),
+ "ERROR_STACKTRACE" CLOB,
+ "ERROR_TYPE" VARCHAR(20)
+);
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_CMPT_UUID" ON "CE_ACTIVITY" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_MAIN_CMPT_UUID" ON "CE_ACTIVITY" ("TMP_MAIN_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest/ce_activity.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest/ce_activity.sql
new file mode 100644
index 00000000000..68242f666e7
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeActivityTest/ce_activity.sql
@@ -0,0 +1,87 @@
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
+ "ANALYSIS_UUID" VARCHAR(50) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL,
+ "ERROR_MESSAGE" VARCHAR(1000),
+ "ERROR_STACKTRACE" CLOB,
+ "ERROR_TYPE" VARCHAR(20)
+);
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_CMPT_UUID" ON "CE_ACTIVITY" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_MAIN_CMPT_UUID" ON "CE_ACTIVITY" ("TMP_MAIN_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
+
+CREATE TABLE "CE_TASK_CHARACTERISTICS" (
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(50) NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000),
+
+ CONSTRAINT "PK_CE_TASK_CHARACTERISTICS" PRIMARY KEY ("UUID")
+);
+CREATE INDEX "CE_TASK_CHARACTERISTICS_TASK_UUID" ON "CE_TASK_CHARACTERISTICS" ("TASK_UUID");
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(400),
+ "UUID" VARCHAR(50) NOT NULL,
+ "UUID_PATH" VARCHAR(1500) NOT NULL,
+ "ROOT_UUID" VARCHAR(50) NOT NULL,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(1500),
+ "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "PRIVATE" BOOLEAN NOT NULL,
+ "TAGS" VARCHAR(500),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_COMPONENT_UUID" VARCHAR(50),
+ "LONG_NAME" VARCHAR(2000),
+ "DEVELOPER_UUID" VARCHAR(50),
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT,
+ "B_CHANGED" BOOLEAN,
+ "B_COPY_COMPONENT_UUID" VARCHAR(50),
+ "B_DESCRIPTION" VARCHAR(2000),
+ "B_ENABLED" BOOLEAN,
+ "B_UUID_PATH" VARCHAR(1500),
+ "B_LANGUAGE" VARCHAR(20),
+ "B_LONG_NAME" VARCHAR(500),
+ "B_MODULE_UUID" VARCHAR(50),
+ "B_MODULE_UUID_PATH" VARCHAR(1500),
+ "B_NAME" VARCHAR(500),
+ "B_PATH" VARCHAR(2000),
+ "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest/ce_queue.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest/ce_queue.sql
new file mode 100644
index 00000000000..96cd2eab03c
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpColumnsToCeQueueTest/ce_queue.sql
@@ -0,0 +1,77 @@
+CREATE TABLE "CE_QUEUE" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "CE_QUEUE_UUID" ON "CE_QUEUE" ("UUID");
+CREATE INDEX "CE_QUEUE_COMPONENT_UUID" ON "CE_QUEUE" ("COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_TMP_COMPONENT_UUID" ON "CE_QUEUE" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_TMP_MAIN_CMPT_UUID" ON "CE_QUEUE" ("TMP_MAIN_COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_STATUS" ON "CE_QUEUE" ("STATUS");
+
+CREATE TABLE "CE_TASK_CHARACTERISTICS" (
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(50) NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000),
+
+ CONSTRAINT "PK_CE_TASK_CHARACTERISTICS" PRIMARY KEY ("UUID")
+);
+CREATE INDEX "CE_TASK_CHARACTERISTICS_TASK_UUID" ON "CE_TASK_CHARACTERISTICS" ("TASK_UUID");
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(400),
+ "UUID" VARCHAR(50) NOT NULL,
+ "UUID_PATH" VARCHAR(1500) NOT NULL,
+ "ROOT_UUID" VARCHAR(50) NOT NULL,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(1500),
+ "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "PRIVATE" BOOLEAN NOT NULL,
+ "TAGS" VARCHAR(500),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_COMPONENT_UUID" VARCHAR(50),
+ "LONG_NAME" VARCHAR(2000),
+ "DEVELOPER_UUID" VARCHAR(50),
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT,
+ "B_CHANGED" BOOLEAN,
+ "B_COPY_COMPONENT_UUID" VARCHAR(50),
+ "B_DESCRIPTION" VARCHAR(2000),
+ "B_ENABLED" BOOLEAN,
+ "B_UUID_PATH" VARCHAR(1500),
+ "B_LANGUAGE" VARCHAR(20),
+ "B_LONG_NAME" VARCHAR(500),
+ "B_MODULE_UUID" VARCHAR(50),
+ "B_MODULE_UUID_PATH" VARCHAR(1500),
+ "B_NAME" VARCHAR(500),
+ "B_PATH" VARCHAR(2000),
+ "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest/ce_activity.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest/ce_activity.sql
new file mode 100644
index 00000000000..b4e110b67e7
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/PopulateTmpLastKeyColumnsToCeActivityTest/ce_activity.sql
@@ -0,0 +1,38 @@
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
+ "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
+ "ANALYSIS_UUID" VARCHAR(50) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "TMP_IS_LAST" BOOLEAN,
+ "TMP_IS_LAST_KEY" VARCHAR(55),
+ "TMP_MAIN_IS_LAST" BOOLEAN,
+ "TMP_MAIN_IS_LAST_KEY" VARCHAR(55),
+ "SUBMITTER_UUID" VARCHAR(255) NULL,
+ "WORKER_UUID" VARCHAR(40) NULL,
+ "EXECUTION_COUNT" INTEGER NOT NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL,
+ "ERROR_MESSAGE" VARCHAR(1000),
+ "ERROR_STACKTRACE" CLOB,
+ "ERROR_TYPE" VARCHAR(20)
+);
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_CPNT_UUID" ON "CE_ACTIVITY" ("TMP_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_TMP_MAIN_CPNT_UUID" ON "CE_ACTIVITY" ("TMP_MAIN_COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
+CREATE INDEX "CE_ACTIVITY_T_ISLAST_KEY" ON "CE_ACTIVITY" ("TMP_IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_T_ISLAST" ON "CE_ACTIVITY" ("TMP_IS_LAST", "STATUS");
+CREATE INDEX "CE_ACTIVITY_T_MAIN_ISLAST_KEY" ON "CE_ACTIVITY" ("TMP_MAIN_IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_T_MAIN_ISLAST" ON "CE_ACTIVITY" ("TMP_MAIN_IS_LAST", "STATUS");