From: Aurelien <100427063+aurelien-poscia-sonarsource@users.noreply.github.com> Date: Fri, 22 Apr 2022 12:27:26 +0000 (+0200) Subject: SONAR-16302 DB migration to create RULE_DESC_SECTIONS and migrate data (#5817) X-Git-Tag: 9.5.0.56709~171 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c063d9205ddfbca5ebd6136e1506d8dc6974ff35;p=sonarqube.git SONAR-16302 DB migration to create RULE_DESC_SECTIONS and migrate data (#5817) * SONAR-16302 DB migration to create RULE_DESC_SECTIONS and migrate data --- diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index 358b0fee453..f04aa9a8c4d 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -807,6 +807,15 @@ CREATE TABLE "QUALITY_GATES"( ); ALTER TABLE "QUALITY_GATES" ADD CONSTRAINT "PK_QUALITY_GATES" PRIMARY KEY("UUID"); +CREATE TABLE "RULE_DESC_SECTIONS"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "RULE_UUID" CHARACTER VARYING(40) NOT NULL, + "KEE" CHARACTER VARYING(50) NOT NULL, + "DESCRIPTION" CHARACTER LARGE OBJECT NOT NULL +); +ALTER TABLE "RULE_DESC_SECTIONS" ADD CONSTRAINT "PK_RULE_DESC_SECTIONS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "UNIQ_RULE_DESC_SECTIONS_KEE" ON "RULE_DESC_SECTIONS"("RULE_UUID" NULLS FIRST, "KEE" NULLS FIRST); + CREATE TABLE "RULE_REPOSITORIES"( "KEE" CHARACTER VARYING(200) NOT NULL, "LANGUAGE" CHARACTER VARYING(20) NOT NULL, @@ -823,7 +832,6 @@ CREATE TABLE "RULES"( "PLUGIN_CONFIG_KEY" CHARACTER VARYING(200), "PLUGIN_NAME" CHARACTER VARYING(255) NOT NULL, "SCOPE" CHARACTER VARYING(20) NOT NULL, - "DESCRIPTION" CHARACTER LARGE OBJECT, "PRIORITY" INTEGER, "STATUS" CHARACTER VARYING(40), "LANGUAGE" CHARACTER VARYING(20), diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java new file mode 100644 index 00000000000..5bf051020f2 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.step; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder; + +public abstract class DropColumnChange extends DdlChange { + + private final String tableName; + private final String columnName; + + protected DropColumnChange(Database db, String tableName, String columnName) { + super(db); + this.tableName = tableName; + this.columnName = columnName; + } + + @Override + public void execute(DdlChange.Context context) throws SQLException { + if (!checkIfUseManagedColumnExists()) { + return; + } + + context.execute(new DropColumnsBuilder(getDatabase().getDialect(), tableName, columnName).build()); + } + + private boolean checkIfUseManagedColumnExists() throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + if (DatabaseUtils.tableColumnExists(connection, tableName, columnName)) { + return true; + } + } + return false; + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/AbstractDropColumn.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/AbstractDropColumn.java deleted file mode 100644 index be598019140..00000000000 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/AbstractDropColumn.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.v94; - -import java.sql.SQLException; -import org.sonar.db.Database; -import org.sonar.db.DatabaseUtils; -import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder; -import org.sonar.server.platform.db.migration.step.DdlChange; - -public abstract class AbstractDropColumn extends DdlChange { - - private final String tableName; - private final String columnName; - - protected AbstractDropColumn(Database db, String tableName, String columnName) { - super(db); - this.tableName = tableName; - this.columnName = columnName; - } - - @Override - public void execute(DdlChange.Context context) throws SQLException { - if (!checkIfUseManagedColumnExists()) { - return; - } - - context.execute(new DropColumnsBuilder(getDatabase().getDialect(), tableName, columnName).build()); - } - - private boolean checkIfUseManagedColumnExists() throws SQLException { - try (var connection = getDatabase().getDataSource().getConnection()) { - if (DatabaseUtils.tableColumnExists(connection, tableName, columnName)) { - return true; - } - } - return false; - } - -} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropActionPlanKeyIssueColumn.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropActionPlanKeyIssueColumn.java index 9ba82a50b66..f9a4d2297ae 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropActionPlanKeyIssueColumn.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropActionPlanKeyIssueColumn.java @@ -20,8 +20,9 @@ package org.sonar.server.platform.db.migration.version.v94; import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DropColumnChange; -public class DropActionPlanKeyIssueColumn extends AbstractDropColumn { +public class DropActionPlanKeyIssueColumn extends DropColumnChange { public static final String TABLE_NAME = "issues"; public static final String COLUMN_NAME = "action_plan_key"; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropIssuesAttributesIssueColumn.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropIssuesAttributesIssueColumn.java index 973631039e9..58ad24373cd 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropIssuesAttributesIssueColumn.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropIssuesAttributesIssueColumn.java @@ -20,8 +20,9 @@ package org.sonar.server.platform.db.migration.version.v94; import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DropColumnChange; -public class DropIssuesAttributesIssueColumn extends AbstractDropColumn{ +public class DropIssuesAttributesIssueColumn extends DropColumnChange { public static final String TABLE_NAME = "issues"; public static final String COLUMN_NAME = "issue_attributes"; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropReporterIssueColumn.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropReporterIssueColumn.java index 90493089189..6d6263d9f99 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropReporterIssueColumn.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropReporterIssueColumn.java @@ -20,8 +20,9 @@ package org.sonar.server.platform.db.migration.version.v94; import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DropColumnChange; -public class DropReporterIssueColumn extends AbstractDropColumn { +public class DropReporterIssueColumn extends DropColumnChange { public static final String TABLE_NAME = "issues"; public static final String COLUMN_NAME = "reporter"; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSections.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSections.java new file mode 100644 index 00000000000..9291273863e --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSections.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v95; + +import com.google.common.annotations.VisibleForTesting; +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.version.v95.CreateRuleDescSectionsTable.RULE_DESCRIPTION_SECTIONS_TABLE; + +public class CreateIndexForRuleDescSections extends DdlChange { + @VisibleForTesting + static final String INDEX_NAME = "uniq_rule_desc_sections_kee"; + + public CreateIndexForRuleDescSections(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + try (Connection c = getDatabase().getDataSource().getConnection()) { + if (!DatabaseUtils.indexExistsIgnoreCase(RULE_DESCRIPTION_SECTIONS_TABLE, INDEX_NAME, c)) { + context.execute(new CreateIndexBuilder() + .setTable(RULE_DESCRIPTION_SECTIONS_TABLE) + .setName(INDEX_NAME) + .addColumn("rule_uuid") + .addColumn("kee") + .setUnique(true) + .build()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/CreateRuleDescSectionsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/CreateRuleDescSectionsTable.java new file mode 100644 index 00000000000..233dff60d44 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/CreateRuleDescSectionsTable.java @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v95; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.sql.CreateTableBuilder; +import org.sonar.server.platform.db.migration.step.CreateTableChange; + +import static org.sonar.server.platform.db.migration.def.ClobColumnDef.newClobColumnDefBuilder; +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 CreateRuleDescSectionsTable extends CreateTableChange { + + static final String RULE_DESCRIPTION_SECTIONS_TABLE = "rule_desc_sections"; + + public CreateRuleDescSectionsTable(Database db) { + super(db, RULE_DESCRIPTION_SECTIONS_TABLE); + } + + @Override + public void execute(Context context, String tableName) throws SQLException { + context.execute(new CreateTableBuilder(getDialect(), tableName) + .addPkColumn(newVarcharColumnDefBuilder().setColumnName("uuid").setIsNullable(false).setLimit(UUID_SIZE).build()) + .addColumn(newVarcharColumnDefBuilder().setColumnName("rule_uuid").setIsNullable(false).setLimit(40).build()) + .addColumn(newVarcharColumnDefBuilder().setColumnName("kee").setIsNullable(false).setLimit(50).build()) + .addColumn(newClobColumnDefBuilder().setColumnName("description").setIsNullable(false).build()) + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java index 2862b35978d..84d46409d4c 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java @@ -29,6 +29,11 @@ public class DbVersion95 implements DbVersion { .add(6401, "Add column 'project_key' to 'user_tokens'", AddProjectKeyColumnToUserTokens.class) .add(6402, "Add column 'type' to 'user_tokens'", AddTypeColumnToUserTokens.class) .add(6403, "Upsert value of type in 'user_tokens'", UpsertUserTokensTypeValue.class) - .add(6404, "Make column 'type' in 'user_tokens' not nullable", MakeTypeColumnNotNullableOnUserTokens.class); + .add(6404, "Make column 'type' in 'user_tokens' not nullable", MakeTypeColumnNotNullableOnUserTokens.class) + .add(6405, "Create table RULE_DESC_SECTIONS", CreateRuleDescSectionsTable.class) + .add(6406, "Insert descriptions from RULES into RULE_DESC_SECTIONS", InsertRuleDescriptionIntoRuleDescSections.class) + .add(6407, "Create index for RULE_DESC_SECTIONS", CreateIndexForRuleDescSections.class) + .add(6408, "Drop column DESCRIPTIONS from RULES table", DropRuleDescriptionColumn.class) + ; } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumn.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumn.java new file mode 100644 index 00000000000..a71de21e782 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumn.java @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v95; + +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DropColumnChange; + +public class DropRuleDescriptionColumn extends DropColumnChange { + + public static final String TABLE_NAME = "rules"; + public static final String COLUMN_NAME = "description"; + + public DropRuleDescriptionColumn(Database db) { + super(db, TABLE_NAME, COLUMN_NAME); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSections.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSections.java new file mode 100644 index 00000000000..2cec6a3f2a7 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSections.java @@ -0,0 +1,98 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v95; + +import com.google.common.annotations.VisibleForTesting; +import java.sql.SQLException; +import java.util.List; +import org.sonar.core.util.UuidFactory; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.Upsert; + +import static org.sonar.server.platform.db.migration.version.v95.CreateRuleDescSectionsTable.RULE_DESCRIPTION_SECTIONS_TABLE; + +public class InsertRuleDescriptionIntoRuleDescSections extends DataChange { + @VisibleForTesting + static final String DEFAULT_DESCRIPTION_KEY = "default"; + + private static final String SELECT_EXISTING_RULE_DESCRIPTIONS = "select uuid, description from rules where description is not null " + + "and uuid not in (select rule_uuid from " + RULE_DESCRIPTION_SECTIONS_TABLE + ")"; + private static final String INSERT_INTO_RULE_DESC_SECTIONS = "insert into " + RULE_DESCRIPTION_SECTIONS_TABLE + " (uuid, rule_uuid, kee, description) values " + + "(?,?,?,?)"; + + private final UuidFactory uuidFactory; + + public InsertRuleDescriptionIntoRuleDescSections(Database db, UuidFactory uuidFactory) { + super(db); + this.uuidFactory = uuidFactory; + } + + @Override + protected void execute(Context context) throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + if (!DatabaseUtils.tableColumnExists(connection, "rules", "description")) { + return; + } + } + List selectRuleDb = findExistingRuleDescriptions(context); + if (selectRuleDb.isEmpty()) { + return; + } + insertRuleDescSections(context, selectRuleDb); + } + + private List findExistingRuleDescriptions(Context context) throws SQLException { + return context.prepareSelect(SELECT_EXISTING_RULE_DESCRIPTIONS) + .list(r -> new RuleDb(r.getString(1), r.getString(2))); + } + + private void insertRuleDescSections(Context context, List selectRuleDb) throws SQLException { + Upsert insertRuleDescSections = context.prepareUpsert(INSERT_INTO_RULE_DESC_SECTIONS); + for (RuleDb ruleDb : selectRuleDb) { + insertRuleDescSections + .setString(1, uuidFactory.create()) + .setString(2, ruleDb.getUuid()) + .setString(3, DEFAULT_DESCRIPTION_KEY) + .setString(4, ruleDb.getDescription()) + .addBatch(); + } + insertRuleDescSections.execute().commit(); + } + + private static class RuleDb { + private final String uuid; + private final String description; + + private RuleDb(String uuid, String description) { + this.uuid = uuid; + this.description = description; + } + + public String getUuid() { + return uuid; + } + + public String getDescription() { + return description; + } + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSectionsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSectionsTest.java new file mode 100644 index 00000000000..7022cf49b60 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSectionsTest.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v95; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.lang.RandomStringUtils; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.sonar.server.platform.db.migration.version.v95.CreateIndexForRuleDescSections.INDEX_NAME; +import static org.sonar.server.platform.db.migration.version.v95.CreateRuleDescSectionsTable.RULE_DESCRIPTION_SECTIONS_TABLE; + +public class CreateIndexForRuleDescSectionsTest { + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(CreateIndexForRuleDescSectionsTest.class, "schema.sql"); + + private final CreateIndexForRuleDescSections createIndex = new CreateIndexForRuleDescSections(db.database()); + + @Test + public void should_create_index() throws SQLException { + db.assertIndexDoesNotExist(RULE_DESCRIPTION_SECTIONS_TABLE, INDEX_NAME); + createIndex.execute(); + db.assertUniqueIndex(RULE_DESCRIPTION_SECTIONS_TABLE, INDEX_NAME, "rule_uuid", "kee"); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertIndexDoesNotExist(RULE_DESCRIPTION_SECTIONS_TABLE, INDEX_NAME); + + createIndex.execute(); + //re-entrant + createIndex.execute(); + + db.assertUniqueIndex(RULE_DESCRIPTION_SECTIONS_TABLE, INDEX_NAME, "rule_uuid", "kee"); + } + + @Test + public void index_should_prevent_two_descriptions_with_same_key_on_same_rule() throws SQLException { + createIndex.execute(); + + insertRuleDescSection("default", "rule1"); + insertRuleDescSection("default", "rule2"); + insertRuleDescSection("non_default", "rule2"); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> this.insertRuleDescSection("default", "rule1")); + } + + private void insertRuleDescSection(String key, String ruleUuid) { + Map ruleParams = new HashMap<>(); + ruleParams.put("uuid", RandomStringUtils.randomAlphanumeric(40)); + ruleParams.put("rule_uuid", ruleUuid); + ruleParams.put("kee", key); + ruleParams.put("description", "descriptions"); + + db.executeInsert("rule_desc_sections", ruleParams); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/CreateRuleDescSectionsTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/CreateRuleDescSectionsTableTest.java new file mode 100644 index 00000000000..aa4aefcad29 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/CreateRuleDescSectionsTableTest.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v95; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; + +public class CreateRuleDescSectionsTableTest { + private static final String TABLE_NAME = "rule_desc_sections"; + + @Rule + public final CoreDbTester db = CoreDbTester.createEmpty(); + + private final CreateRuleDescSectionsTable createRuleDescSectionsTable = new CreateRuleDescSectionsTable(db.database()); + + @Test + public void migration_should_create_table() throws SQLException { + db.assertTableDoesNotExist(TABLE_NAME); + + createRuleDescSectionsTable.execute(); + + db.assertTableExists(TABLE_NAME); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertTableDoesNotExist(TABLE_NAME); + + createRuleDescSectionsTable.execute(); + //re-entrant + createRuleDescSectionsTable.execute(); + + db.assertTableExists(TABLE_NAME); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumnTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumnTest.java new file mode 100644 index 00000000000..17dded6e5df --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumnTest.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v95; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class DropRuleDescriptionColumnTest { + + private static final String COLUMN_NAME = "description"; + private static final String TABLE_NAME = "rules"; + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(DropRuleDescriptionColumnTest.class, "schema.sql"); + + private final DdlChange dropRuleDescriptionColumn = new DropRuleDescriptionColumn(db.database()); + + @Test + public void migration_should_drop_action_plan_column() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.CLOB, null, true); + dropRuleDescriptionColumn.execute(); + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.CLOB, null, true); + dropRuleDescriptionColumn.execute(); + // re-entrant + dropRuleDescriptionColumn.execute(); + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSectionsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSectionsTest.java new file mode 100644 index 00000000000..eb717af0e07 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSectionsTest.java @@ -0,0 +1,122 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v95; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.lang.RandomStringUtils; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.UuidFactoryFast; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DataChange; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.sonar.server.platform.db.migration.version.v95.CreateRuleDescSectionsTable.RULE_DESCRIPTION_SECTIONS_TABLE; +import static org.sonar.server.platform.db.migration.version.v95.InsertRuleDescriptionIntoRuleDescSections.DEFAULT_DESCRIPTION_KEY; + +public class InsertRuleDescriptionIntoRuleDescSectionsTest { + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(InsertRuleDescriptionIntoRuleDescSectionsTest.class, "schema.sql"); + + private final UuidFactory uuidFactory = UuidFactoryFast.getInstance(); + + private final DataChange insertRuleDescriptions = new InsertRuleDescriptionIntoRuleDescSections(db.database(), uuidFactory); + + @Test + public void insertRuleDescriptions_doesNotFailIfRulesTableIsEmpty() { + assertThatCode(insertRuleDescriptions::execute) + .doesNotThrowAnyException(); + } + + @Test + public void insertRuleDescriptions_whenDifferentRules_createsRelevantSectionDescription() throws SQLException { + String description1 = RandomStringUtils.randomAlphanumeric(5000); + String uuid1 = "uuid1"; + insertRule(uuid1, description1); + + String description2 = RandomStringUtils.randomAlphanumeric(5000); + String uuid2 = "uuid2"; + insertRule(uuid2, description2); + + insertRuleDescriptions.execute(); + + assertThat(db.countRowsOfTable(RULE_DESCRIPTION_SECTIONS_TABLE)).isEqualTo(2); + assertRuleDescriptionCreated(uuid1, description1); + assertRuleDescriptionCreated(uuid2, description2); + } + + @Test + public void insertRuleDescriptions_whenReentrant_doesNotFail() throws SQLException { + String description1 = RandomStringUtils.randomAlphanumeric(5000); + String uuid1 = "uuid1"; + insertRule(uuid1, description1); + + insertRuleDescriptions.execute(); + insertRuleDescriptions.execute(); + insertRuleDescriptions.execute(); + + assertThat(db.countRowsOfTable(RULE_DESCRIPTION_SECTIONS_TABLE)).isEqualTo(1); + assertRuleDescriptionCreated(uuid1, description1); + } + + @Test + public void insertRuleDescriptions_whenNoDescription_doesNotCreateRuleDescriptionSection() throws SQLException { + String uuid1 = "uuid1"; + insertRule(uuid1, null); + + insertRuleDescriptions.execute(); + + assertThat(db.countRowsOfTable(RULE_DESCRIPTION_SECTIONS_TABLE)).isZero(); + } + + private void assertRuleDescriptionCreated(String uuid1, String description1) { + Map result1 = findRuleSectionDescription(uuid1); + assertThat(result1) + .containsEntry("RULE_UUID", uuid1) + .containsEntry("KEE", DEFAULT_DESCRIPTION_KEY) + .containsEntry("DESCRIPTION", description1) + .extractingByKey("UUID").isNotNull(); + } + + private Map findRuleSectionDescription(String uuid) { + return db.selectFirst("select uuid, kee, rule_uuid, description from " + + RULE_DESCRIPTION_SECTIONS_TABLE + " where rule_uuid = '" + uuid + "'"); + } + + private void insertRule(String uuid, String description) { + Map ruleParams = new HashMap<>(); + ruleParams.put("uuid", uuid); + ruleParams.put("plugin_rule_key", uuid); + ruleParams.put("plugin_name", "plugin_name"); + ruleParams.put("scope", "ALL"); + ruleParams.put("is_template", false); + ruleParams.put("is_external", true); + ruleParams.put("is_ad_hoc", false); + ruleParams.put("description", description); + + db.executeInsert("rules", ruleParams); + } + +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSectionsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSectionsTest/schema.sql new file mode 100644 index 00000000000..b0f0270437e --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSectionsTest/schema.sql @@ -0,0 +1,7 @@ +CREATE TABLE "RULE_DESC_SECTIONS"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "RULE_UUID" CHARACTER VARYING(40) NOT NULL, + "KEE" CHARACTER VARYING(50) NOT NULL, + "DESCRIPTION" CHARACTER LARGE OBJECT NOT NULL +); +ALTER TABLE "RULE_DESC_SECTIONS" ADD CONSTRAINT "PK_RULE_DESC_SECTIONS" PRIMARY KEY("UUID"); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumnTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumnTest/schema.sql new file mode 100644 index 00000000000..f080e96399b --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumnTest/schema.sql @@ -0,0 +1,29 @@ +CREATE TABLE "RULES"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "NAME" CHARACTER VARYING(200), + "PLUGIN_RULE_KEY" CHARACTER VARYING(200) NOT NULL, + "PLUGIN_KEY" CHARACTER VARYING(200), + "PLUGIN_CONFIG_KEY" CHARACTER VARYING(200), + "PLUGIN_NAME" CHARACTER VARYING(255) NOT NULL, + "SCOPE" CHARACTER VARYING(20) NOT NULL, + "DESCRIPTION" CHARACTER LARGE OBJECT, + "PRIORITY" INTEGER, + "STATUS" CHARACTER VARYING(40), + "LANGUAGE" CHARACTER VARYING(20), + "DEF_REMEDIATION_FUNCTION" CHARACTER VARYING(20), + "DEF_REMEDIATION_GAP_MULT" CHARACTER VARYING(20), + "DEF_REMEDIATION_BASE_EFFORT" CHARACTER VARYING(20), + "GAP_DESCRIPTION" CHARACTER VARYING(4000), + "SYSTEM_TAGS" CHARACTER VARYING(4000), + "IS_TEMPLATE" BOOLEAN DEFAULT FALSE NOT NULL, + "DESCRIPTION_FORMAT" CHARACTER VARYING(20), + "RULE_TYPE" TINYINT, + "SECURITY_STANDARDS" CHARACTER VARYING(4000), + "IS_AD_HOC" BOOLEAN NOT NULL, + "IS_EXTERNAL" BOOLEAN NOT NULL, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT, + "TEMPLATE_UUID" CHARACTER VARYING(40) +); +ALTER TABLE "RULES" ADD CONSTRAINT "PK_RULES" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES"("PLUGIN_RULE_KEY" NULLS FIRST, "PLUGIN_NAME" NULLS FIRST); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSectionsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSectionsTest/schema.sql new file mode 100644 index 00000000000..f7d903dcf47 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSectionsTest/schema.sql @@ -0,0 +1,38 @@ +CREATE TABLE "RULE_DESC_SECTIONS"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "RULE_UUID" CHARACTER VARYING(40) NOT NULL, + "KEE" CHARACTER VARYING(50) NOT NULL, + "DESCRIPTION" CHARACTER LARGE OBJECT NOT NULL +); +ALTER TABLE "RULE_DESC_SECTIONS" ADD CONSTRAINT "PK_RULE_DESC_SECTIONS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "UNIQ_RULE_DESC_SECTIONS_KEE" ON "RULE_DESC_SECTIONS"("RULE_UUID" NULLS FIRST, "KEE" NULLS FIRST); + +CREATE TABLE "RULES"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "NAME" CHARACTER VARYING(200), + "PLUGIN_RULE_KEY" CHARACTER VARYING(200) NOT NULL, + "PLUGIN_KEY" CHARACTER VARYING(200), + "PLUGIN_CONFIG_KEY" CHARACTER VARYING(200), + "PLUGIN_NAME" CHARACTER VARYING(255) NOT NULL, + "SCOPE" CHARACTER VARYING(20) NOT NULL, + "DESCRIPTION" CHARACTER LARGE OBJECT, + "PRIORITY" INTEGER, + "STATUS" CHARACTER VARYING(40), + "LANGUAGE" CHARACTER VARYING(20), + "DEF_REMEDIATION_FUNCTION" CHARACTER VARYING(20), + "DEF_REMEDIATION_GAP_MULT" CHARACTER VARYING(20), + "DEF_REMEDIATION_BASE_EFFORT" CHARACTER VARYING(20), + "GAP_DESCRIPTION" CHARACTER VARYING(4000), + "SYSTEM_TAGS" CHARACTER VARYING(4000), + "IS_TEMPLATE" BOOLEAN DEFAULT FALSE NOT NULL, + "DESCRIPTION_FORMAT" CHARACTER VARYING(20), + "RULE_TYPE" TINYINT, + "SECURITY_STANDARDS" CHARACTER VARYING(4000), + "IS_AD_HOC" BOOLEAN NOT NULL, + "IS_EXTERNAL" BOOLEAN NOT NULL, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT, + "TEMPLATE_UUID" CHARACTER VARYING(40) +); +ALTER TABLE "RULES" ADD CONSTRAINT "PK_RULES" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES"("PLUGIN_RULE_KEY" NULLS FIRST, "PLUGIN_NAME" NULLS FIRST);