]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6884 Update existing collation columns on MsSQL 670/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 3 Dec 2015 18:12:42 +0000 (19:12 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 3 Dec 2015 18:16:08 +0000 (19:16 +0100)
server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1002_fix_mssql_collation.rb [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
sonar-db/src/main/java/org/sonar/db/version/DdlChange.java
sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
sonar-db/src/main/java/org/sonar/db/version/v53/FixMsSqlCollation.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/version/v53/package-info.java [new file with mode: 0644]
sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
sonar-db/src/test/java/org/sonar/db/version/v53/FixMsSqlCollationTest.java [new file with mode: 0644]

diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1002_fix_mssql_collation.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1002_fix_mssql_collation.rb
new file mode 100644 (file)
index 0000000..8a98bad
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube 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.
+#
+# SonarQube 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.
+#
+
+#
+# SonarQube 5.3
+# SONAR-6884
+#
+class FixMssqlCollation < ActiveRecord::Migration
+
+  def self.up
+    execute_java_migration('org.sonar.db.version.v53.FixMsSqlCollation')
+  end
+
+end
+
+
index aebdd7143a4871d047d15c21ebe9e848134b6175..2faa9191b04d2774cf49b9e926eec2ed702ffe56 100644 (file)
@@ -29,7 +29,7 @@ import org.sonar.db.MyBatis;
 
 public class DatabaseVersion {
 
-  public static final int LAST_VERSION = 1001;
+  public static final int LAST_VERSION = 1002;
 
   /**
    * The minimum supported version which can be upgraded. Lower
index 47eb81dcfc76eaad621056bd809d4bffed85f931..6a2bfae0538cf9ff238269c5f2f4ce57829c31ed 100644 (file)
@@ -21,9 +21,13 @@ package org.sonar.db.version;
 
 import java.sql.Connection;
 import java.sql.SQLException;
+import java.util.List;
 import org.apache.commons.dbutils.DbUtils;
 import org.sonar.db.Database;
 
+import static java.lang.String.*;
+import static java.util.Arrays.asList;
+
 public abstract class DdlChange implements MigrationStep {
 
   private final Database db;
@@ -46,6 +50,12 @@ public abstract class DdlChange implements MigrationStep {
     }
   }
 
+  public abstract void execute(Context context) throws SQLException;
+
+  protected Database getDatabase() {
+    return db;
+  }
+
   public static class Context {
     private final Connection writeConnection;
 
@@ -57,14 +67,18 @@ public abstract class DdlChange implements MigrationStep {
       try {
         UpsertImpl.create(writeConnection, sql).execute().commit();
       } catch (Exception e) {
-        throw new IllegalStateException(String.format("Fail to execute %s", sql), e);
+        throw new IllegalStateException(format("Fail to execute %s", sql), e);
       }
     }
-  }
 
-  public abstract void execute(Context context) throws SQLException;
+    public void execute(String... sqls) throws SQLException {
+      execute(asList(sqls));
+    }
 
-  protected Database getDatabase() {
-    return db;
+    public void execute(List<String> sqls) throws SQLException {
+      for (String sql : sqls) {
+        execute(sql);
+      }
+    }
   }
 }
index 0a28311f2a327b38e341817bfb906c3c6e0c7663..5f36fdbfc05f0b29f71bb755b4f9e9dbad9d11b5 100644 (file)
@@ -59,6 +59,7 @@ import org.sonar.db.version.v52.RemoveComponentLibraries;
 import org.sonar.db.version.v52.RemoveDuplicatedComponentKeys;
 import org.sonar.db.version.v52.RemoveRuleMeasuresOnIssues;
 import org.sonar.db.version.v52.RemoveSnapshotLibraries;
+import org.sonar.db.version.v53.FixMsSqlCollation;
 
 public class MigrationStepModule extends Module {
   @Override
@@ -109,7 +110,9 @@ public class MigrationStepModule extends Module {
       RemoveDuplicatedComponentKeys.class,
       IncreasePrecisionOfNumerics.class,
       RemoveAnalysisReportsFromActivities.class,
-      RemoveRuleMeasuresOnIssues.class
-    );
+      RemoveRuleMeasuresOnIssues.class,
+
+      // 5.3
+      FixMsSqlCollation.class);
   }
 }
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v53/FixMsSqlCollation.java b/sonar-db/src/main/java/org/sonar/db/version/v53/FixMsSqlCollation.java
new file mode 100644 (file)
index 0000000..fa40e20
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.version.v53;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.Database;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.version.AlterColumnsTypeBuilder;
+import org.sonar.db.version.DdlChange;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.db.version.ClobColumnDef.newClobColumnDefBuilder;
+import static org.sonar.db.version.ColumnDefValidation.validateColumnName;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+/**
+ * Fix the collation of all columns on MsSQL
+ */
+public class FixMsSqlCollation extends DdlChange {
+
+  private static final Logger LOGGER = Loggers.get(FixMsSqlCollation.class);
+
+  private final Database db;
+
+  public FixMsSqlCollation(Database db) {
+    super(db);
+    this.db = db;
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    if (!getDatabase().getDialect().getId().equals(MsSql.ID)) {
+      return;
+    }
+
+    // characteristics
+    new UpdateTableCollation(context, db, "characteristics")
+      .addVarcharColumn("kee", 100)
+      .addVarcharColumn("name", 100)
+      .addVarcharColumn("function_key", 100)
+      .addVarcharColumn("factor_unit", 100)
+      .addVarcharColumn("offset_unit", 100)
+      .execute();
+
+    // rules_parameters
+    new UpdateTableCollation(context, db, "rules_parameters")
+      .addNotNullableVarcharColumn("name", 128)
+      .addNotNullableVarcharColumn("param_type", 512)
+      .addVarcharColumn("default_value", 4000)
+      .addVarcharColumn("description", 4000)
+      .execute();
+
+    // rules_profiles
+    new UpdateTableCollation(context, db, "rules_profiles")
+      .addUniqueIndex("uniq_qprof_key", "kee")
+      .addNotNullableVarcharColumn("name", 100)
+      .addVarcharColumn("language", 20)
+      .addNotNullableVarcharColumn("kee", 255)
+      .addVarcharColumn("parent_kee", 255)
+      .addVarcharColumn("rules_updated_at", 100)
+      .execute();
+
+    // project_qprofiles
+    new UpdateTableCollation(context, db, "project_qprofiles")
+      .addUniqueIndex("uniq_project_qprofiles", "project_uuid", "profile_key")
+      .addNotNullableVarcharColumn("project_uuid", 50)
+      .addNotNullableVarcharColumn("profile_key", 255)
+      .execute();
+
+    // widgets
+    new UpdateTableCollation(context, db, "widgets")
+      .addIndex("widgets_widgetkey", "widget_key")
+      .addNotNullableVarcharColumn("widget_key", 256)
+      .addVarcharColumn("name", 256)
+      .addVarcharColumn("description", 1000)
+      .execute();
+
+    // groups
+    new UpdateTableCollation(context, db, "groups")
+      .addVarcharColumn("name", 500)
+      .addVarcharColumn("description", 200)
+      .execute();
+
+    // snapshots
+    new UpdateTableCollation(context, db, "snapshots")
+      .addIndex("snapshots_qualifier", "qualifier")
+      .addVarcharColumn("scope", 3)
+      .addVarcharColumn("qualifier", 10)
+      .addVarcharColumn("version", 500)
+      .addVarcharColumn("path", 500)
+      .execute();
+
+    // schema_migrations
+    new UpdateTableCollation(context, db, "schema_migrations")
+      .addIndex("unique_schema_migrations", "version")
+      .addNotNullableVarcharColumn("version", 256)
+      .execute();
+
+    // group_roles
+    new UpdateTableCollation(context, db, "group_roles")
+      .addUniqueIndex("uniq_group_roles", "group_id", "resource_id", "role")
+      .addIndex("group_roles_role", "role")
+      .addNotNullableVarcharColumn("role", 64)
+      .execute();
+
+    // Rules
+    new UpdateTableCollation(context, db, "rules")
+      .addUniqueIndex("rules_repo_key", "plugin_name", "plugin_rule_key")
+      .addNotNullableVarcharColumn("plugin_rule_key", 200)
+      .addNotNullableVarcharColumn("plugin_name", 255)
+      .addClobColumn("description")
+      .addVarcharColumn("description_format", 20)
+      .addVarcharColumn("plugin_config_key", 500)
+      .addVarcharColumn("name", 200)
+      .addVarcharColumn("status", 40)
+      .addVarcharColumn("language", 20)
+      .addVarcharColumn("note_user_login", 255)
+      .addVarcharColumn("remediation_function", 20)
+      .addVarcharColumn("default_remediation_function", 20)
+      .addVarcharColumn("remediation_coeff", 20)
+      .addVarcharColumn("default_remediation_coeff", 20)
+      .addVarcharColumn("remediation_offset", 20)
+      .addVarcharColumn("default_remediation_offset", 20)
+      .addVarcharColumn("effort_to_fix_description", 4000)
+      .addVarcharColumn("tags", 4000)
+      .addVarcharColumn("system_tags", 4000)
+      .execute();
+
+    // widget_properties
+    new UpdateTableCollation(context, db, "widget_properties")
+      .addVarcharColumn("kee", 100)
+      .addVarcharColumn("text_value", 4000)
+      .execute();
+
+    // events
+    new UpdateTableCollation(context, db, "events")
+      .addIndex("events_component_uuid", "component_uuid")
+      .addVarcharColumn("name", 400)
+      .addVarcharColumn("component_uuid", 50)
+      .addVarcharColumn("category", 50)
+      .addVarcharColumn("description", 4000)
+      .addVarcharColumn("event_data", 4000)
+      .execute();
+
+    // quality_gates
+    new UpdateTableCollation(context, db, "quality_gates")
+      .addUniqueIndex("uniq_quality_gates", "name")
+      .addVarcharColumn("name", 100)
+      .execute();
+
+    // quality_gate_conditions
+    new UpdateTableCollation(context, db, "quality_gate_conditions")
+      .addVarcharColumn("operator", 3)
+      .addVarcharColumn("value_error", 64)
+      .addVarcharColumn("value_warning", 64)
+      .execute();
+
+    // properties
+    new UpdateTableCollation(context, db, "properties")
+      .addIndex("properties_key", "prop_key")
+      .addVarcharColumn("prop_key", 512)
+      .execute();
+
+    // project_links
+    new UpdateTableCollation(context, db, "project_links")
+      .addVarcharColumn("component_uuid", 50)
+      .addVarcharColumn("link_type", 20)
+      .addVarcharColumn("name", 128)
+      .addNotNullableVarcharColumn("href", 2048)
+      .execute();
+
+    // duplications_index
+    new UpdateTableCollation(context, db, "duplications_index")
+      .addIndex("duplications_index_hash", "hash")
+      .addNotNullableVarcharColumn("hash", 50)
+      .execute();
+
+    // project_measures
+    new UpdateTableCollation(context, db, "project_measures")
+      .addVarcharColumn("text_value", 4000)
+      .addVarcharColumn("alert_status", 5)
+      .addVarcharColumn("alert_text", 4000)
+      .addVarcharColumn("url", 2000)
+      .addVarcharColumn("description", 4000)
+      .execute();
+
+    // projects
+    new UpdateTableCollation(context, db, "projects")
+      .addUniqueIndex("projects_kee", "kee")
+      .addUniqueIndex("projects_uuid", "uuid")
+      .addIndex("projects_project_uuid", "project_uuid")
+      .addIndex("projects_module_uuid", "module_uuid")
+      .addIndex("projects_qualifier", "qualifier")
+      .addVarcharColumn("kee", 400)
+      .addVarcharColumn("uuid", 50)
+      .addVarcharColumn("project_uuid", 50)
+      .addVarcharColumn("module_uuid", 50)
+      .addVarcharColumn("module_uuid_path", 4000)
+      .addVarcharColumn("name", 256)
+      .addVarcharColumn("description", 2000)
+      .addVarcharColumn("scope", 3)
+      .addVarcharColumn("qualifier", 10)
+      .addVarcharColumn("deprecated_kee", 400)
+      .addVarcharColumn("path", 2000)
+      .addVarcharColumn("language", 20)
+      .addVarcharColumn("long_name", 256)
+      .execute();
+
+    // manual_measures
+    new UpdateTableCollation(context, db, "manual_measures")
+      .addIndex("manual_measures_component_uuid", "component_uuid")
+      .addVarcharColumn("component_uuid", 50)
+      .addVarcharColumn("text_value", 4000)
+      .addVarcharColumn("user_login", 255)
+      .addVarcharColumn("description", 4000)
+      .execute();
+
+    // active_rules
+    new UpdateTableCollation(context, db, "active_rules")
+      .addVarcharColumn("inheritance", 10)
+      .execute();
+
+    // user_roles
+    new UpdateTableCollation(context, db, "user_roles")
+      .addNotNullableVarcharColumn("role", 64)
+      .execute();
+
+    // active_rule_parameters
+    new UpdateTableCollation(context, db, "active_rule_parameters")
+      .addVarcharColumn("rules_parameter_key", 128)
+      .addVarcharColumn("value", 4000)
+      .execute();
+
+    // users
+    new UpdateTableCollation(context, db, "users")
+      .addUniqueIndex("users_login", "login")
+      .addVarcharColumn("login", 255)
+      .addVarcharColumn("name", 200)
+      .addVarcharColumn("email", 100)
+      .addVarcharColumn("crypted_password", 40)
+      .addVarcharColumn("salt", 40)
+      .addVarcharColumn("remember_token", 500)
+      .addVarcharColumn("scm_accounts", 4000)
+      .execute();
+
+    // dashboards
+    new UpdateTableCollation(context, db, "dashboards")
+      .addVarcharColumn("name", 256)
+      .addVarcharColumn("description", 1000)
+      .addVarcharColumn("column_layout", 20)
+      .execute();
+
+    // metrics
+    new UpdateTableCollation(context, db, "metrics")
+      .addUniqueIndex("metrics_unique_name", "name")
+      .addNotNullableVarcharColumn("name", 64)
+      .addVarcharColumn("description", 255)
+      .addVarcharColumn("domain", 64)
+      .addVarcharColumn("short_name", 64)
+      .addVarcharColumn("val_type", 8)
+      .execute();
+
+    // loaded_templates
+    new UpdateTableCollation(context, db, "loaded_templates")
+      .addVarcharColumn("kee", 200)
+      .addVarcharColumn("template_type", 15)
+      .execute();
+
+    // resource_index
+    new UpdateTableCollation(context, db, "resource_index")
+      .addIndex("resource_index_key", "kee")
+      .addNotNullableVarcharColumn("kee", 400)
+      .addNotNullableVarcharColumn("qualifier", 10)
+      .execute();
+
+    // action_plans
+    new UpdateTableCollation(context, db, "action_plans")
+      .addVarcharColumn("kee", 100)
+      .addVarcharColumn("name", 200)
+      .addVarcharColumn("description", 1000)
+      .addVarcharColumn("user_login", 255)
+      .addVarcharColumn("status", 10)
+      .execute();
+
+    // authors
+    new UpdateTableCollation(context, db, "authors")
+      .addUniqueIndex("uniq_author_logins", "login")
+      .addVarcharColumn("login", 100)
+      .execute();
+
+    // measure_filters
+    new UpdateTableCollation(context, db, "measure_filters")
+      .addIndex("measure_filters_name", "name")
+      .addNotNullableVarcharColumn("name", 100)
+      .addVarcharColumn("description", 4000)
+      .execute();
+
+    // issues
+    new UpdateTableCollation(context, db, "issues")
+      .addUniqueIndex("issues_kee", "kee")
+      .addIndex("issues_component_uuid", "component_uuid")
+      .addIndex("issues_project_uuid", "project_uuid")
+      .addIndex("issues_severity", "severity")
+      .addIndex("issues_status", "status")
+      .addIndex("issues_resolution", "resolution")
+      .addIndex("issues_assignee", "assignee")
+      .addIndex("issues_action_plan_key", "action_plan_key")
+      .addNotNullableVarcharColumn("kee", 50)
+      .addVarcharColumn("component_uuid", 50)
+      .addVarcharColumn("project_uuid", 50)
+      .addVarcharColumn("severity", 10)
+      .addVarcharColumn("message", 4000)
+      .addVarcharColumn("status", 20)
+      .addVarcharColumn("resolution", 20)
+      .addVarcharColumn("checksum", 1000)
+      .addVarcharColumn("reporter", 255)
+      .addVarcharColumn("assignee", 255)
+      .addVarcharColumn("author_login", 255)
+      .addVarcharColumn("action_plan_key", 50)
+      .addVarcharColumn("issue_attributes", 4000)
+      .addVarcharColumn("tags", 4000)
+      .execute();
+
+    // issue_changes
+    new UpdateTableCollation(context, db, "issue_changes")
+      .addIndex("issue_changes_kee", "kee")
+      .addIndex("issue_changes_issue_key", "issue_key")
+      .addVarcharColumn("kee", 50)
+      .addNotNullableVarcharColumn("issue_key", 50)
+      .addVarcharColumn("user_login", 255)
+      .addVarcharColumn("change_type", 40)
+      .addClobColumn("change_data")
+      .execute();
+
+    // issue_filters
+    new UpdateTableCollation(context, db, "issue_filters")
+      .addIndex("issue_filters_name", "name")
+      .addNotNullableVarcharColumn("name", 100)
+      .addVarcharColumn("user_login", 255)
+      .addVarcharColumn("description", 4000)
+      .execute();
+
+    // issue_filter_favourites
+    new UpdateTableCollation(context, db, "issue_filter_favourites")
+      .addIndex("issue_filter_favs_user", "user_login")
+      .addNotNullableVarcharColumn("user_login", 255)
+      .execute();
+
+    // permission_templates
+    new UpdateTableCollation(context, db, "permission_templates")
+      .addNotNullableVarcharColumn("name", 100)
+      .addNotNullableVarcharColumn("kee", 100)
+      .addVarcharColumn("description", 4000)
+      .addVarcharColumn("key_pattern", 500)
+      .execute();
+
+    // perm_templates_users
+    new UpdateTableCollation(context, db, "perm_templates_users")
+      .addNotNullableVarcharColumn("permission_reference", 64)
+      .execute();
+
+    // perm_templates_groups
+    new UpdateTableCollation(context, db, "perm_templates_groups")
+      .addNotNullableVarcharColumn("permission_reference", 64)
+      .execute();
+
+    // activities
+    new UpdateTableCollation(context, db, "activities")
+      .addUniqueIndex("activities_log_key", "log_key")
+      .addVarcharColumn("log_key", 250)
+      .addVarcharColumn("user_login", 30)
+      .addVarcharColumn("log_type", 250)
+      .addVarcharColumn("log_action", 250)
+      .addVarcharColumn("log_message", 250)
+      .execute();
+
+    // file_sources
+    new UpdateTableCollation(context, db, "file_sources")
+      .addIndex("file_sources_project_uuid", "project_uuid")
+      .addUniqueIndex("file_sources_uuid_type", "file_uuid", "data_type")
+      .addNotNullableVarcharColumn("project_uuid", 50)
+      .addNotNullableVarcharColumn("file_uuid", 50)
+      .addVarcharColumn("data_type", 20)
+      .addVarcharColumn("data_hash", 50)
+      .addVarcharColumn("src_hash", 50)
+      .addVarcharColumn("revision", 100)
+      .execute();
+
+    // ce_queue
+    new UpdateTableCollation(context, db, "ce_queue")
+      .addUniqueIndex("ce_queue_uuid", "uuid")
+      .addNotNullableVarcharColumn("uuid", 40)
+      .addNotNullableVarcharColumn("task_type", 15)
+      .addVarcharColumn("component_uuid", 40)
+      .addNotNullableVarcharColumn("status", 15)
+      .addVarcharColumn("submitter_login", 255)
+      .execute();
+
+    // ce_activity
+    new UpdateTableCollation(context, db, "ce_activity")
+      .addUniqueIndex("ce_activity_uuid", "uuid")
+      .addIndex("ce_activity_component_uuid", "component_uuid")
+      .addNotNullableVarcharColumn("uuid", 40)
+      .addNotNullableVarcharColumn("task_type", 15)
+      .addVarcharColumn("component_uuid", 40)
+      .addNotNullableVarcharColumn("status", 15)
+      .addNotNullableVarcharColumn("is_last_key", 55)
+      .addVarcharColumn("submitter_login", 255)
+      .execute();
+  }
+
+  @VisibleForTesting
+  static class UpdateTableCollation {
+    private final String table;
+    private final List<Index> indexes = new ArrayList<>();
+    private final AlterColumnsTypeBuilder alterColumnsBuilder;
+    private final Context context;
+
+    protected UpdateTableCollation(Context context, Database db, String table) {
+      this.context = context;
+      this.table = requireNonNull(table);
+      this.alterColumnsBuilder = new AlterColumnsTypeBuilder(db.getDialect(), table);
+      LOGGER.info("Updating columns from table {}", table);
+    }
+
+    public UpdateTableCollation addVarcharColumn(String name, int size) {
+      addVarcharColumn(name, size, true);
+      return this;
+    }
+
+    public UpdateTableCollation addNotNullableVarcharColumn(String name, int size) {
+      addVarcharColumn(name, size, false);
+      return this;
+    }
+
+    private UpdateTableCollation addVarcharColumn(String name, int size, boolean isNullable) {
+      alterColumnsBuilder.updateColumn(newVarcharColumnDefBuilder().setColumnName(name).setLimit(size).setIsNullable(isNullable).build());
+      return this;
+    }
+
+    public UpdateTableCollation addClobColumn(String name) {
+      alterColumnsBuilder.updateColumn(newClobColumnDefBuilder().setColumnName(name).build());
+      return this;
+    }
+
+    public UpdateTableCollation addIndex(String indexName, String... columns) {
+      addIndex(indexName, false, columns);
+      return this;
+    }
+
+    public UpdateTableCollation addUniqueIndex(String indexName, String... columns) {
+      addIndex(indexName, true, columns);
+      return this;
+    }
+
+    private UpdateTableCollation addIndex(String indexName, boolean unique, String... columns) {
+      indexes.add(new Index(indexName, unique, columns));
+      return this;
+    }
+
+    public void execute() throws SQLException {
+      removeIndexes();
+      updateCollation();
+      addIndexes();
+    }
+
+    private void updateCollation() throws SQLException {
+      context.execute(alterColumnsBuilder.build());
+    }
+
+    private void removeIndexes() {
+      for (Index index : indexes) {
+        try {
+          context.execute(dropIndex(index));
+        } catch (SQLException e) {
+          LOGGER.warn("Could not remove index '{}' on table '{}'. It probably doesn't exist, it will be ignored", index, table, e);
+        }
+      }
+    }
+
+    private void addIndexes() throws SQLException {
+      for (Index index : indexes) {
+        context.execute(createIndex(index));
+      }
+    }
+
+    private String dropIndex(Index index) {
+      return "DROP INDEX " + index.indexName + " ON " + table;
+    }
+
+    private String createIndex(Index index) {
+      StringBuilder sql = new StringBuilder().append("CREATE ");
+      if (index.unique) {
+        sql.append("UNIQUE ");
+      }
+      sql.append("INDEX ").append(index.indexName).append(" ON ").append(table).append("(");
+      for (int columnIndex = 0; columnIndex < index.columnNames.length; columnIndex++) {
+        sql.append(index.columnNames[columnIndex]);
+        if (columnIndex < index.columnNames.length - 1) {
+          sql.append(",");
+        }
+      }
+      sql.append(")");
+
+      return sql.toString();
+    }
+
+    private static class Index {
+      private final String indexName;
+      private final boolean unique;
+      private final String[] columnNames;
+
+      public Index(String indexName, boolean unique, String[] columnNames) {
+        this.indexName = validateColumnName(requireNonNull(indexName));
+        this.unique = unique;
+        this.columnNames = validateColumns(columnNames);
+      }
+
+      private static String[] validateColumns(String[] columnNames) {
+        checkState(columnNames.length > 0, "At least one column must be added");
+        for (String column : columnNames) {
+          validateColumnName(column);
+        }
+        return columnNames;
+      }
+    }
+  }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v53/package-info.java b/sonar-db/src/main/java/org/sonar/db/version/v53/package-info.java
new file mode 100644 (file)
index 0000000..260005e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.db.version.v53;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
index 9a726809cea3a6c110cb159040c50800002a5ea5..4f97c05b4d5bf604f23778be658e429aff6ae5d0 100644 (file)
@@ -362,6 +362,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('940');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('941');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1000');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1001');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1002');
 
 INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null);
 ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
index e5192fc510a19a0c3af4f974ce3bd0bddfc588c3..c9a71b7dec647476781f6dee9e4b476e0b3d5d97 100644 (file)
@@ -29,6 +29,6 @@ public class MigrationStepModuleTest {
   public void verify_count_of_added_MigrationStep_types() {
     ComponentContainer container = new ComponentContainer();
     new MigrationStepModule().configure(container);
-    assertThat(container.size()).isEqualTo(41);
+    assertThat(container.size()).isEqualTo(42);
   }
 }
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v53/FixMsSqlCollationTest.java b/sonar-db/src/test/java/org/sonar/db/version/v53/FixMsSqlCollationTest.java
new file mode 100644 (file)
index 0000000..e187c67
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.version.v53;
+
+import org.junit.Test;
+import org.sonar.db.Database;
+import org.sonar.db.dialect.H2;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.MySql;
+import org.sonar.db.dialect.Oracle;
+
+import static java.util.Collections.singletonList;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.version.DdlChange.Context;
+
+public class FixMsSqlCollationTest {
+
+  Database db = mock(Database.class);
+  Context context = mock(Context.class);
+
+  FixMsSqlCollation underTest = new FixMsSqlCollation(db);
+
+  @Test
+  public void execute_sql_on_mssql() throws Exception {
+    when(db.getDialect()).thenReturn(new MsSql());
+
+    underTest.execute(context);
+
+    verify(context, times(42)).execute(anyListOf(String.class));
+  }
+
+  @Test
+  public void nothing_to_do_on_mysql() throws Exception {
+    when(db.getDialect()).thenReturn(new MySql());
+
+    underTest.execute(context);
+
+    verifyZeroInteractions(context);
+  }
+
+  @Test
+  public void nothing_to_do_on_h2() throws Exception {
+    when(db.getDialect()).thenReturn(new H2());
+
+    underTest.execute(context);
+
+    verifyZeroInteractions(context);
+  }
+
+  @Test
+  public void nothing_to_do_on_oracle() throws Exception {
+    when(db.getDialect()).thenReturn(new Oracle());
+
+    underTest.execute(context);
+
+    verifyZeroInteractions(context);
+  }
+
+  @Test
+  public void update_collation() throws Exception {
+    useMssql();
+    new FixMsSqlCollation.UpdateTableCollation(context, db, "rules")
+      .addVarcharColumn("plugin_rule_key", 200)
+      .execute();
+
+    verify(context).execute(singletonList(
+      "ALTER TABLE rules ALTER COLUMN plugin_rule_key NVARCHAR (200) COLLATE Latin1_General_CS_AS"
+      ));
+  }
+
+  @Test
+  public void update_collation_with_not_nullable_column() throws Exception {
+    useMssql();
+    new FixMsSqlCollation.UpdateTableCollation(context, db, "rules")
+      .addNotNullableVarcharColumn("plugin_rule_key", 200)
+      .execute();
+
+    verify(context).execute(singletonList(
+      "ALTER TABLE rules ALTER COLUMN plugin_rule_key NVARCHAR (200) COLLATE Latin1_General_CS_AS NOT NULL"
+      ));
+  }
+
+  @Test
+  public void update_collation_with_text_column() throws Exception {
+    useMssql();
+    new FixMsSqlCollation.UpdateTableCollation(context, db, "rules")
+      .addClobColumn("description")
+      .execute();
+
+    verify(context).execute(singletonList(
+      "ALTER TABLE rules ALTER COLUMN description NVARCHAR (MAX) COLLATE Latin1_General_CS_AS"
+      ));
+  }
+
+  @Test
+  public void update_collation_remove_and_recreate_index() throws Exception {
+    useMssql();
+    new FixMsSqlCollation.UpdateTableCollation(context, db, "rules")
+      .addIndex("rules_repo_key", "plugin_name", "plugin_rule_key")
+      .addVarcharColumn("plugin_rule_key", 200)
+      .execute();
+
+    verify(context).execute("DROP INDEX rules_repo_key ON rules");
+    verify(context).execute("CREATE INDEX rules_repo_key ON rules(plugin_name,plugin_rule_key)");
+  }
+
+  @Test
+  public void update_collation_recreate_unique_index() throws Exception {
+    useMssql();
+    new FixMsSqlCollation.UpdateTableCollation(context, db, "rules")
+      .addUniqueIndex("rules_repo_key", "plugin_name", "plugin_rule_key")
+      .addVarcharColumn("plugin_rule_key", 200)
+      .execute();
+
+    verify(context).execute("DROP INDEX rules_repo_key ON rules");
+    verify(context).execute("CREATE UNIQUE INDEX rules_repo_key ON rules(plugin_name,plugin_rule_key)");
+  }
+
+  private void useMssql() {
+    when(db.getDialect()).thenReturn(new MsSql());
+  }
+}