diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2016-05-18 16:02:12 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2016-05-19 15:35:58 +0200 |
commit | 4478d4093e636acf7811e96b08fb9ecdbe22eb0f (patch) | |
tree | 18af88b01b82f3d655becd07a87106f5dfb3dfba /sonar-db | |
parent | bc9b9edb7c887a74c47c59d615c48ae7024ab392 (diff) | |
download | sonarqube-4478d4093e636acf7811e96b08fb9ecdbe22eb0f.tar.gz sonarqube-4478d4093e636acf7811e96b08fb9ecdbe22eb0f.zip |
SONAR-7649 do not repair db collation outside migration
Diffstat (limited to 'sonar-db')
11 files changed, 162 insertions, 66 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/charset/CharsetHandler.java b/sonar-db/src/main/java/org/sonar/db/charset/CharsetHandler.java index 233cb14b5a8..df8d2d4fa3c 100644 --- a/sonar-db/src/main/java/org/sonar/db/charset/CharsetHandler.java +++ b/sonar-db/src/main/java/org/sonar/db/charset/CharsetHandler.java @@ -22,6 +22,7 @@ package org.sonar.db.charset; import java.sql.Connection; import java.sql.SQLException; import java.util.List; +import java.util.Set; import javax.annotation.CheckForNull; abstract class CharsetHandler { @@ -34,7 +35,7 @@ abstract class CharsetHandler { this.selectExecutor = selectExecutor; } - abstract void handle(Connection connection, boolean enforceUtf8) throws SQLException; + abstract void handle(Connection connection, Set<DatabaseCharsetChecker.Flag> flags) throws SQLException; protected SqlExecutor getSqlExecutor() { return selectExecutor; @@ -61,5 +62,4 @@ abstract class CharsetHandler { protected final <T> List<T> select(Connection connection, String sql, SqlExecutor.RowConverter<T> rowConverter) throws SQLException { return selectExecutor.executeSelect(connection, sql, rowConverter); } - } diff --git a/sonar-db/src/main/java/org/sonar/db/charset/DatabaseCharsetChecker.java b/sonar-db/src/main/java/org/sonar/db/charset/DatabaseCharsetChecker.java index cdc87bc3b2d..2e89e2d3a97 100644 --- a/sonar-db/src/main/java/org/sonar/db/charset/DatabaseCharsetChecker.java +++ b/sonar-db/src/main/java/org/sonar/db/charset/DatabaseCharsetChecker.java @@ -31,6 +31,9 @@ import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.PostgreSql; +import static com.google.common.collect.Sets.immutableEnumSet; +import static java.util.Arrays.asList; + /** * On fresh installations, checks that all db columns are UTF8. On all installations on MySQL or MSSQL, * whatever fresh or upgrade, fixes case-insensitive columns by converting them to @@ -40,6 +43,10 @@ import org.sonar.db.dialect.PostgreSql; */ public class DatabaseCharsetChecker { + public enum Flag { + ENFORCE_UTF8, AUTO_REPAIR_COLLATION + } + private final Database db; private final SqlExecutor selectExecutor; @@ -53,12 +60,12 @@ public class DatabaseCharsetChecker { this.selectExecutor = selectExecutor; } - public void check(boolean enforceUtf8) { + public void check(Flag... flags) { try { try (Connection connection = db.getDataSource().getConnection()) { CharsetHandler handler = getHandler(db.getDialect()); if (handler != null) { - handler.handle(connection, enforceUtf8); + handler.handle(connection, immutableEnumSet(asList(flags))); } } } catch (SQLException e) { @@ -85,5 +92,4 @@ public class DatabaseCharsetChecker { throw new IllegalArgumentException("Database not supported: " + dialect.getId()); } } - } diff --git a/sonar-db/src/main/java/org/sonar/db/charset/MssqlCharsetHandler.java b/sonar-db/src/main/java/org/sonar/db/charset/MssqlCharsetHandler.java index 774251771eb..cbac5a3d59a 100644 --- a/sonar-db/src/main/java/org/sonar/db/charset/MssqlCharsetHandler.java +++ b/sonar-db/src/main/java/org/sonar/db/charset/MssqlCharsetHandler.java @@ -20,16 +20,21 @@ package org.sonar.db.charset; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; +import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Logger; 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.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION; class MssqlCharsetHandler extends CharsetHandler { @@ -40,15 +45,13 @@ class MssqlCharsetHandler extends CharsetHandler { } @Override - void handle(Connection connection, boolean enforceUtf8) throws SQLException { - LOGGER.info("Verify that database collation is case-sensitive and accent-sensitive"); - checkCollation(connection); - } + void handle(Connection connection, Set<DatabaseCharsetChecker.Flag> flags) throws SQLException { + logInit(flags); - private void checkCollation(Connection connection) throws SQLException { // All VARCHAR columns are returned. No need to check database general collation. // Example of row: // issues | kee | Latin1_General_CS_AS + Set<String> errors = new LinkedHashSet<>(); List<ColumnDef> columns = select(connection, ColumnDef.SELECT_COLUMNS + "FROM [INFORMATION_SCHEMA].[COLUMNS] " + @@ -56,9 +59,26 @@ class MssqlCharsetHandler extends CharsetHandler { "ORDER BY table_name,column_name", ColumnDef.ColumnDefRowConverter.INSTANCE); for (ColumnDef column : from(columns).filter(ColumnDef.IsInSonarQubeTablePredicate.INSTANCE)) { if (!endsWithIgnoreCase(column.getCollation(), "_CS_AS")) { - repairColumnCollation(connection, column); + if (flags.contains(AUTO_REPAIR_COLLATION)) { + repairColumnCollation(connection, column); + } else { + errors.add(format("%s.%s", column.getTable(), column.getColumn())); + } } } + + if (!errors.isEmpty()) { + throw MessageException.of(format("Case-sensitive and accent-sensitive collation is required for database columns [%s]", + Joiner.on(", ").join(errors))); + } + } + + private static void logInit(Set<DatabaseCharsetChecker.Flag> flags) { + if (flags.contains(AUTO_REPAIR_COLLATION)) { + LOGGER.info("Repair case-insensitive or accent-insensitive database columns"); + } else { + LOGGER.info("Verify that database columns are case-sensitive and accent-sensitive"); + } } private void repairColumnCollation(Connection connection, ColumnDef column) throws SQLException { diff --git a/sonar-db/src/main/java/org/sonar/db/charset/MysqlCharsetHandler.java b/sonar-db/src/main/java/org/sonar/db/charset/MysqlCharsetHandler.java index 43bec2d9ffc..4f003aba735 100644 --- a/sonar-db/src/main/java/org/sonar/db/charset/MysqlCharsetHandler.java +++ b/sonar-db/src/main/java/org/sonar/db/charset/MysqlCharsetHandler.java @@ -23,8 +23,9 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import java.sql.Connection; import java.sql.SQLException; -import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Logger; @@ -34,6 +35,8 @@ import static com.google.common.collect.FluentIterable.from; import static java.lang.String.format; import static org.apache.commons.lang.StringUtils.containsIgnoreCase; import static org.apache.commons.lang.StringUtils.endsWithIgnoreCase; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8; class MysqlCharsetHandler extends CharsetHandler { @@ -45,20 +48,22 @@ class MysqlCharsetHandler extends CharsetHandler { } @Override - void handle(Connection connection, boolean enforceUtf8) throws SQLException { - logInit(enforceUtf8); - checkCollation(connection, enforceUtf8); + void handle(Connection connection, Set<DatabaseCharsetChecker.Flag> flags) throws SQLException { + logInit(flags); + checkCollation(connection, flags); } - private static void logInit(boolean enforceUtf8) { - String message = "Verify that database collation is case-sensitive"; - if (enforceUtf8) { - message = "Verify that database collation is UTF8"; + private static void logInit(Set<DatabaseCharsetChecker.Flag> flags) { + if (flags.contains(AUTO_REPAIR_COLLATION)) { + LOGGER.info("Repair case-insensitive database columns"); + } else if (flags.contains(ENFORCE_UTF8)) { + LOGGER.info("Verify that database collation is UTF8"); + } else { + LOGGER.info("Verify that database collation is case-sensitive"); } - LOGGER.info(message); } - private void checkCollation(Connection connection, boolean enforceUtf8) throws SQLException { + private void checkCollation(Connection connection, Set<DatabaseCharsetChecker.Flag> flags) throws SQLException { // All VARCHAR columns are returned. No need to check database general collation. // Example of row: // issues | kee | utf8 | utf8_bin @@ -66,16 +71,21 @@ class MysqlCharsetHandler extends CharsetHandler { ColumnDef.SELECT_COLUMNS + "FROM INFORMATION_SCHEMA.columns " + "WHERE table_schema=database() and character_set_name is not null and collation_name is not null", ColumnDef.ColumnDefRowConverter.INSTANCE); - List<String> utf8Errors = new ArrayList<>(); + Set<String> errors = new LinkedHashSet<>(); for (ColumnDef column : from(columns).filter(ColumnDef.IsInSonarQubeTablePredicate.INSTANCE)) { - if (enforceUtf8 && !containsIgnoreCase(column.getCharset(), UTF8)) { - utf8Errors.add(format("%s.%s", column.getTable(), column.getColumn())); - } else if (endsWithIgnoreCase(column.getCollation(), "_ci")) { - repairCaseInsensitiveColumn(connection, column); + if (flags.contains(ENFORCE_UTF8) && !containsIgnoreCase(column.getCharset(), UTF8)) { + errors.add(format("%s.%s", column.getTable(), column.getColumn())); + } + if (endsWithIgnoreCase(column.getCollation(), "_ci")) { + if (flags.contains(AUTO_REPAIR_COLLATION)) { + repairCaseInsensitiveColumn(connection, column); + } else { + errors.add(format("%s.%s", column.getTable(), column.getColumn())); + } } } - if (!utf8Errors.isEmpty()) { - throw MessageException.of(format("UTF8 case-sensitive collation is required for database columns [%s]", Joiner.on(", ").join(utf8Errors))); + if (!errors.isEmpty()) { + throw MessageException.of(format("UTF8 case-sensitive collation is required for database columns [%s]", Joiner.on(", ").join(errors))); } } diff --git a/sonar-db/src/main/java/org/sonar/db/charset/OracleCharsetHandler.java b/sonar-db/src/main/java/org/sonar/db/charset/OracleCharsetHandler.java index 179d7c2f796..1ff377175cf 100644 --- a/sonar-db/src/main/java/org/sonar/db/charset/OracleCharsetHandler.java +++ b/sonar-db/src/main/java/org/sonar/db/charset/OracleCharsetHandler.java @@ -21,11 +21,13 @@ package org.sonar.db.charset; import java.sql.Connection; import java.sql.SQLException; +import java.util.Set; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Loggers; import static java.lang.String.format; import static org.apache.commons.lang.StringUtils.containsIgnoreCase; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8; class OracleCharsetHandler extends CharsetHandler { @@ -34,9 +36,9 @@ class OracleCharsetHandler extends CharsetHandler { } @Override - public void handle(Connection connection, boolean enforceUtf8) throws SQLException { + public void handle(Connection connection, Set<DatabaseCharsetChecker.Flag> flags) throws SQLException { // Oracle does not allow to override character set on tables. Only global charset is verified. - if (enforceUtf8) { + if (flags.contains(ENFORCE_UTF8)) { Loggers.get(getClass()).info("Verify that database charset is UTF8"); checkUtf8(connection); } diff --git a/sonar-db/src/main/java/org/sonar/db/charset/PostgresCharsetHandler.java b/sonar-db/src/main/java/org/sonar/db/charset/PostgresCharsetHandler.java index 1452750ae1f..81cd5bcc1a3 100644 --- a/sonar-db/src/main/java/org/sonar/db/charset/PostgresCharsetHandler.java +++ b/sonar-db/src/main/java/org/sonar/db/charset/PostgresCharsetHandler.java @@ -22,14 +22,16 @@ package org.sonar.db.charset; import com.google.common.base.Joiner; import java.sql.Connection; import java.sql.SQLException; -import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Loggers; import static java.lang.String.format; import static org.apache.commons.lang.StringUtils.containsIgnoreCase; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8; class PostgresCharsetHandler extends CharsetHandler { @@ -38,9 +40,9 @@ class PostgresCharsetHandler extends CharsetHandler { } @Override - void handle(Connection connection, boolean enforceUtf8) throws SQLException { + void handle(Connection connection, Set<DatabaseCharsetChecker.Flag> flags) throws SQLException { // PostgreSQL does not support case-insensitive collations. Only charset must be verified. - if (enforceUtf8) { + if (flags.contains(ENFORCE_UTF8)) { Loggers.get(getClass()).info("Verify that database collation supports UTF8"); checkUtf8(connection); } @@ -58,7 +60,7 @@ class PostgresCharsetHandler extends CharsetHandler { "and udt_name='varchar' " + "order by table_name, column_name", new SqlExecutor.StringsConverter(3 /* columns returned by SELECT */)); boolean mustCheckGlobalCollation = false; - List<String> errors = new ArrayList<>(); + Set<String> errors = new LinkedHashSet<>(); for (String[] row : rows) { if (StringUtils.isBlank(row[2])) { mustCheckGlobalCollation = true; diff --git a/sonar-db/src/test/java/org/sonar/db/charset/DatabaseCharsetCheckerTest.java b/sonar-db/src/test/java/org/sonar/db/charset/DatabaseCharsetCheckerTest.java index 88e47d4af2e..41c87e6b28f 100644 --- a/sonar-db/src/test/java/org/sonar/db/charset/DatabaseCharsetCheckerTest.java +++ b/sonar-db/src/test/java/org/sonar/db/charset/DatabaseCharsetCheckerTest.java @@ -21,6 +21,9 @@ package org.sonar.db.charset; import java.sql.Connection; import java.sql.SQLException; +import java.util.Set; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -35,13 +38,15 @@ import org.sonar.db.dialect.PostgreSql; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.anySet; +import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8; public class DatabaseCharsetCheckerTest { @@ -58,8 +63,17 @@ public class DatabaseCharsetCheckerTest { when(underTest.getHandler(dialect)).thenReturn(handler); when(db.getDialect()).thenReturn(dialect); - underTest.check(true); - verify(handler).handle(any(Connection.class), eq(true)); + underTest.check(ENFORCE_UTF8); + verify(handler).handle(any(Connection.class), argThat(new TypeSafeMatcher<Set<DatabaseCharsetChecker.Flag>>() { + @Override + protected boolean matchesSafely(Set<DatabaseCharsetChecker.Flag> flags) { + return flags.contains(ENFORCE_UTF8) && flags.size() == 1; + } + + @Override + public void describeTo(Description description) { + } + })); } @Test @@ -67,11 +81,11 @@ public class DatabaseCharsetCheckerTest { Oracle dialect = new Oracle(); when(underTest.getHandler(dialect)).thenReturn(handler); when(db.getDialect()).thenReturn(dialect); - doThrow(new SQLException("failure")).when(handler).handle(any(Connection.class), anyBoolean()); + doThrow(new SQLException("failure")).when(handler).handle(any(Connection.class), anySet()); expectedException.expect(IllegalStateException.class); expectedException.expectMessage("failure"); - underTest.check(true); + underTest.check(AUTO_REPAIR_COLLATION); } @Test diff --git a/sonar-db/src/test/java/org/sonar/db/charset/MssqlCharsetHandlerTest.java b/sonar-db/src/test/java/org/sonar/db/charset/MssqlCharsetHandlerTest.java index f3748576380..a81bf41ddda 100644 --- a/sonar-db/src/test/java/org/sonar/db/charset/MssqlCharsetHandlerTest.java +++ b/sonar-db/src/test/java/org/sonar/db/charset/MssqlCharsetHandlerTest.java @@ -26,7 +26,9 @@ import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.utils.MessageException; +import static com.google.common.collect.Sets.immutableEnumSet; import static java.util.Arrays.asList; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; @@ -35,6 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION; public class MssqlCharsetHandlerTest { @@ -50,28 +53,42 @@ public class MssqlCharsetHandlerTest { MssqlCharsetHandler underTest = new MssqlCharsetHandler(selectExecutor); @Test - public void does_not_fail_if_charsets_of_all_columns_are_utf8() throws Exception { + public void do_not_fail_if_charsets_of_all_columns_are_CS_AS() throws Exception { answerColumns(asList( new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "Latin1_General", "Latin1_General_CS_AS", "varchar", 10, false), new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", "Latin1_General_CS_AS", "varchar", 10, false))); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), Collections.<DatabaseCharsetChecker.Flag>emptySet()); } @Test - public void repairs_case_insensitive_column_without_index() throws Exception { + public void fail_if_a_column_is_case_insensitive_and_repair_is_disabled() throws Exception { answerColumns(asList( new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "Latin1_General", "Latin1_General_CS_AS", "varchar", 10, false), new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", "Latin1_General_CI_AI", "varchar", 10, false))); + expectedException.expect(MessageException.class); + expectedException.expectMessage("Case-sensitive and accent-sensitive collation is required for database columns [projects.name]"); Connection connection = mock(Connection.class); - underTest.handle(connection, false); + underTest.handle(connection, Collections.<DatabaseCharsetChecker.Flag>emptySet()); + + verify(selectExecutor, never()).executeUpdate(any(Connection.class), anyString()); + } + + @Test + public void repair_case_insensitive_column_without_index() throws Exception { + answerColumns(asList( + new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "Latin1_General", "Latin1_General_CS_AS", "varchar", 10, false), + new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", "Latin1_General_CI_AI", "varchar", 10, false))); + + Connection connection = mock(Connection.class); + underTest.handle(connection, immutableEnumSet(AUTO_REPAIR_COLLATION)); verify(selectExecutor).executeUpdate(connection, "ALTER TABLE projects ALTER COLUMN name varchar(10) COLLATE Latin1_General_CS_AS NOT NULL"); } @Test - public void repairs_case_insensitive_column_with_indices() throws Exception { + public void repair_case_insensitive_column_with_indices() throws Exception { answerColumns(asList( new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "Latin1_General", "Latin1_General_CS_AS", "varchar", 10, false), new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", "Latin1_General_CI_AI", "varchar", 10, false))); @@ -81,7 +98,7 @@ public class MssqlCharsetHandlerTest { new MssqlCharsetHandler.ColumnIndex("projects_login_and_name", true, "login,name"))); Connection connection = mock(Connection.class); - underTest.handle(connection, false); + underTest.handle(connection, immutableEnumSet(AUTO_REPAIR_COLLATION)); verify(selectExecutor).executeUpdate(connection, "DROP INDEX projects.projects_name"); verify(selectExecutor).executeUpdate(connection, "DROP INDEX projects.projects_login_and_name"); @@ -97,7 +114,7 @@ public class MssqlCharsetHandlerTest { answerIndices(Collections.<MssqlCharsetHandler.ColumnIndex>emptyList()); Connection connection = mock(Connection.class); - underTest.handle(connection, false); + underTest.handle(connection, immutableEnumSet(AUTO_REPAIR_COLLATION)); verify(selectExecutor).executeUpdate(connection, "ALTER TABLE projects ALTER COLUMN name nvarchar(max) COLLATE Latin1_General_CS_AS NOT NULL"); } @@ -107,7 +124,7 @@ public class MssqlCharsetHandlerTest { answerColumns(asList(new ColumnDef("sys.sysusers", COLUMN_NAME, "Latin1_General", "Latin1_General_CI_AI", "varchar", 10, false))); Connection connection = mock(Connection.class); - underTest.handle(connection, false); + underTest.handle(connection, immutableEnumSet(AUTO_REPAIR_COLLATION)); verify(selectExecutor, never()).executeUpdate(any(Connection.class), anyString()); } diff --git a/sonar-db/src/test/java/org/sonar/db/charset/MysqlCharsetHandlerTest.java b/sonar-db/src/test/java/org/sonar/db/charset/MysqlCharsetHandlerTest.java index 9e9687a2bfd..d050fbedf3c 100644 --- a/sonar-db/src/test/java/org/sonar/db/charset/MysqlCharsetHandlerTest.java +++ b/sonar-db/src/test/java/org/sonar/db/charset/MysqlCharsetHandlerTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.utils.MessageException; +import static com.google.common.collect.Sets.immutableEnumSet; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; @@ -35,6 +36,8 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8; public class MysqlCharsetHandlerTest { @@ -50,17 +53,29 @@ public class MysqlCharsetHandlerTest { MysqlCharsetHandler underTest = new MysqlCharsetHandler(selectExecutor); @Test - public void does_not_fail_if_charsets_of_all_columns_are_utf8() throws Exception { + public void do_not_fail_if_charsets_of_all_columns_are_utf8_and_case_sensitive() throws Exception { answerColumnDef(asList( new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "utf8", "utf8_bin", "varchar", 10, false), new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "utf8", "utf8_bin", "varchar", 10, false))); // all columns are utf8 - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), immutableEnumSet(ENFORCE_UTF8)); } @Test - public void fails_if_not_utf8() throws Exception { + public void fail_if_charsets_of_a_column_is_utf8_but_case_insensitive() throws Exception { + answerColumnDef(asList( + new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "utf8", "utf8_bin", "varchar", 10, false), + new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "utf8", "utf8_general_ci", "varchar", 10, false))); + + expectedException.expect(MessageException.class); + expectedException.expectMessage("UTF8 case-sensitive collation is required for database columns [projects.name]"); + + underTest.handle(mock(Connection.class), immutableEnumSet(ENFORCE_UTF8)); + } + + @Test + public void fail_if_not_utf8() throws Exception { answerColumnDef(asList( new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "utf8", "utf8_bin", "varchar", 10, false), new ColumnDef(TABLE_PROJECTS, COLUMN_KEE, "latin1", "latin1_german1_ci", "varchar", 10, false), @@ -68,17 +83,17 @@ public class MysqlCharsetHandlerTest { expectedException.expect(MessageException.class); expectedException.expectMessage("UTF8 case-sensitive collation is required for database columns [projects.kee, projects.name]"); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), immutableEnumSet(ENFORCE_UTF8)); } @Test - public void repairs_case_insensitive_column() throws Exception { + public void repair_case_insensitive_column() throws Exception { answerColumnDef(asList( new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "utf8", "utf8_bin", "varchar", 10, false), new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "latin1", "latin1_swedish_ci", "varchar", 10, false))); Connection connection = mock(Connection.class); - underTest.handle(connection, false); + underTest.handle(connection, immutableEnumSet(AUTO_REPAIR_COLLATION)); verify(selectExecutor).executeUpdate(connection, "ALTER TABLE projects MODIFY name varchar(10) CHARACTER SET 'latin1' COLLATE 'latin1_bin' NOT NULL"); } @@ -88,7 +103,7 @@ public class MysqlCharsetHandlerTest { answerColumnDef(asList(new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "latin1", "latin1_german1_ci", "longtext", 4_294_967_295L, false))); Connection connection = mock(Connection.class); - underTest.handle(connection, false); + underTest.handle(connection, immutableEnumSet(AUTO_REPAIR_COLLATION)); verify(selectExecutor).executeUpdate(connection, "ALTER TABLE " + TABLE_ISSUES + " MODIFY " + COLUMN_KEE + " longtext CHARACTER SET 'latin1' COLLATE 'latin1_bin' NOT NULL"); } diff --git a/sonar-db/src/test/java/org/sonar/db/charset/OracleCharsetHandlerTest.java b/sonar-db/src/test/java/org/sonar/db/charset/OracleCharsetHandlerTest.java index 0d39cdb598d..76be06e7f21 100644 --- a/sonar-db/src/test/java/org/sonar/db/charset/OracleCharsetHandlerTest.java +++ b/sonar-db/src/test/java/org/sonar/db/charset/OracleCharsetHandlerTest.java @@ -23,19 +23,25 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.Collections; import java.util.List; +import java.util.Set; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.utils.MessageException; +import org.sonar.db.charset.DatabaseCharsetChecker.Flag; +import static com.google.common.collect.Sets.immutableEnumSet; import static java.util.Collections.singletonList; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8; public class OracleCharsetHandlerTest { + private static final Set<Flag> ENFORCE_UTF8_FLAGS = immutableEnumSet(ENFORCE_UTF8); + @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -47,7 +53,7 @@ public class OracleCharsetHandlerTest { answerSql( singletonList(new String[] {"UTF8"}), singletonList(new String[] {"BINARY"})); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), ENFORCE_UTF8_FLAGS); } @Test @@ -55,7 +61,7 @@ public class OracleCharsetHandlerTest { answerSql( singletonList(new String[] {"AL32UTF8"}), singletonList(new String[] {"BINARY"})); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), ENFORCE_UTF8_FLAGS); } @Test @@ -66,7 +72,7 @@ public class OracleCharsetHandlerTest { expectedException.expect(MessageException.class); expectedException.expectMessage("Oracle must be have UTF8 charset and BINARY sort. NLS_CHARACTERSET is LATIN and NLS_SORT is BINARY."); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), ENFORCE_UTF8_FLAGS); } @Test @@ -77,7 +83,7 @@ public class OracleCharsetHandlerTest { expectedException.expect(MessageException.class); expectedException.expectMessage("Oracle must be have UTF8 charset and BINARY sort. NLS_CHARACTERSET is UTF8 and NLS_SORT is LINGUISTIC."); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), ENFORCE_UTF8_FLAGS); } @Test @@ -86,12 +92,12 @@ public class OracleCharsetHandlerTest { expectedException.expect(MessageException.class); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), ENFORCE_UTF8_FLAGS); } @Test public void does_nothing_if_utf8_must_not_verified() throws Exception { - underTest.handle(mock(Connection.class), false); + underTest.handle(mock(Connection.class), Collections.<Flag>emptySet()); } private void answerSql(List<String[]> firstRequest, List<String[]>... otherRequests) throws SQLException { diff --git a/sonar-db/src/test/java/org/sonar/db/charset/PostgresCharsetHandlerTest.java b/sonar-db/src/test/java/org/sonar/db/charset/PostgresCharsetHandlerTest.java index de30b65f63a..d348e31117e 100644 --- a/sonar-db/src/test/java/org/sonar/db/charset/PostgresCharsetHandlerTest.java +++ b/sonar-db/src/test/java/org/sonar/db/charset/PostgresCharsetHandlerTest.java @@ -22,17 +22,21 @@ package org.sonar.db.charset; import java.sql.Connection; import java.sql.SQLException; 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.sonar.api.utils.MessageException; +import org.sonar.db.charset.DatabaseCharsetChecker.Flag; +import static com.google.common.collect.Sets.immutableEnumSet; import static java.util.Arrays.asList; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8; public class PostgresCharsetHandlerTest { @@ -53,7 +57,7 @@ public class PostgresCharsetHandlerTest { new String[] {TABLE_ISSUES, COLUMN_KEE, "utf8"}, new String[] {TABLE_PROJECTS, COLUMN_NAME, "utf8"})); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), immutableEnumSet(ENFORCE_UTF8)); } @Test @@ -68,7 +72,7 @@ public class PostgresCharsetHandlerTest { Arrays.<String[]>asList(new String[] {"utf8"})); // no error - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), immutableEnumSet(ENFORCE_UTF8)); } @Test @@ -81,7 +85,7 @@ public class PostgresCharsetHandlerTest { expectedException.expect(MessageException.class); expectedException.expectMessage("Database columns [projects.kee, projects.name] must support UTF8 collation."); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), immutableEnumSet(ENFORCE_UTF8)); } @Test @@ -98,12 +102,12 @@ public class PostgresCharsetHandlerTest { expectedException.expect(MessageException.class); expectedException.expectMessage("Database collation is latin. It must support UTF8."); - underTest.handle(mock(Connection.class), true); + underTest.handle(mock(Connection.class), immutableEnumSet(ENFORCE_UTF8)); } @Test public void does_nothing_if_utf8_must_not_verified() throws Exception { - underTest.handle(mock(Connection.class), false); + underTest.handle(mock(Connection.class), Collections.<Flag>emptySet()); } private void answerSql(List<String[]> firstRequest, List<String[]>... otherRequests) throws SQLException { |