]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11310 add and use target columns in CE_ACTIVITY & CE_QUEUE
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 1 Oct 2018 15:29:50 +0000 (17:29 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 10 Oct 2018 07:22:39 +0000 (09:22 +0200)
18 files changed:
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeActivityMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddLastKeyColumnsToCeActivity.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddMainComponentUuidColumnsToCeActivity.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddMainComponentUuidColumnsToCeQueue.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddMainComponentUuidColumnsToCeTable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateLastKeyColumnsToCeActivity.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeActivity.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeQueue.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeTable.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74Test.java
server/sonar-server/src/main/java/org/sonar/server/platform/ws/MigrateDataAction.java

index 81a1b3b17a08d63ef98924c29b6c78e283840a2b..b7fa9b36bc775a274118b5d503c20a68dbf99769 100644 (file)
@@ -669,6 +669,7 @@ CREATE TABLE "CE_QUEUE" (
   "UUID" VARCHAR(40) NOT NULL,
   "TASK_TYPE" VARCHAR(15) NOT NULL,
   "COMPONENT_UUID" VARCHAR(40) NULL,
+  "MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
   "TMP_COMPONENT_UUID" VARCHAR(40) NULL,
   "TMP_MAIN_COMPONENT_UUID" VARCHAR(40) NULL,
   "STATUS" VARCHAR(15) NOT NULL,
@@ -680,7 +681,8 @@ CREATE TABLE "CE_QUEUE" (
   "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_COMPONENT" ON "CE_QUEUE" ("COMPONENT_UUID");
+CREATE INDEX "CE_QUEUE_MAIN_COMPONENT" ON "CE_QUEUE" ("MAIN_COMPONENT_UUID");
 CREATE INDEX "CE_QUEUE_TMP_CPNT_UUID" ON "CE_QUEUE" ("TMP_COMPONENT_UUID");
 CREATE INDEX "CE_QUEUE_TMP_MAIN_CPNT_UUID" ON "CE_QUEUE" ("TMP_MAIN_COMPONENT_UUID");
 CREATE INDEX "CE_QUEUE_STATUS" ON "CE_QUEUE" ("STATUS");
@@ -691,12 +693,15 @@ CREATE TABLE "CE_ACTIVITY" (
   "UUID" VARCHAR(40) NOT NULL,
   "TASK_TYPE" VARCHAR(15) NOT NULL,
   "COMPONENT_UUID" VARCHAR(40) NULL,
+  "MAIN_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,
   "IS_LAST_KEY" VARCHAR(55),
+  "MAIN_IS_LAST" BOOLEAN,
+  "MAIN_IS_LAST_KEY" VARCHAR(55),
   "TMP_IS_LAST" BOOLEAN,
   "TMP_IS_LAST_KEY" VARCHAR(55),
   "TMP_MAIN_IS_LAST" BOOLEAN,
@@ -715,11 +720,14 @@ CREATE TABLE "CE_ACTIVITY" (
   "ERROR_TYPE" VARCHAR(20)
 );
 CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+CREATE INDEX "CE_ACTIVITY_COMPONENT" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+CREATE INDEX "CE_ACTIVITY_MAIN_COMPONENT" ON "CE_ACTIVITY" ("COMPONENT_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_ISLAST_KEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_ISLAST" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
+CREATE INDEX "CE_ACTIVITY_MAIN_ISLAST_KEY" ON "CE_ACTIVITY" ("MAIN_IS_LAST_KEY");
+CREATE INDEX "CE_ACTIVITY_MAIN_ISLAST" ON "CE_ACTIVITY" ("MAIN_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");
index 6791b86ee08d41fedf034619fa790def2a0fd5b5..71c6c5ea4ba7fb7bdaa2add289f5119abc56f531 100644 (file)
@@ -51,7 +51,8 @@ public class CeActivityDao implements Dao {
 
     CeActivityMapper ceActivityMapper = mapper(dbSession);
     if (isLast) {
-      ceActivityMapper.clearIsLast(dto.getIsLastKey(), dto.getMainIsLastKey(), dto.getUpdatedAt());
+      ceActivityMapper.clearIsLast(dto.getIsLastKey(), dto.getUpdatedAt());
+      ceActivityMapper.clearMainIsLast(dto.getMainIsLastKey(), dto.getUpdatedAt());
     }
     ceActivityMapper.insert(dto);
   }
index bc36fd2d9dd8602a166e3128c48efdb4e361b6a5..42b7fedab7cd578274a7cccc4e72b820d4d93880 100644 (file)
@@ -38,7 +38,9 @@ public interface CeActivityMapper {
 
   void insert(CeActivityDto dto);
 
-  void clearIsLast(@Param("isLastKey") String isLastKey, @Param("mainIsLastKey") String mainIsLastKey, @Param("updatedAt") long updatedAt);
+  void clearIsLast(@Param("isLastKey") String isLastKey, @Param("updatedAt") long updatedAt);
+
+  void clearMainIsLast(@Param("mainIsLastKey") String mainIsLastKey, @Param("updatedAt") long updatedAt);
 
   void deleteByUuids(@Param("uuids") List<String> uuids);
 }
index b5be2b8d9aa1edd35a3c7c3d3197e2003627de44..213a996e172bc1729e4e5573b5d71c43be12a3c2 100644 (file)
@@ -19,8 +19,8 @@
     ca.id,
     ca.uuid,
     ca.task_type as taskType,
-    ca.tmp_component_uuid as componentUuid,
-    ca.tmp_main_component_uuid as mainComponentUuid,
+    ca.component_uuid as componentUuid,
+    ca.main_component_uuid as mainComponentUuid,
     ca.analysis_uuid as analysisUuid,
     ca.status as status,
     ca.submitter_uuid as submitterUuid,
     ca.executed_at as executedAt,
     ca.created_at as createdAt,
     ca.updated_at as updatedAt,
-    ca.tmp_is_last as isLast,
-    ca.tmp_is_last_key as isLastKey,
-    ca.tmp_main_is_last as mainIsLast,
-    ca.tmp_main_is_last_key as mainIsLastKey,
+    ca.is_last as isLast,
+    ca.is_last_key as isLastKey,
+    ca.main_is_last as mainIsLast,
+    ca.main_is_last_key as mainIsLastKey,
     ca.execution_time_ms as executionTimeMs,
     ca.error_message as errorMessage,
     ca.error_type as errorType,
     left outer join ce_scanner_context csc on csc.task_uuid = ca.uuid
     <where>
       <if test="query.onlyCurrents">
-        and ca.tmp_main_is_last=${_true}
+        and ca.main_is_last=${_true}
       </if>
       <if test="query.mainComponentUuids != null and query.mainComponentUuids.size()>0">
-        and ca.tmp_main_component_uuid in
+        and ca.main_component_uuid in
         <foreach collection="query.mainComponentUuids" open="(" close=")" item="cUuid" separator=",">
           #{cUuid,jdbcType=VARCHAR}
         </foreach>
     from
       ce_activity
     where
-      tmp_main_is_last=${_true}
+      main_is_last=${_true}
       and status=#{status,jdbcType=VARCHAR}
       <if test="mainComponentUuid!=null">
-        and tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
+        and main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
       </if>
   </select>
 
   <insert id="insert" parameterType="org.sonar.db.ce.CeActivityDto" useGeneratedKeys="false">
     insert into ce_activity (
       uuid,
+      component_uuid,
+      main_component_uuid,
       tmp_component_uuid,
       tmp_main_component_uuid,
       analysis_uuid,
       status,
       task_type,
+      is_last,
+      is_last_key,
+      main_is_last,
+      main_is_last_key,
       tmp_is_last,
       tmp_is_last_key,
       tmp_main_is_last,
       #{uuid,jdbcType=VARCHAR},
       #{componentUuid,jdbcType=VARCHAR},
       #{mainComponentUuid,jdbcType=VARCHAR},
+      #{componentUuid,jdbcType=VARCHAR},
+      #{mainComponentUuid,jdbcType=VARCHAR},
       #{analysisUuid,jdbcType=VARCHAR},
       #{status,jdbcType=VARCHAR},
       #{taskType,jdbcType=VARCHAR},
       #{isLastKey,jdbcType=VARCHAR},
       #{mainIsLast,jdbcType=BOOLEAN},
       #{mainIsLastKey,jdbcType=VARCHAR},
+      #{isLast,jdbcType=BOOLEAN},
+      #{isLastKey,jdbcType=VARCHAR},
+      #{mainIsLast,jdbcType=BOOLEAN},
+      #{mainIsLastKey,jdbcType=VARCHAR},
       #{submitterUuid,jdbcType=VARCHAR},
       #{submittedAt,jdbcType=BIGINT},
       #{workerUuid,jdbcType=VARCHAR},
 
   <update id="clearIsLast" parameterType="map">
     update ce_activity set
+      is_last=${_false},
+      updated_at=#{updatedAt,jdbcType=BIGINT}
+    where
+      is_last=${_true}
+      and is_last_key=#{isLastKey,jdbcType=VARCHAR}
+  </update>
+
+  <update id="clearMainIsLast" parameterType="map">
+    update ce_activity set
+      main_is_last=${_false},
       tmp_is_last=${_false},
       tmp_main_is_last=${_false},
       updated_at=#{updatedAt,jdbcType=BIGINT}
     where
-      (tmp_is_last=${_true} and tmp_is_last_key=#{isLastKey,jdbcType=VARCHAR})
-      or
-      (tmp_main_is_last=${_true} and tmp_main_is_last_key=#{mainIsLastKey,jdbcType=VARCHAR})
+      main_is_last=${_true}
+      and main_is_last_key=#{mainIsLastKey,jdbcType=VARCHAR}
   </update>
 
   <delete id="deleteByUuids" parameterType="string">
index 3135f04da1655183d83892fe1ec02a1c11ed22ce..ea523ace644f6a4b509d659dafa4949c2fb6d354 100644 (file)
@@ -6,8 +6,8 @@
   <sql id="columns">
     cq.uuid,
     cq.task_type as taskType,
-    cq.tmp_component_uuid as componentUuid,
-    cq.tmp_main_component_uuid as mainComponentUuid,
+    cq.component_uuid as componentUuid,
+    cq.main_component_uuid as mainComponentUuid,
     cq.status as status,
     cq.submitter_uuid as submitterUuid,
     cq.worker_uuid as workerUuid,
     where
       status=#{status,jdbcType=VARCHAR}
       <if test="mainComponentUuid!=null">
-        and tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
+        and main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
       </if>
   </select>
 
   <select id="countByStatusAndMainComponentUuids" resultType="org.sonar.db.ce.QueueCount">
     select
-      tmp_main_component_uuid as mainComponentUuid,
+      main_component_uuid as mainComponentUuid,
       count(1) as total
     from
       ce_queue
     where
       status=#{status,jdbcType=VARCHAR}
-      and tmp_main_component_uuid in
+      and main_component_uuid in
       <foreach collection="mainComponentUuids" open="(" close=")" item="mainComponentUuid" separator=",">
         #{mainComponentUuid,jdbcType=VARCHAR}
       </foreach>
-    group by tmp_main_component_uuid
+    group by main_component_uuid
   </select>
 
   <select id="countAll" resultType="int">
@@ -77,7 +77,7 @@
     from
       ce_queue cq
     where
-      cq.tmp_main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
+      cq.main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
     <include refid="orderByDateAndId"/>
   </select>
 
       ce_queue cq
     <where>
       <if test="query.mainComponentUuids != null and query.mainComponentUuids.size()>0">
-        and cq.tmp_main_component_uuid in
+        and cq.main_component_uuid in
         <foreach collection="query.mainComponentUuids" open="(" close=")" item="mainComponentUuid" separator=",">
           #{mainComponentUuid,jdbcType=VARCHAR}
         </foreach>
         from
           ce_queue cq2
         where
-          cq.tmp_main_component_uuid=cq2.tmp_main_component_uuid
+          cq.main_component_uuid=cq2.main_component_uuid
           and cq2.status &lt;&gt; 'PENDING'
       )
   </sql>
     (
       uuid,
       task_type,
+      component_uuid,
+      main_component_uuid,
       tmp_component_uuid,
       tmp_main_component_uuid,
       status,
       #{taskType,jdbcType=VARCHAR},
       #{componentUuid,jdbcType=VARCHAR},
       #{mainComponentUuid,jdbcType=VARCHAR},
+      #{componentUuid,jdbcType=VARCHAR},
+      #{mainComponentUuid,jdbcType=VARCHAR},
       #{status,jdbcType=VARCHAR},
       #{submitterUuid,jdbcType=VARCHAR},
       0,
index 1cca92d67d31e506ba2cdb3979d84d8099eed9f2..074b447023f1ad8a27c4c80b072219f7ea4acd3f 100644 (file)
   <delete id="deleteCeScannerContextOfCeActivityByProjectUuid">
     delete from ce_scanner_context
     where
-      task_uuid in (select uuid from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
+      task_uuid in (select uuid from ce_activity where main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
   </delete>
 
   <delete id="deleteCeTaskCharacteristicsOfCeActivityByProjectUuid">
     delete from ce_task_characteristics
     where
-      task_uuid in (select uuid from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
+      task_uuid in (select uuid from ce_activity where main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
   </delete>
 
   <delete id="deleteCeTaskInputOfCeActivityByProjectUuid">
     delete from ce_task_input
     where
-      task_uuid in (select uuid from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
+      task_uuid in (select uuid from ce_activity where main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
   </delete>
 
   <delete id="deleteCeActivityByProjectUuid">
-      delete from ce_activity where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR}
+      delete from ce_activity where main_component_uuid=#{projectUuid,jdbcType=VARCHAR}
   </delete>
 
   <delete id="deleteCeScannerContextOfCeQueueByProjectUuid">
     delete from ce_scanner_context
     where
-      task_uuid in (select uuid from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
+      task_uuid in (select uuid from ce_queue where main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
   </delete>
 
   <delete id="deleteCeTaskCharacteristicsOfCeQueueByProjectUuid">
     delete from ce_task_characteristics
     where
-      task_uuid in (select uuid from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
+      task_uuid in (select uuid from ce_queue where main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
   </delete>
 
   <delete id="deleteCeTaskInputOfCeQueueByProjectUuid">
     delete from ce_task_input
     where
-      task_uuid in (select uuid from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
+      task_uuid in (select uuid from ce_queue where main_component_uuid=#{projectUuid,jdbcType=VARCHAR})
   </delete>
 
   <delete id="deleteCeQueueByProjectUuid">
-    delete from ce_queue where tmp_main_component_uuid=#{projectUuid,jdbcType=VARCHAR}
+    delete from ce_queue where main_component_uuid=#{projectUuid,jdbcType=VARCHAR}
   </delete>
 
   <delete id="deleteWebhooksByProjectUuid">
index ff8226a5235f52876c6b3fe2fc88a9532219dd5b..c38abaec26eb57bd49380313380ddb0bd03a6989 100644 (file)
@@ -22,15 +22,24 @@ package org.sonar.db.ce;
 import com.google.common.base.Function;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import org.assertj.core.api.AbstractListAssert;
+import org.assertj.core.api.ObjectAssert;
+import org.assertj.core.groups.Tuple;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.sonar.api.utils.internal.TestSystem2;
 import org.sonar.core.util.CloseableIterator;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
@@ -40,13 +49,16 @@ import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
 import static org.sonar.db.Pagination.forPage;
+import static org.sonar.db.ce.CeActivityDto.Status.CANCELED;
 import static org.sonar.db.ce.CeActivityDto.Status.FAILED;
 import static org.sonar.db.ce.CeActivityDto.Status.SUCCESS;
 import static org.sonar.db.ce.CeQueueDto.Status.PENDING;
 import static org.sonar.db.ce.CeQueueTesting.makeInProgress;
 import static org.sonar.db.ce.CeTaskTypes.REPORT;
 
+@RunWith(DataProviderRunner.class)
 public class CeActivityDaoTest {
 
   private static final String MAINCOMPONENT_1 = randomAlphabetic(12);
@@ -91,6 +103,197 @@ public class CeActivityDaoTest {
     assertThat(dto.isHasScannerContext()).isFalse();
   }
 
+  @Test
+  @UseDataProvider("notCanceledStatus")
+  public void insert_resets_is_last_and_main_is_last_fields_based_on_component_and_main_component(CeActivityDto.Status status) {
+    String project1 = randomAlphabetic(5);
+    String branch11 = randomAlphabetic(6);
+    String project2 = randomAlphabetic(8);
+    String branch21 = randomAlphabetic(9);
+    String type = randomAlphabetic(10);
+
+    String task1Project1 = insertAndCommit(newUuid(), type, project1, project1, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(true, true));
+
+    String task2Project1 = insertAndCommit(newUuid(), type, project1, project1, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project1).containsOnly(tuple(true, true));
+
+    String task1Branch11 = insertAndCommit(newUuid(), type, branch11, project1, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project1).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch11).containsOnly(tuple(true, true));
+
+    String task2Branch11 = insertAndCommit(newUuid(), type, branch11, project1, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project1).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch11).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Branch11).containsOnly(tuple(true, true));
+
+    String task1Project2 = insertAndCommit(newUuid(), type, project2, project2, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project1).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch11).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Branch11).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(task1Project2).containsOnly(tuple(true, true));
+
+    String task2Project2 = insertAndCommit(newUuid(), type, project2, project2, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project1).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch11).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Branch11).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(task1Project2).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project2).containsOnly(tuple(true, true));
+
+    String task1Branch21 = insertAndCommit(newUuid(), type, branch21, project2, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project1).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch11).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Branch11).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(task1Project2).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project2).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch21).containsOnly(tuple(true, true));
+
+    String task3project1 = insertAndCommit(newUuid(), type, project1, project1, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch11).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Branch11).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Project2).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task2Project2).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch21).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(task3project1).containsOnly(tuple(true, true));
+  }
+
+  @Test
+  @UseDataProvider("notCanceledStatus")
+  public void insert_resets_is_last_and_main_is_last_fields_based_on_type(CeActivityDto.Status status) {
+    String type1 = randomAlphabetic(10);
+    String type2 = randomAlphabetic(11);
+    String project = randomAlphabetic(5);
+    String branch = randomAlphabetic(6);
+
+    String type1Project1 = insertAndCommit(newUuid(), type1, project, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, true));
+
+    String type2Project1 = insertAndCommit(newUuid(), type2, project, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type2Project1).containsOnly(tuple(true, true));
+
+    String type2Project2 = insertAndCommit(newUuid(), type2, project, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type2Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type2Project2).containsOnly(tuple(true, true));
+
+    String type1Branch1 = insertAndCommit(newUuid(), type1, branch, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(type2Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type2Project2).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type1Branch1).containsOnly(tuple(true, true));
+
+    String type2Branch1 = insertAndCommit(newUuid(), type2, branch, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(type2Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type2Project2).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(type1Branch1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type2Branch1).containsOnly(tuple(true, true));
+
+    String type2Branch2 = insertAndCommit(newUuid(), type2, branch, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(type2Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type2Project2).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(type1Branch1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type2Branch1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type2Branch2).containsOnly(tuple(true, true));
+  }
+
+  @Test
+  @UseDataProvider("notCanceledStatus")
+  public void insert_resets_is_last_and_main_is_last_fields_based_on_component_or_not(CeActivityDto.Status status) {
+    String project = randomAlphabetic(5);
+    String type1 = randomAlphabetic(11);
+    String type2 = randomAlphabetic(11);
+
+    String type1Project1 = insertAndCommit(newUuid(), type1, project, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, true));
+
+    String type1NoProject1 = insertAndCommit(newUuid(), type1, null, null, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject1).containsOnly(tuple(true, true));
+
+    String type1NoProject2 = insertAndCommit(newUuid(), type1, null, null, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject2).containsOnly(tuple(true, true));
+
+    String type2NoProject1 = insertAndCommit(newUuid(), type2, null, null, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject2).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type2NoProject1).containsOnly(tuple(true, true));
+
+    String type2NoProject2 = insertAndCommit(newUuid(), type2, null, null, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject2).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type2NoProject1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type2NoProject2).containsOnly(tuple(true, true));
+
+    String type1Project2 = insertAndCommit(newUuid(), type1, project, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(type1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type1NoProject2).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type2NoProject1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(type2NoProject2).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(type1Project2).containsOnly(tuple(true, true));
+  }
+
+  @Test
+  @UseDataProvider("notCanceledStatus")
+  public void insert_does_not_resets_is_last_and_main_is_last_fields_if_status_is_CANCELED(CeActivityDto.Status status) {
+    String project = randomAlphabetic(5);
+    String branch = randomAlphabetic(6);
+    String type = randomAlphabetic(10);
+
+    String task1Project1 = insertAndCommit(newUuid(), type, project, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(true, true));
+
+    String task1Project2 = insertAndCommit(newUuid(), type, project, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Project2).containsOnly(tuple(true, true));
+
+    String task1Project3 = insertAndCommit(newUuid(), type, project, project, CANCELED).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Project2).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(task1Project3).containsOnly(tuple(false, false));
+
+    String task1Branch1 = insertAndCommit(newUuid(), type, branch, project, status).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Project2).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Project3).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch1).containsOnly(tuple(true, true));
+
+    String task1Branch2 = insertAndCommit(newUuid(), type, branch, project, CANCELED).getUuid();
+    assertIsLastAndMainIsLastFieldsOf(task1Project1).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Project2).containsOnly(tuple(true, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Project3).containsOnly(tuple(false, false));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch1).containsOnly(tuple(true, true));
+    assertIsLastAndMainIsLastFieldsOf(task1Branch2).containsOnly(tuple(false, false));
+  }
+
+  @DataProvider
+  public static Object[][] notCanceledStatus() {
+    return Arrays.stream(CeActivityDto.Status.values())
+      .filter(t -> t != CANCELED)
+      .map(t -> new Object[] {t})
+      .toArray(Object[][]::new);
+  }
+
+  private AbstractListAssert<?, List<? extends Tuple>, Tuple, ObjectAssert<Tuple>> assertIsLastAndMainIsLastFieldsOf(String taskUuid) {
+    return assertThat(db.select("select is_last as \"IS_LAST\", main_is_last as \"MAIN_IS_LAST\" from ce_activity where uuid='" + taskUuid + "'"))
+      .extracting(t -> (Boolean) t.get("IS_LAST"), t -> (Boolean) t.get("MAIN_IS_LAST"));
+  }
+
   @Test
   public void test_insert_of_errorMessage_of_1_000_chars() {
     CeActivityDto dto = createActivityDto("TASK_1", REPORT, COMPONENT_1, MAINCOMPONENT_1, CeActivityDto.Status.FAILED)
@@ -352,7 +555,13 @@ public class CeActivityDaoTest {
     return insert(uuid, type, mainComponentUuid, mainComponentUuid, status);
   }
 
-  private CeActivityDto insert(String uuid, String type, String componentUuid, @Nullable String mainComponentUuid, CeActivityDto.Status status) {
+  private CeActivityDto insertAndCommit(String uuid, String type, @Nullable String componentUuid, @Nullable String mainComponentUuid, CeActivityDto.Status status) {
+    CeActivityDto res = insert(uuid, type, componentUuid, mainComponentUuid, status);
+    db.commit();
+    return res;
+  }
+
+  private CeActivityDto insert(String uuid, String type, @Nullable String componentUuid, @Nullable String mainComponentUuid, CeActivityDto.Status status) {
     CeActivityDto dto = createActivityDto(uuid, type, componentUuid, mainComponentUuid, status);
     underTest.insert(db.getSession(), dto);
     return dto;
@@ -418,4 +627,8 @@ public class CeActivityDaoTest {
       return input.getUuid();
     }
   }
+
+  private static String newUuid() {
+    return UuidFactoryFast.getInstance().create();
+  }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddLastKeyColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddLastKeyColumnsToCeActivity.java
new file mode 100644 (file)
index 0000000..5152685
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.sql.DropColumnsBuilder;
+import org.sonar.server.platform.db.migration.sql.DropIndexBuilder;
+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 AddLastKeyColumnsToCeActivity extends DdlChange {
+  private static final String TABLE_NAME = "ce_activity";
+  private static final int TASK_TYPE_COLUMN_SIZE = 15;
+  private static final BooleanColumnDef COLUMN_IS_LAST = newBooleanColumnDefBuilder()
+    .setColumnName("is_last")
+    .setIsNullable(true)
+    .build();
+  private static final VarcharColumnDef COLUMN_IS_LAST_KEY = newVarcharColumnDefBuilder()
+    .setColumnName("is_last_key")
+    .setLimit(UUID_SIZE + TASK_TYPE_COLUMN_SIZE)
+    .setIsNullable(true)
+    .build();
+  private static final BooleanColumnDef COLUMN_MAIN_IS_LAST = newBooleanColumnDefBuilder()
+    .setColumnName("main_is_last")
+    .setIsNullable(true)
+    .build();
+  private static final VarcharColumnDef COLUMN_MAIN_IS_LAST_KEY = newVarcharColumnDefBuilder()
+    .setColumnName("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 AddLastKeyColumnsToCeActivity(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    // drop existing column with wrong values
+    context.execute(new DropIndexBuilder(getDialect())
+      .setTable(TABLE_NAME)
+      .setName("ce_activity_islastkey")
+      .build());
+    context.execute(new DropIndexBuilder(getDialect())
+      .setTable(TABLE_NAME)
+      .setName("ce_activity_islast_status")
+      .build());
+    context.execute(new DropColumnsBuilder(getDialect(), TABLE_NAME, COLUMN_IS_LAST.getName(), COLUMN_IS_LAST_KEY.getName())
+      .build());
+
+
+    context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME)
+      .addColumn(COLUMN_IS_LAST)
+      .addColumn(COLUMN_IS_LAST_KEY)
+      .addColumn(COLUMN_MAIN_IS_LAST)
+      .addColumn(COLUMN_MAIN_IS_LAST_KEY)
+      .build());
+
+    // create indexes
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable(TABLE_NAME)
+      .setName(TABLE_NAME + "_islast_key")
+      .addColumn(COLUMN_IS_LAST_KEY)
+      .setUnique(false)
+      .build());
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable(TABLE_NAME)
+      .setName(TABLE_NAME + "_islast")
+      .addColumn(COLUMN_IS_LAST)
+      .addColumn(COLUMN_STATUS)
+      .setUnique(false)
+      .build());
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable(TABLE_NAME)
+      .setName(TABLE_NAME + "_main_islast_key")
+      .addColumn(COLUMN_MAIN_IS_LAST_KEY)
+      .setUnique(false)
+      .build());
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable(TABLE_NAME)
+      .setName(TABLE_NAME + "_main_islast")
+      .addColumn(COLUMN_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/AddMainComponentUuidColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddMainComponentUuidColumnsToCeActivity.java
new file mode 100644 (file)
index 0000000..dc48dad
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 AddMainComponentUuidColumnsToCeActivity extends AddMainComponentUuidColumnsToCeTable {
+
+  public AddMainComponentUuidColumnsToCeActivity(Database db) {
+    super(db, "ce_activity");
+  }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddMainComponentUuidColumnsToCeQueue.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddMainComponentUuidColumnsToCeQueue.java
new file mode 100644 (file)
index 0000000..e8bedd3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 AddMainComponentUuidColumnsToCeQueue extends AddMainComponentUuidColumnsToCeTable {
+
+  public AddMainComponentUuidColumnsToCeQueue(Database db) {
+    super(db, "ce_queue");
+  }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddMainComponentUuidColumnsToCeTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddMainComponentUuidColumnsToCeTable.java
new file mode 100644 (file)
index 0000000..e2e7b5b
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.sql.DropColumnsBuilder;
+import org.sonar.server.platform.db.migration.sql.DropIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public abstract class AddMainComponentUuidColumnsToCeTable extends DdlChange {
+  private static final VarcharColumnDef COLUMN_COMPONENT_UUID = newVarcharColumnDefBuilder()
+    .setColumnName("component_uuid")
+    .setLimit(VarcharColumnDef.UUID_SIZE)
+    .setIsNullable(true)
+    .build();
+  private static final VarcharColumnDef COLUMN_MAIN_COMPONENT_UUID = newVarcharColumnDefBuilder()
+    .setColumnName("main_component_uuid")
+    .setLimit(VarcharColumnDef.UUID_SIZE)
+    .setIsNullable(true)
+    .build();
+  private final String tableName;
+
+  AddMainComponentUuidColumnsToCeTable(Database db, String tableName) {
+    super(db);
+    this.tableName = tableName;
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    // drop existing column with wrong values
+    context.execute(new DropIndexBuilder(getDialect())
+      .setTable(tableName)
+      .setName(tableName + "_component_uuid")
+      .build());
+    context.execute(new DropColumnsBuilder(getDialect(), tableName, COLUMN_COMPONENT_UUID.getName())
+      .build());
+
+    // add new columns
+    context.execute(new AddColumnsBuilder(getDialect(), tableName)
+      .addColumn(COLUMN_COMPONENT_UUID)
+      .addColumn(COLUMN_MAIN_COMPONENT_UUID)
+      .build());
+
+    // create indexes
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable(tableName)
+      .setName(tableName + "_component")
+      .addColumn(COLUMN_COMPONENT_UUID)
+      .setUnique(false)
+      .build());
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable(tableName)
+      .setName(tableName + "_main_component")
+      .addColumn(COLUMN_MAIN_COMPONENT_UUID)
+      .setUnique(false)
+      .build());
+  }
+}
index 642e3ef57f359661ccd03e68f2cbcc6d154786f9..85b37533ee7c41accc37333f0981ad38202fcb35 100644 (file)
@@ -39,6 +39,12 @@ public class DbVersion74 implements DbVersion {
       .add(2309, "Add CE_ACTIVITY.MAIN_LAST_KEY 1/6", AddTmpLastKeyColumnsToCeActivity.class)
       .add(2310, "Populate CE_ACTIVITY.MAIN_LAST_KEY 2/6", PopulateTmpLastKeyColumnsToCeActivity.class)
       .add(2311, "Populate CE_ACTIVITY.MAIN_LAST_KEY 3/6", MakeCeActivityLastKeyColumnsNullable.class)
+      .add(2312, "Add CE_QUEUE.MAIN_COMPONENT_UUID 3/5", AddMainComponentUuidColumnsToCeQueue.class)
+      .add(2313, "Add CE_ACTIVITY.MAIN_COMPONENT_UUID 3/5", AddMainComponentUuidColumnsToCeActivity.class)
+      .add(2314, "Add CE_ACTIVITY.MAIN_LAST_KEY 3/6", AddLastKeyColumnsToCeActivity.class)
+      .add(2315, "Populate CE_QUEUE.MAIN_COMPONENT_UUID 4/5", PopulateMainComponentUuidColumnsToCeQueue.class)
+      .add(2316, "Populate CE_ACTIVITY.MAIN_COMPONENT_UUID 4/5", PopulateMainComponentUuidColumnsToCeActivity.class)
+      .add(2317, "Populate CE_ACTIVITY.MAIN_LAST_KEY 4/6", PopulateLastKeyColumnsToCeActivity.class)
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateLastKeyColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateLastKeyColumnsToCeActivity.java
new file mode 100644 (file)
index 0000000..8fc66f2
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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 PopulateLastKeyColumnsToCeActivity extends DataChange {
+  private static final String TABLE_NAME = "ce_activity";
+
+  private final Configuration configuration;
+
+  public PopulateLastKeyColumnsToCeActivity(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" +
+      "  ca.uuid, ca.tmp_is_last, ca.tmp_is_last_key, ca.tmp_main_is_last, ca.tmp_main_is_last_key" +
+      " from ce_activity ca" +
+      " where" +
+      "  ca.is_last is null" +
+      "  or ca.is_last_key is null" +
+      "  or ca.main_is_last is null" +
+      "  or ca.main_is_last_key is null");
+    massUpdate.rowPluralName("rows of " + TABLE_NAME);
+    massUpdate.update("update " + TABLE_NAME + " set is_last=?, is_last_key=?, main_is_last=?, main_is_last_key=? where uuid=?");
+    massUpdate.execute(PopulateLastKeyColumnsToCeActivity::handleUpdate);
+  }
+
+  private static boolean handleUpdate(Select.Row row, SqlStatement update) throws SQLException {
+    String uuid = row.getString(1);
+    boolean isLast = row.getBoolean(2);
+    String isLastKey = row.getString(3);
+    boolean mainIsLast = row.getBoolean(2);
+    String mainIsLastKey = row.getString(3);
+
+    update.setBoolean(1, isLast);
+    update.setString(2, isLastKey);
+    update.setBoolean(3, mainIsLast);
+    update.setString(4, mainIsLastKey);
+    update.setString(5, uuid);
+
+    return true;
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeActivity.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeActivity.java
new file mode 100644 (file)
index 0000000..ae2b75f
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+@SupportsBlueGreen
+public class PopulateMainComponentUuidColumnsToCeActivity extends PopulateMainComponentUuidColumnsToCeTable {
+  private final Configuration configuration;
+
+  public PopulateMainComponentUuidColumnsToCeActivity(Database db, Configuration configuration) {
+    super(db, "ce_activity");
+    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;
+    }
+
+    super.execute(context);
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeQueue.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeQueue.java
new file mode 100644 (file)
index 0000000..2fae863
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 PopulateMainComponentUuidColumnsToCeQueue extends PopulateMainComponentUuidColumnsToCeTable {
+
+  public PopulateMainComponentUuidColumnsToCeQueue(Database db) {
+    super(db, "ce_queue");
+  }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/PopulateMainComponentUuidColumnsToCeTable.java
new file mode 100644 (file)
index 0000000..b59af21
--- /dev/null
@@ -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.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+public abstract class PopulateMainComponentUuidColumnsToCeTable extends DataChange {
+  protected final String tableName;
+
+  PopulateMainComponentUuidColumnsToCeTable(Database db, String tableName) {
+    super(db);
+    this.tableName = tableName;
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("select" +
+      "  c.uuid, c.tmp_component_uuid, c.tmp_main_component_uuid" +
+      " from " + tableName + " c" +
+      " where" +
+      "  c.tmp_component_uuid is not null" +
+      "  and (c.component_uuid is null or c.main_component_uuid is null)");
+    massUpdate.rowPluralName("tasks with component");
+    massUpdate.update("update " + tableName + " set component_uuid=?, main_component_uuid=? where uuid=?");
+    massUpdate.execute(PopulateMainComponentUuidColumnsToCeTable::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;
+  }
+}
index 7f5028ad33218249e03cfcc37d0f559c72d4d84a..5064842d84d1a8fbed238e3084c85535238205bf 100644 (file)
@@ -35,6 +35,6 @@ public class DbVersion74Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 12);
+    verifyMigrationCount(underTest, 18);
   }
 }
index 0c4dde8885f7341577db28c03748d72873119892..5a8cb42cf86006d1935af7c45c61a49d0415474c 100644 (file)
@@ -27,9 +27,9 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.db.DbClient;
-import org.sonar.server.platform.db.migration.version.v74.PopulateTmpColumnsToCeActivity;
-import org.sonar.server.platform.db.migration.version.v74.PopulateTmpColumnsToCeQueue;
-import org.sonar.server.platform.db.migration.version.v74.PopulateTmpLastKeyColumnsToCeActivity;
+import org.sonar.server.platform.db.migration.version.v74.PopulateLastKeyColumnsToCeActivity;
+import org.sonar.server.platform.db.migration.version.v74.PopulateMainComponentUuidColumnsToCeActivity;
+import org.sonar.server.platform.db.migration.version.v74.PopulateMainComponentUuidColumnsToCeQueue;
 import org.sonar.server.user.UserSession;
 
 public class MigrateDataAction implements SystemWsAction {
@@ -55,9 +55,9 @@ public class MigrateDataAction implements SystemWsAction {
     userSession.isSystemAdministrator();
 
     Configuration emptyConfiguration = new MapSettings().asConfig();
-    new PopulateTmpColumnsToCeQueue(dbClient.getDatabase()).execute();
-    new PopulateTmpColumnsToCeActivity(dbClient.getDatabase(), emptyConfiguration).execute();
-    new PopulateTmpLastKeyColumnsToCeActivity(dbClient.getDatabase(), emptyConfiguration).execute();
+    new PopulateMainComponentUuidColumnsToCeQueue(dbClient.getDatabase()).execute();
+    new PopulateMainComponentUuidColumnsToCeActivity(dbClient.getDatabase(), emptyConfiguration).execute();
+    new PopulateLastKeyColumnsToCeActivity(dbClient.getDatabase(), emptyConfiguration).execute();
     LOG.info("done");
 
     response.noContent();