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);
// 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 {
@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
*/
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;
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";
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