]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7798 mssql fix collation now support complex collations
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 24 Jun 2016 08:51:08 +0000 (10:51 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 27 Jun 2016 13:52:46 +0000 (15:52 +0200)
collations can actually have a combination of case sensitivity and accent sensitivity
also, the might not end with accent sensitivity

sonar-db/src/main/java/org/sonar/db/charset/MssqlCharsetHandler.java
sonar-db/src/test/java/org/sonar/db/charset/MssqlCharsetHandlerTest.java

index cbac5a3d59a33c6883c9dbd7479b85f8912e8cd3..6eb183647965b3417c239afc414af9fe547c239c 100644 (file)
@@ -33,12 +33,16 @@ import org.sonar.api.utils.log.Loggers;
 
 import static com.google.common.collect.FluentIterable.from;
 import static java.lang.String.format;
-import static org.apache.commons.lang.StringUtils.endsWithIgnoreCase;
+import static org.apache.commons.lang.StringUtils.containsIgnoreCase;
 import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION;
 
 class MssqlCharsetHandler extends CharsetHandler {
 
   private static final Logger LOGGER = Loggers.get(MssqlCharsetHandler.class);
+  private static final String CASE_SENSITIVE_ACCENT_SENSITIVE = "_CS_AS";
+  private static final String CASE_INSENSITIVE_ACCENT_INSENSITIVE = "_CI_AI";
+  private static final String CASE_INSENSITIVE_ACCENT_SENSITIVE = "_CI_AS";
+  private static final String CASE_SENSITIVE_ACCENT_INSENSITIVE = "_CS_AI";
 
   protected MssqlCharsetHandler(SqlExecutor selectExecutor) {
     super(selectExecutor);
@@ -50,15 +54,16 @@ class MssqlCharsetHandler extends CharsetHandler {
 
     // All VARCHAR columns are returned. No need to check database general collation.
     // Example of row:
-    // issues | kee | Latin1_General_CS_AS
+    // issues | kee | Latin1_General_CS_AS or Latin1_General_100_CI_AS_KS_WS
     Set<String> errors = new LinkedHashSet<>();
     List<ColumnDef> columns = select(connection,
       ColumnDef.SELECT_COLUMNS +
         "FROM [INFORMATION_SCHEMA].[COLUMNS] " +
         "WHERE collation_name is not null " +
-        "ORDER BY table_name,column_name", ColumnDef.ColumnDefRowConverter.INSTANCE);
+        "ORDER BY table_name,column_name",
+      ColumnDef.ColumnDefRowConverter.INSTANCE);
     for (ColumnDef column : from(columns).filter(ColumnDef.IsInSonarQubeTablePredicate.INSTANCE)) {
-      if (!endsWithIgnoreCase(column.getCollation(), "_CS_AS")) {
+      if (!containsIgnoreCase(column.getCollation(), CASE_SENSITIVE_ACCENT_SENSITIVE)) {
         if (flags.contains(AUTO_REPAIR_COLLATION)) {
           repairColumnCollation(connection, column);
         } else {
@@ -132,8 +137,11 @@ class MssqlCharsetHandler extends CharsetHandler {
 
   @VisibleForTesting
   static String toCaseSensitive(String ciCollation) {
-    // Example: Latin1_General_CI_AI --> Latin1_General_CS_AS
-    return ciCollation.substring(0, ciCollation.length() - "_CI_AI".length()) + "_CS_AS";
+    // Example: Latin1_General_CI_AI --> Latin1_General_CS_AS or Latin1_General_100_CI_AS_KS_WS --> Latin1_General_100_CS_AS_KS_WS
+    return ciCollation
+      .replace(CASE_INSENSITIVE_ACCENT_INSENSITIVE, CASE_SENSITIVE_ACCENT_SENSITIVE)
+      .replace(CASE_INSENSITIVE_ACCENT_SENSITIVE, CASE_SENSITIVE_ACCENT_SENSITIVE)
+      .replace(CASE_SENSITIVE_ACCENT_INSENSITIVE, CASE_SENSITIVE_ACCENT_SENSITIVE);
   }
 
   @VisibleForTesting
index a81bf41ddda91df47dfe8b5bfebbd425069cc5e5..f097436adf0595ac5788f1c8c63b071f466dc0e3 100644 (file)
  */
 package org.sonar.db.charset;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
 import java.sql.Connection;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
 import org.sonar.api.utils.MessageException;
 
 import static com.google.common.collect.Sets.immutableEnumSet;
+import static java.lang.String.format;
 import static java.util.Arrays.asList;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
@@ -39,6 +46,7 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION;
 
+@RunWith(DataProviderRunner.class)
 public class MssqlCharsetHandlerTest {
 
   private static final String TABLE_ISSUES = "issues";
@@ -107,6 +115,35 @@ public class MssqlCharsetHandlerTest {
     verify(selectExecutor).executeUpdate(connection, "CREATE UNIQUE INDEX projects_login_and_name ON projects (login,name)");
   }
 
+  @Test
+  @UseDataProvider("combinationsOfCsAsAndSuffix")
+  public void repair_case_insensitive_accent_insensitive_combinations_with_or_without_suffix(String collation, String expectedCollation) throws Exception {
+    answerColumns(Collections.singletonList(new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "Latin1_General", collation, "varchar", 10, false)));
+
+    Connection connection = mock(Connection.class);
+    underTest.handle(connection, immutableEnumSet(AUTO_REPAIR_COLLATION));
+
+    verify(selectExecutor).executeUpdate(connection, "ALTER TABLE issues ALTER COLUMN kee varchar(10) COLLATE " + expectedCollation + " NOT NULL");
+  }
+
+  @DataProvider
+  public static Object[][] combinationsOfCsAsAndSuffix() {
+    List<String[]> res = new ArrayList<>();
+    for (String caseSensitivity : Arrays.asList("CS", "CI")) {
+      for (String accentSensitivity : Arrays.asList("AS", "AI")) {
+        if (caseSensitivity.equals("CI") || accentSensitivity.equals("AI")) {
+          for (String suffix : Arrays.asList("", "_KS_WS")) {
+            res.add(new String[] {
+              format("Latin1_General_%s_%s%s", caseSensitivity, accentSensitivity, suffix),
+              format("Latin1_General_CS_AS%s", suffix)
+            });
+          }
+        }
+      }
+    }
+    return res.stream().toArray(Object[][]::new);
+  }
+
   @Test
   public void support_the_max_size_of_varchar_column() throws Exception {
     // returned size is -1