]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16302 DB migration to create RULE_DESC_SECTIONS and migrate data (#5817)
authorAurelien <100427063+aurelien-poscia-sonarsource@users.noreply.github.com>
Fri, 22 Apr 2022 12:27:26 +0000 (14:27 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 6 May 2022 20:02:43 +0000 (20:02 +0000)
* SONAR-16302 DB migration to create RULE_DESC_SECTIONS and migrate data

18 files changed:
server/sonar-db-dao/src/schema/schema-sq.ddl
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/AbstractDropColumn.java [deleted file]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropActionPlanKeyIssueColumn.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropIssuesAttributesIssueColumn.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v94/DropReporterIssueColumn.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSections.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/CreateRuleDescSectionsTable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumn.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSections.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSectionsTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/CreateRuleDescSectionsTableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumnTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSectionsTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/CreateIndexForRuleDescSectionsTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/DropRuleDescriptionColumnTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/InsertRuleDescriptionIntoRuleDescSectionsTest/schema.sql [new file with mode: 0644]

index 358b0fee453d1f7a986166c16b51aa7fbeb1101d..f04aa9a8c4dde6f9b5270bfb14eb69116372b1b5 100644 (file)
@@ -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 (file)
index 0000000..5bf0510
--- /dev/null
@@ -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 (file)
index be59801..0000000
+++ /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;
-  }
-
-}
index 9ba82a50b66736d7c2bebf1702423027e8f29d52..f9a4d2297ae8f29ded244b72244ac154b23da0b1 100644 (file)
@@ -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";
index 973631039e9c231d7afb279acca69ec9d1324209..58ad24373cde246946921c583d7dfbaa29e274c3 100644 (file)
@@ -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";
index 90493089189653dc218a4aedc1e8c7838e261e78..6d6263d9f998688d5445bae891ffe2acd9fd0c10 100644 (file)
@@ -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 (file)
index 0000000..9291273
--- /dev/null
@@ -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 (file)
index 0000000..233dff6
--- /dev/null
@@ -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());
+  }
+}
index 2862b35978df24514771b1d1964f4c11e25c3735..84d46409d4c672b3446cacb2ccda97e8b4d053fd 100644 (file)
@@ -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 (file)
index 0000000..a71de21
--- /dev/null
@@ -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 (file)
index 0000000..2cec6a3
--- /dev/null
@@ -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<RuleDb> selectRuleDb = findExistingRuleDescriptions(context);
+    if (selectRuleDb.isEmpty()) {
+      return;
+    }
+    insertRuleDescSections(context, selectRuleDb);
+  }
+
+  private List<RuleDb> 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<RuleDb> 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 (file)
index 0000000..7022cf4
--- /dev/null
@@ -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<String, Object> 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 (file)
index 0000000..aa4aefc
--- /dev/null
@@ -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 (file)
index 0000000..17dded6
--- /dev/null
@@ -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 (file)
index 0000000..eb717af
--- /dev/null
@@ -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<String, Object> result1 = findRuleSectionDescription(uuid1);
+    assertThat(result1)
+      .containsEntry("RULE_UUID", uuid1)
+      .containsEntry("KEE", DEFAULT_DESCRIPTION_KEY)
+      .containsEntry("DESCRIPTION", description1)
+      .extractingByKey("UUID").isNotNull();
+  }
+
+  private Map<String, Object> 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<String, Object> 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 (file)
index 0000000..b0f0270
--- /dev/null
@@ -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 (file)
index 0000000..f080e96
--- /dev/null
@@ -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 (file)
index 0000000..f7d903d
--- /dev/null
@@ -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);