From 1d098d7f3bf8bdb16edb7422a00128adfc25b423 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Fri, 7 Sep 2018 16:58:27 +0200 Subject: [PATCH] SONAR-11238 add table CE_TASK_MESSAGE --- .../ComputeEngineContainerImplTest.java | 2 +- .../java/org/sonar/db/version/SqTables.java | 1 + .../org/sonar/db/version/schema-h2.ddl | 10 ++ .../src/main/java/org/sonar/db/DaoModule.java | 2 + .../src/main/java/org/sonar/db/DbClient.java | 7 + .../src/main/java/org/sonar/db/MyBatis.java | 2 + .../org/sonar/db/ce/CeTaskMessageDao.java | 33 +++++ .../org/sonar/db/ce/CeTaskMessageDto.java | 79 +++++++++++ .../org/sonar/db/ce/CeTaskMessageMapper.java | 27 ++++ .../org/sonar/db/purge/PurgeCommands.java | 8 ++ .../java/org/sonar/db/purge/PurgeMapper.java | 4 + .../org/sonar/db/ce/CeTaskMessageMapper.xml | 22 +++ .../org/sonar/db/purge/PurgeMapper.xml | 12 ++ .../test/java/org/sonar/db/DaoModuleTest.java | 2 +- .../org/sonar/db/ce/CeTaskMessageDaoTest.java | 54 ++++++++ .../org/sonar/db/ce/CeTaskMessageDtoTest.java | 69 ++++++++++ .../java/org/sonar/db/purge/PurgeDaoTest.java | 130 ++++++++++++++++-- .../db/migration/sql/CreateTableBuilder.java | 10 ++ .../version/v74/CreateCeTaskMessage.java | 81 +++++++++++ .../db/migration/version/v74/DbVersion74.java | 1 + .../version/v74/CreateCeTaskMessageTest.java | 68 +++++++++ .../version/v74/DbVersion74Test.java | 2 +- .../v74/CreateCeTaskMessageTest/empty.sql | 0 23 files changed, 613 insertions(+), 13 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java create mode 100644 server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDtoTest.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessage.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessageTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessageTest/empty.sql diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index 369ef9a8578..325b65119c1 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -121,7 +121,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION + 27 // level 1 - + 56 // content of DaoModule + + 57 // content of DaoModule + 3 // content of EsModule + 54 // content of CorePropertyDefinitions + 1 // StopFlagContainer diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java index dfb19a480f4..085d7de5625 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java @@ -58,6 +58,7 @@ public final class SqTables { "ce_queue", "ce_task_characteristics", "ce_task_input", + "ce_task_message", "ce_scanner_context", "default_qprofiles", "deprecated_rule_keys", diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl index 9ad1de692b4..78b6523e7a5 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl @@ -748,6 +748,16 @@ CREATE TABLE "CE_SCANNER_CONTEXT" ( CONSTRAINT "PK_CE_SCANNER_CONTEXT" PRIMARY KEY ("TASK_UUID") ); +CREATE TABLE "CE_TASK_MESSAGE" ( + "UUID" VARCHAR(40) NOT NULL, + "TASK_UUID" VARCHAR(40) NOT NULL, + "MESSAGE" VARCHAR(4000) NOT NULL, + "CREATED_AT" BIGINT NOT NULL, + + CONSTRAINT "CE_TASK_MESSAGE" PRIMARY KEY ("UUID") +); +CREATE INDEX "CE_TASK_MESSAGE_TASK" ON "CE_TASK_MESSAGE" ("TASK_UUID"); + CREATE TABLE "USER_TOKENS" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java index f16b9e9e588..fbc228cdc35 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java @@ -29,6 +29,7 @@ import org.sonar.db.ce.CeQueueDao; import org.sonar.db.ce.CeScannerContextDao; import org.sonar.db.ce.CeTaskCharacteristicDao; import org.sonar.db.ce.CeTaskInputDao; +import org.sonar.db.ce.CeTaskMessageDao; import org.sonar.db.component.AnalysisPropertiesDao; import org.sonar.db.component.BranchDao; import org.sonar.db.component.ComponentDao; @@ -94,6 +95,7 @@ public class DaoModule extends Module { CeScannerContextDao.class, CeTaskCharacteristicDao.class, CeTaskInputDao.class, + CeTaskMessageDao.class, ComponentDao.class, ComponentKeyUpdaterDao.class, CustomMeasureDao.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java index a6775767e91..edc6731c31b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java @@ -27,6 +27,7 @@ import org.sonar.db.ce.CeQueueDao; import org.sonar.db.ce.CeScannerContextDao; import org.sonar.db.ce.CeTaskCharacteristicDao; import org.sonar.db.ce.CeTaskInputDao; +import org.sonar.db.ce.CeTaskMessageDao; import org.sonar.db.component.AnalysisPropertiesDao; import org.sonar.db.component.BranchDao; import org.sonar.db.component.ComponentDao; @@ -112,6 +113,7 @@ public class DbClient { private final CeTaskInputDao ceTaskInputDao; private final CeTaskCharacteristicDao ceTaskCharacteristicsDao; private final CeScannerContextDao ceScannerContextDao; + private final CeTaskMessageDao ceTaskMessageDao; private final FileSourceDao fileSourceDao; private final ProjectLinkDao projectLinkDao; private final EventDao eventDao; @@ -178,6 +180,7 @@ public class DbClient { ceTaskInputDao = getDao(map, CeTaskInputDao.class); ceTaskCharacteristicsDao = getDao(map, CeTaskCharacteristicDao.class); ceScannerContextDao = getDao(map, CeScannerContextDao.class); + ceTaskMessageDao = getDao(map, CeTaskMessageDao.class); fileSourceDao = getDao(map, FileSourceDao.class); projectLinkDao = getDao(map, ProjectLinkDao.class); eventDao = getDao(map, EventDao.class); @@ -332,6 +335,10 @@ public class DbClient { return ceScannerContextDao; } + public CeTaskMessageDao ceTaskMessageDao() { + return ceTaskMessageDao; + } + public FileSourceDao fileSourceDao() { return fileSourceDao; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java index 9743df75701..93af5cc3914 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java @@ -39,6 +39,7 @@ import org.sonar.db.ce.CeScannerContextMapper; import org.sonar.db.ce.CeTaskCharacteristicDto; import org.sonar.db.ce.CeTaskCharacteristicMapper; import org.sonar.db.ce.CeTaskInputMapper; +import org.sonar.db.ce.CeTaskMessageMapper; import org.sonar.db.component.AnalysisPropertiesMapper; import org.sonar.db.component.BranchMapper; import org.sonar.db.component.ComponentDto; @@ -211,6 +212,7 @@ public class MyBatis implements Startable { CeScannerContextMapper.class, CeTaskInputMapper.class, CeTaskCharacteristicMapper.class, + CeTaskMessageMapper.class, ComponentKeyUpdaterMapper.class, ComponentMapper.class, LiveMeasureMapper.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java new file mode 100644 index 00000000000..12af5bf80f0 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDao.java @@ -0,0 +1,33 @@ +/* + * 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.db.ce; + +import org.sonar.db.Dao; +import org.sonar.db.DbSession; + +public class CeTaskMessageDao implements Dao { + public void insert(DbSession dbSession, CeTaskMessageDto dto) { + getMapper(dbSession).insert(dto); + } + + private static CeTaskMessageMapper getMapper(DbSession dbSession) { + return dbSession.getMapper(CeTaskMessageMapper.class); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java new file mode 100644 index 00000000000..e3ab8dab0e9 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageDto.java @@ -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.db.ce; + +import static com.google.common.base.Preconditions.checkArgument; + +public class CeTaskMessageDto { + /** + * Unique identifier of each message. Not null + */ + private String uuid; + /** + * UUID of the task the message belongs to. Not null + */ + private String taskUuid; + /** + * The text of the message. Not null + */ + private String message; + /** + * Timestamp the message was created. Not null + */ + private long createdAt; + + public String getUuid() { + return uuid; + } + + public CeTaskMessageDto setUuid(String uuid) { + this.uuid = uuid; + return this; + } + + public String getTaskUuid() { + return taskUuid; + } + + public CeTaskMessageDto setTaskUuid(String taskUuid) { + this.taskUuid = taskUuid; + return this; + } + + public String getMessage() { + return message; + } + + public CeTaskMessageDto setMessage(String message) { + checkArgument(message != null && !message.isEmpty(), "message can't be null nor empty"); + checkArgument(message.length() <= 4000, "message is too long: %s", message.length()); + this.message = message; + return this; + } + + public long getCreatedAt() { + return createdAt; + } + + public CeTaskMessageDto setCreatedAt(long createdAt) { + this.createdAt = createdAt; + return this; + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java new file mode 100644 index 00000000000..ad312927f3a --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskMessageMapper.java @@ -0,0 +1,27 @@ +/* + * 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.db.ce; + +import org.apache.ibatis.annotations.Param; + +public interface CeTaskMessageMapper { + void insert(@Param("dto") CeTaskMessageDto dto); + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java index fd869e96a7d..38191d4be35 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java @@ -259,6 +259,10 @@ class PurgeCommands { purgeMapper.deleteCeTaskInputOfCeActivityByProjectUuid(rootUuid); session.commit(); profiler.stop(); + profiler.start("deleteCeActivity (ce_task_message)"); + purgeMapper.deleteCeTaskMessageOfCeActivityByProjectUuid(rootUuid); + session.commit(); + profiler.stop(); profiler.start("deleteCeActivity (ce_activity)"); purgeMapper.deleteCeActivityByProjectUuid(rootUuid); session.commit(); @@ -278,6 +282,10 @@ class PurgeCommands { purgeMapper.deleteCeTaskInputOfCeQueueByProjectUuid(rootUuid); session.commit(); profiler.stop(); + profiler.start("deleteCeQueue (ce_task_message)"); + purgeMapper.deleteCeTaskMessageOfCeQueueByProjectUuid(rootUuid); + session.commit(); + profiler.stop(); profiler.start("deleteCeQueue (ce_queue)"); purgeMapper.deleteCeQueueByProjectUuid(rootUuid); session.commit(); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java index 3a6dbd86f41..af898c1a076 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java @@ -94,6 +94,8 @@ public interface PurgeMapper { void deleteCeTaskInputOfCeActivityByProjectUuid(@Param("projectUuid") String projectUuid); + void deleteCeTaskMessageOfCeActivityByProjectUuid(@Param("projectUuid") String projectUuid); + void deleteCeScannerContextOfCeActivityByProjectUuid(@Param("projectUuid") String projectUuid); void deleteCeActivityByProjectUuid(@Param("projectUuid") String projectUuid); @@ -104,6 +106,8 @@ public interface PurgeMapper { void deleteCeTaskInputOfCeQueueByProjectUuid(@Param("projectUuid") String projectUuid); + void deleteCeTaskMessageOfCeQueueByProjectUuid(@Param("projectUuid") String projectUuid); + void deleteCeQueueByProjectUuid(@Param("projectUuid") String projectUuid); void deleteWebhooksByProjectUuid(@Param("projectUuid") String projectUuid); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml new file mode 100644 index 00000000000..ef9af42a3f3 --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeTaskMessageMapper.xml @@ -0,0 +1,22 @@ + + + + + + + insert into ce_task_message + ( + uuid, + task_uuid, + message, + created_at + ) + values ( + #{dto.uuid,jdbcType=VARCHAR}, + #{dto.taskUuid,jdbcType=VARCHAR}, + #{dto.message,jdbcType=VARCHAR}, + #{dto.createdAt,jdbcType=BIGINT} + ) + + + diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml index 074b447023f..8a63d01490f 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml @@ -330,6 +330,12 @@ task_uuid in (select uuid from ce_activity where main_component_uuid=#{projectUuid,jdbcType=VARCHAR}) + + delete from ce_task_message + where + task_uuid in (select uuid from ce_activity where main_component_uuid=#{projectUuid,jdbcType=VARCHAR}) + + delete from ce_activity where main_component_uuid=#{projectUuid,jdbcType=VARCHAR} @@ -352,6 +358,12 @@ task_uuid in (select uuid from ce_queue where main_component_uuid=#{projectUuid,jdbcType=VARCHAR}) + + delete from ce_task_message + where + task_uuid in (select uuid from ce_queue where main_component_uuid=#{projectUuid,jdbcType=VARCHAR}) + + delete from ce_queue where main_component_uuid=#{projectUuid,jdbcType=VARCHAR} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java index aa65e1e5183..307a4db0861 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java @@ -30,6 +30,6 @@ public class DaoModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new DaoModule().configure(container); - assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 56); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 57); } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java new file mode 100644 index 00000000000..f219e8873ab --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDaoTest.java @@ -0,0 +1,54 @@ +/* + * 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.db.ce; + +import org.assertj.core.groups.Tuple; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.db.DbTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CeTaskMessageDaoTest { + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private CeTaskMessageDao underTest = new CeTaskMessageDao(); + + @Test + public void insert() { + underTest.insert(dbTester.getSession(), new CeTaskMessageDto() + .setUuid("uuid_1") + .setTaskUuid("task_uuid_1") + .setMessage("message_1") + .setCreatedAt(1_222_333L)); + dbTester.getSession().commit(); + + assertThat(dbTester.select("select uuid as \"UUID\", task_uuid as \"TASK_UUID\", message as \"MESSAGE\", created_at as \"CREATED_AT\" from ce_task_message")) + .hasSize(1) + .extracting(t -> t.get("UUID"), t -> t.get("TASK_UUID"), t -> t.get("MESSAGE"), t -> t.get("CREATED_AT")) + .containsOnly(Tuple.tuple("uuid_1", "task_uuid_1", "message_1", 1_222_333L)); + + } +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDtoTest.java new file mode 100644 index 00000000000..81af200bf3c --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeTaskMessageDtoTest.java @@ -0,0 +1,69 @@ +/* + * 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.db.ce; + +import java.util.Random; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.apache.commons.lang.StringUtils.repeat; +import static org.assertj.core.api.Assertions.assertThat; + +public class CeTaskMessageDtoTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private CeTaskMessageDto underTest = new CeTaskMessageDto(); + + @Test + public void setMessage_fails_with_IAE_if_argument_is_null() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("message can't be null nor empty"); + + underTest.setMessage(null); + } + + @Test + public void setMessage_fails_with_IAE_if_argument_is_empty() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("message can't be null nor empty"); + + underTest.setMessage(""); + } + + @Test + public void setMessage_accept_argument_of_size_4000() { + String str = repeat("a", 4000); + underTest.setMessage(str); + + assertThat(underTest.getMessage()).isEqualTo(str); + } + + @Test + public void setMessage_fails_with_IAE_if_argument_has_size_bigger_then_4000() { + int size = 4000 + 1 + new Random().nextInt(100); + String str = repeat("a", size); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("message is too long: " + size); + + underTest.setMessage(str); + } +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java index 11565908fe4..f62e6260a8f 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java @@ -48,6 +48,7 @@ import org.sonar.db.ce.CeActivityDto; import org.sonar.db.ce.CeQueueDto; import org.sonar.db.ce.CeQueueDto.Status; import org.sonar.db.ce.CeTaskCharacteristicDto; +import org.sonar.db.ce.CeTaskMessageDto; import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; @@ -212,7 +213,8 @@ public class PurgeDaoTest { // back to present when(system2.now()).thenReturn(new Date().getTime()); - underTest.purge(dbSession, newConfigurationWith30Days(system2, project.uuid(), module.uuid(), dir.uuid(), srcFile.uuid(), testFile.uuid()), PurgeListener.EMPTY, new PurgeProfiler()); + underTest.purge(dbSession, newConfigurationWith30Days(system2, project.uuid(), module.uuid(), dir.uuid(), srcFile.uuid(), testFile.uuid()), PurgeListener.EMPTY, + new PurgeProfiler()); dbSession.commit(); // set purge_status=1 for non-last snapshot @@ -300,10 +302,55 @@ public class PurgeDaoTest { ComponentDto project = db.components().insertMainBranch(); ComponentDto branch = db.components().insertProjectBranch(project); - underTest.deleteBranch(dbSession, project.uuid()); + CeQueueDto projectCeQueue = insertCeQueue(project); + CeActivityDto projectCeActivity = insertCeActivity(project); + CeQueueDto branchCeQueue = insertCeQueue(branch); + CeActivityDto branchCeActivity = insertCeActivity(branch); + Stream.of(projectCeQueue.getUuid(), projectCeActivity.getUuid(), branchCeQueue.getUuid(), branchCeActivity.getUuid()) + .forEach(taskUuid -> { + insertCeScannerContext(taskUuid); + insertCeTaskCharacteristics(taskUuid, 2); + insertCeTaskMessages(taskUuid, 3); + insertCeTaskInput(taskUuid); + }); + + underTest.deleteBranch(dbSession, branch.uuid()); dbSession.commit(); - assertThat(db.countRowsOfTable("project_branches")).isEqualTo(1); - assertThat(db.countRowsOfTable("projects")).isEqualTo(1); + + assertThat(uuidsOfTable("projects")).containsOnly(project.uuid()); + assertThat(uuidsOfTable("project_branches")).containsOnly(project.uuid()); + // deleteBranch is bugged and does not delete from ce_* tables (see SONAR-10642) + assertThat(uuidsOfTable("ce_queue")) + .containsOnly(projectCeQueue.getUuid(), branchCeQueue.getUuid()) + .hasSize(2); + assertThat(uuidsOfTable("ce_activity")) + .containsOnly(projectCeActivity.getUuid(), branchCeActivity.getUuid()) + .hasSize(2); + String[] allTaskUuids = {projectCeQueue.getUuid(), projectCeActivity.getUuid(), branchCeQueue.getUuid(), branchCeActivity.getUuid()}; + assertThat(taskUuidsOfTable("ce_scanner_context")) + .containsOnly(allTaskUuids) + .hasSize(4); + assertThat(taskUuidsOfTable("ce_task_characteristics")) + .containsOnly(allTaskUuids) + .hasSize(8); + assertThat(taskUuidsOfTable("ce_task_message")) + .containsOnly(allTaskUuids) + .hasSize(12); + assertThat(taskUuidsOfTable("ce_task_input")) + .containsOnly(allTaskUuids) + .hasSize(4); + } + + private Stream taskUuidsOfTable(String tableName) { + return db.select("select task_uuid as \"TASK_UUID\" from " + tableName) + .stream() + .map(s -> (String) s.get("TASK_UUID")); + } + + private Stream uuidsOfTable(String tableName) { + return db.select("select uuid as \"UUID\" from " + tableName) + .stream() + .map(s -> (String) s.get("UUID")); } @Test @@ -313,14 +360,16 @@ public class PurgeDaoTest { dbClient.componentDao().insert(dbSession, projectToBeDeleted, anotherLivingProject); // Insert 2 rows in CE_ACTIVITY : one for the project that will be deleted, and one on another project - insertCeActivity(projectToBeDeleted); - insertCeActivity(anotherLivingProject); + CeActivityDto toBeDeletedActivity = insertCeActivity(projectToBeDeleted); + CeActivityDto notDeletedActivity = insertCeActivity(anotherLivingProject); dbSession.commit(); underTest.deleteProject(dbSession, projectToBeDeleted.uuid()); dbSession.commit(); - assertThat(db.countRowsOfTable("ce_activity")).isEqualTo(1); + assertThat(uuidsOfTable("ce_activity")) + .containsOnly(notDeletedActivity.getUuid()) + .hasSize(1); } @Test @@ -492,6 +541,56 @@ public class PurgeDaoTest { .containsOnly(toNotDelete.getUuid(), "non existing task"); } + @Test + public void delete_row_in_ce_task_message_referring_to_a_row_in_ce_queue_when_deleting_project() { + ComponentDto projectToBeDeleted = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()); + ComponentDto anotherLivingProject = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()); + dbClient.componentDao().insert(dbSession, projectToBeDeleted, anotherLivingProject); + + // Insert 2 rows in CE_ACTIVITY : one for the project that will be deleted, and one on another project + CeQueueDto toBeDeleted = insertCeQueue(projectToBeDeleted); + insertCeTaskMessages(toBeDeleted.getUuid(), 3); + CeQueueDto toNotDelete = insertCeQueue(anotherLivingProject); + insertCeTaskMessages(toNotDelete.getUuid(), 2); + insertCeTaskMessages("non existing task", 5); + dbSession.commit(); + + underTest.deleteProject(dbSession, projectToBeDeleted.uuid()); + dbSession.commit(); + + assertThat(db.select("select uuid as \"UUID\" from ce_queue")) + .extracting(row -> (String) row.get("UUID")) + .containsOnly(toNotDelete.getUuid()); + assertThat(db.select("select task_uuid as \"TASK_UUID\" from ce_task_message")) + .extracting(row -> (String) row.get("TASK_UUID")) + .containsOnly(toNotDelete.getUuid(), "non existing task"); + } + + @Test + public void delete_row_in_ce_task_message_referring_to_a_row_in_ce_activity_when_deleting_project() { + ComponentDto projectToBeDeleted = ComponentTesting.newPublicProjectDto(db.getDefaultOrganization()); + ComponentDto anotherLivingProject = ComponentTesting.newPublicProjectDto(db.getDefaultOrganization()); + dbClient.componentDao().insert(dbSession, projectToBeDeleted, anotherLivingProject); + + // Insert 2 rows in CE_ACTIVITY : one for the project that will be deleted, and one on another project + CeActivityDto toBeDeleted = insertCeActivity(projectToBeDeleted); + insertCeTaskMessages(toBeDeleted.getUuid(), 3); + CeActivityDto toNotDelete = insertCeActivity(anotherLivingProject); + insertCeTaskMessages(toNotDelete.getUuid(), 2); + insertCeTaskMessages("non existing task", 5); + dbSession.commit(); + + underTest.deleteProject(dbSession, projectToBeDeleted.uuid()); + dbSession.commit(); + + assertThat(db.select("select uuid as \"UUID\" from ce_activity")) + .extracting(row -> (String) row.get("UUID")) + .containsOnly(toNotDelete.getUuid()); + assertThat(db.select("select task_uuid as \"TASK_UUID\" from ce_task_message")) + .extracting(row -> (String) row.get("TASK_UUID")) + .containsOnly(toNotDelete.getUuid(), "non existing task"); + } + private ComponentDto insertProjectWithBranchAndRelatedData() { RuleDefinitionDto rule = db.rules().insert(); ComponentDto project = db.components().insertMainBranch(); @@ -926,12 +1025,12 @@ public class PurgeDaoTest { return dto; } - private CeQueueDto insertCeQueue(ComponentDto project) { + private CeQueueDto insertCeQueue(ComponentDto component) { CeQueueDto res = new CeQueueDto() .setUuid(UuidFactoryFast.getInstance().create()) .setTaskType("foo") - .setComponentUuid(project.uuid()) - .setMainComponentUuid(firstNonNull(project.getMainBranchProjectUuid(), project.uuid())) + .setComponentUuid(component.uuid()) + .setMainComponentUuid(firstNonNull(component.getMainBranchProjectUuid(), component.uuid())) .setStatus(Status.PENDING) .setCreatedAt(1_2323_222L) .setUpdatedAt(1_2323_222L); @@ -962,6 +1061,17 @@ public class PurgeDaoTest { dbSession.commit(); } + private void insertCeTaskMessages(String uuid, int count) { + IntStream.range(0, count) + .mapToObj(i -> new CeTaskMessageDto() + .setUuid(UuidFactoryFast.getInstance().create()) + .setTaskUuid(uuid) + .setMessage("key_" + uuid.hashCode() + i) + .setCreatedAt(2_333_444L + i)) + .forEach(dto -> dbClient.ceTaskMessageDao().insert(dbSession, dto)); + dbSession.commit(); + } + private static PurgeableAnalysisDto getById(List snapshots, String uuid) { return snapshots.stream() .filter(snapshot -> uuid.equals(snapshot.getAnalysisUuid())) diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/CreateTableBuilder.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/CreateTableBuilder.java index 83eb121b2fd..a8a56ff7788 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/CreateTableBuilder.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/CreateTableBuilder.java @@ -21,6 +21,8 @@ package org.sonar.server.platform.db.migration.sql; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import java.sql.Connection; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -30,6 +32,8 @@ import java.util.Locale; import java.util.stream.Stream; import javax.annotation.CheckForNull; import org.sonar.core.util.stream.MoreCollectors; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; import org.sonar.db.dialect.Dialect; import org.sonar.db.dialect.H2; import org.sonar.db.dialect.MsSql; @@ -63,6 +67,12 @@ public class CreateTableBuilder { this.tableName = validateTableName(tableName); } + public boolean tableExists(Database database) throws SQLException { + try (Connection connection = database.getDataSource().getConnection()) { + return DatabaseUtils.tableExists(tableName, connection); + } + } + public List build() { checkState(!columnDefs.isEmpty() || !pkColumnDefs.isEmpty(), "at least one column must be specified"); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessage.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessage.java new file mode 100644 index 00000000000..c86e65b92e2 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessage.java @@ -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.BigIntegerColumnDef; +import org.sonar.server.platform.db.migration.def.VarcharColumnDef; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.sql.CreateTableBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.MAX_SIZE; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; + +public class CreateCeTaskMessage extends DdlChange { + + public static final String TABLE_NAME = "ce_task_message"; + private static final VarcharColumnDef COLUMN_UUID = newVarcharColumnDefBuilder() + .setColumnName("uuid") + .setIsNullable(false) + .setLimit(UUID_SIZE) + .build(); + private static final VarcharColumnDef COLUMN_TASK_UUID = newVarcharColumnDefBuilder() + .setColumnName("task_uuid") + .setIsNullable(false) + .setLimit(UUID_SIZE) + .build(); + private static final VarcharColumnDef COLUMN_MESSAGE = newVarcharColumnDefBuilder() + .setColumnName("message") + .setIsNullable(false) + .setLimit(MAX_SIZE) + .build(); + private static final BigIntegerColumnDef COLUMN_CREATED_AT = newBigIntegerColumnDefBuilder() + .setColumnName("created_at") + .setIsNullable(false) + .build(); + + public CreateCeTaskMessage(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + CreateTableBuilder createTableBuilder = new CreateTableBuilder(getDialect(), TABLE_NAME); + if (!createTableBuilder.tableExists(getDatabase())) { + context.execute(createTableBuilder + .addPkColumn(COLUMN_UUID) + .addColumn(COLUMN_TASK_UUID) + .addColumn(COLUMN_MESSAGE) + .addColumn(COLUMN_CREATED_AT) + .build()); + + context.execute(new CreateIndexBuilder(getDialect()) + .setTable(TABLE_NAME) + .setName(TABLE_NAME + "_task") + .addColumn(COLUMN_TASK_UUID) + .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 0591f0bee2c..5fea0de10c9 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 @@ -42,6 +42,7 @@ public class DbVersion74 implements DbVersion { .add(2319, "Finalize CE_ACTIVITY.MAIN_COMPONENT_UUID 3/3", FinalizeMainComponentUuidColumnsToCeQueue.class) .add(2320, "Finalize CE_ACTIVITY.MAIN_LAST_KEY 3/3", FinalizeMainLastKeyColumnsToCeActivity.class) .add(2321, "Increase organization key and name length", IncreaseOrganizationsKeeAndNameLength.class) + .add(2322, "Create table CE_TASK_MESSAGE", CreateCeTaskMessage.class) ; } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessageTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessageTest.java new file mode 100644 index 00000000000..8dfee4b35e4 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessageTest.java @@ -0,0 +1,68 @@ +/* + * 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.sonar.db.CoreDbTester; + +import static java.sql.Types.BIGINT; +import static java.sql.Types.VARCHAR; +import static org.assertj.core.api.Assertions.assertThat; + +public class CreateCeTaskMessageTest { + + private static final String TABLE = "ce_task_message"; + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(CreateCeTaskMessageTest.class, "empty.sql"); + + private CreateCeTaskMessage underTest = new CreateCeTaskMessage(db.database()); + + @Test + public void creates_table_on_empty_db() throws SQLException { + underTest.execute(); + + checkTable(); + } + + @Test + public void migration_is_reentrant() throws SQLException { + underTest.execute(); + underTest.execute(); + + checkTable(); + } + + private void checkTable() { + assertThat(db.countRowsOfTable(TABLE)).isEqualTo(0); + + db.assertColumnDefinition(TABLE, "uuid", VARCHAR, 40, false); + db.assertPrimaryKey(TABLE, "pk_" + TABLE, "uuid"); + db.assertColumnDefinition(TABLE, "task_uuid", VARCHAR, 40, false); + db.assertColumnDefinition(TABLE, "message", VARCHAR, 4000, false); + db.assertColumnDefinition(TABLE, "created_at", BIGINT, null, false); + + db.assertIndex(TABLE, TABLE + "_task", "task_uuid"); + } + + +} 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 824f8b2feb4..db9845202a5 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, 15); + verifyMigrationCount(underTest, 16); } } diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessageTest/empty.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/CreateCeTaskMessageTest/empty.sql new file mode 100644 index 00000000000..e69de29bb2d -- 2.39.5