+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.db;
-
-import org.sonar.api.platform.ServerUpgradeStatus;
-import org.sonar.db.charset.DatabaseCharsetChecker;
-
-/**
- * Checks charset of all database columns when at least one db migration has been executed. This requires
- * to be defined in platform level 3 ({@link org.sonar.server.platform.platformlevel.PlatformLevel3}).
- */
-public class CheckDatabaseCharsetAfterMigration extends CheckDatabaseCharsetAtStartup {
-
- public CheckDatabaseCharsetAfterMigration(ServerUpgradeStatus upgradeStatus, DatabaseCharsetChecker charsetChecker) {
- super(upgradeStatus, charsetChecker);
- }
-
- @Override
- public void start() {
- if (getUpgradeStatus().isFreshInstall() || getUpgradeStatus().isUpgraded()) {
- check();
- }
- }
-}
import org.sonar.api.platform.ServerUpgradeStatus;
import org.sonar.db.charset.DatabaseCharsetChecker;
+import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8;
+
/**
* Checks charset of all existing database columns at startup, before executing db migrations. This requires
* to be defined in platform level 2 ({@link org.sonar.server.platform.platformlevel.PlatformLevel2}).
}
protected final void check() {
- boolean enforceUtf8 = getUpgradeStatus().isFreshInstall();
- charsetChecker.check(enforceUtf8);
- }
-
- protected final ServerUpgradeStatus getUpgradeStatus() {
- return upgradeStatus;
+ if (upgradeStatus.isFreshInstall()) {
+ charsetChecker.check(ENFORCE_UTF8);
+ } else if (!upgradeStatus.isUpgraded()) {
+ charsetChecker.check();
+ }
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db;
+
+import org.picocontainer.Startable;
+import org.sonar.api.platform.ServerUpgradeStatus;
+import org.sonar.db.charset.DatabaseCharsetChecker;
+
+import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION;
+import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8;
+
+/**
+ * Checks charset of all database columns when at least one db migration has been executed.
+ */
+public class CheckDatabaseCollationDuringMigration implements Startable {
+
+ private final ServerUpgradeStatus upgradeStatus;
+ private final DatabaseCharsetChecker charsetChecker;
+
+ public CheckDatabaseCollationDuringMigration(ServerUpgradeStatus upgradeStatus, DatabaseCharsetChecker charsetChecker) {
+ this.upgradeStatus = upgradeStatus;
+ this.charsetChecker = charsetChecker;
+ }
+
+ @Override
+ public void start() {
+ if (upgradeStatus.isFreshInstall()) {
+ charsetChecker.check(ENFORCE_UTF8, AUTO_REPAIR_COLLATION);
+ } else if (upgradeStatus.isUpgraded()) {
+ charsetChecker.check(AUTO_REPAIR_COLLATION);
+ }
+ }
+
+ @Override
+ public void stop() {
+ // do nothing
+ }
+}
import org.sonar.api.utils.UriReader;
import org.sonar.core.util.DefaultHttpDownloader;
-import org.sonar.server.db.CheckDatabaseCharsetAfterMigration;
import org.sonar.server.platform.PersistentSettings;
import org.sonar.server.platform.ServerIdGenerator;
import org.sonar.server.platform.ServerIdLoader;
@Override
protected void configureLevel() {
add(
- CheckDatabaseCharsetAfterMigration.class,
PersistentSettings.class,
ServerMetadataPersister.class,
DefaultHttpDownloader.class,
package org.sonar.server.platform.platformlevel;
import org.sonar.server.app.ProcessCommandWrapper;
+import org.sonar.server.db.CheckDatabaseCollationDuringMigration;
import org.sonar.server.es.IndexerStartupTask;
import org.sonar.server.issue.filter.RegisterIssueFilters;
import org.sonar.server.platform.ServerLifecycleNotifier;
@Override
protected void configureLevel() {
add(
+ CheckDatabaseCollationDuringMigration.class,
IndexerStartupTask.class,
RegisterMetrics.class,
RegisterQualityGates.class,
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.db;
-
-import org.junit.Test;
-import org.sonar.api.platform.ServerUpgradeStatus;
-import org.sonar.db.charset.DatabaseCharsetChecker;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class CheckDatabaseCharsetAfterMigrationTest {
- ServerUpgradeStatus upgradeStatus = mock(ServerUpgradeStatus.class);
- DatabaseCharsetChecker charsetChecker = mock(DatabaseCharsetChecker.class);
- CheckDatabaseCharsetAfterMigration underTest = new CheckDatabaseCharsetAfterMigration(upgradeStatus, charsetChecker);
-
- @Test
- public void enforces_utf8_if_fresh_install() {
- when(upgradeStatus.isFreshInstall()).thenReturn(true);
- underTest.start();
- verify(charsetChecker).check(true);
-
- underTest.stop();
- verifyNoMoreInteractions(charsetChecker);
- }
-
- @Test
- public void checks_charset_but_does_not_enforce_utf8_if_db_upgrade() {
- when(upgradeStatus.isFreshInstall()).thenReturn(false);
- when(upgradeStatus.isUpgraded()).thenReturn(true);
- underTest.start();
- verify(charsetChecker).check(false);
-
- underTest.stop();
- verifyNoMoreInteractions(charsetChecker);
- }
-
- @Test
- public void does_nothing_if_no_db_changes() {
- when(upgradeStatus.isFreshInstall()).thenReturn(false);
- when(upgradeStatus.isUpgraded()).thenReturn(false);
-
- underTest.start();
- underTest.stop();
- verifyZeroInteractions(charsetChecker);
- }
-
-}
*/
package org.sonar.server.db;
+import org.junit.After;
import org.junit.Test;
import org.sonar.api.platform.ServerUpgradeStatus;
import org.sonar.db.charset.DatabaseCharsetChecker;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.ENFORCE_UTF8;
public class CheckDatabaseCharsetAtStartupTest {
DatabaseCharsetChecker charsetChecker = mock(DatabaseCharsetChecker.class);
CheckDatabaseCharsetAtStartup underTest = new CheckDatabaseCharsetAtStartup(upgradeStatus, charsetChecker);
+ @After
+ public void tearDown() {
+ underTest.stop();
+ }
+
@Test
- public void enforces_utf8_if_fresh_install() {
+ public void enforce_utf8_if_fresh_install() {
when(upgradeStatus.isFreshInstall()).thenReturn(true);
+
underTest.start();
- verify(charsetChecker).check(true);
- underTest.stop();
- verifyNoMoreInteractions(charsetChecker);
+ verify(charsetChecker).check(ENFORCE_UTF8);
}
@Test
- public void does_not_enforce_utf8_if_not_fresh_install() {
+ public void do_not_enforce_utf8_and_do_not_repair_at_startup_if_not_fresh_install() {
when(upgradeStatus.isFreshInstall()).thenReturn(false);
+
underTest.start();
- verify(charsetChecker).check(false);
- underTest.stop();
- verifyNoMoreInteractions(charsetChecker);
+ verify(charsetChecker).check();
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db;
+
+import org.junit.After;
+import org.junit.Test;
+import org.sonar.api.platform.ServerUpgradeStatus;
+import org.sonar.db.charset.DatabaseCharsetChecker;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+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 CheckDatabaseCollationDuringMigrationTest {
+ ServerUpgradeStatus upgradeStatus = mock(ServerUpgradeStatus.class);
+ DatabaseCharsetChecker charsetChecker = mock(DatabaseCharsetChecker.class);
+ CheckDatabaseCollationDuringMigration underTest = new CheckDatabaseCollationDuringMigration(upgradeStatus, charsetChecker);
+
+ @After
+ public void tearDown() {
+ underTest.stop();
+ }
+
+ @Test
+ public void enforce_utf8_and_optionally_repair_collation_if_fresh_install() {
+ when(upgradeStatus.isFreshInstall()).thenReturn(true);
+
+ underTest.start();
+
+ verify(charsetChecker).check(ENFORCE_UTF8, AUTO_REPAIR_COLLATION);
+ }
+
+ @Test
+ public void repair_collation_but_do_not_enforce_utf8_if_db_upgrade() {
+ when(upgradeStatus.isFreshInstall()).thenReturn(false);
+ when(upgradeStatus.isUpgraded()).thenReturn(true);
+
+ underTest.start();
+
+ verify(charsetChecker).check(AUTO_REPAIR_COLLATION);
+ }
+
+ @Test
+ public void do_nothing_if_no_db_changes() {
+ when(upgradeStatus.isFreshInstall()).thenReturn(false);
+ when(upgradeStatus.isUpgraded()).thenReturn(false);
+
+ underTest.start();
+
+ verifyZeroInteractions(charsetChecker);
+ }
+
+}
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
+import java.util.Set;
import javax.annotation.CheckForNull;
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;
protected final <T> List<T> select(Connection connection, String sql, SqlExecutor.RowConverter<T> rowConverter) throws SQLException {
return selectExecutor.executeSelect(connection, sql, rowConverter);
}
-
}
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
*/
public class DatabaseCharsetChecker {
+ public enum Flag {
+ ENFORCE_UTF8, AUTO_REPAIR_COLLATION
+ }
+
private final Database db;
private final SqlExecutor selectExecutor;
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) {
throw new IllegalArgumentException("Database not supported: " + dialect.getId());
}
}
-
}
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 {
}
@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] " +
"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 {
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;
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 {
}
@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
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)));
}
}
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 {
}
@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);
}
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 {
}
@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);
}
"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;
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;
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 {
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
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
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;
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 {
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)));
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");
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");
}
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());
}
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;
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 {
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),
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");
}
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");
}
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();
answerSql(
singletonList(new String[] {"UTF8"}), singletonList(new String[] {"BINARY"}));
- underTest.handle(mock(Connection.class), true);
+ underTest.handle(mock(Connection.class), ENFORCE_UTF8_FLAGS);
}
@Test
answerSql(
singletonList(new String[] {"AL32UTF8"}), singletonList(new String[] {"BINARY"}));
- underTest.handle(mock(Connection.class), true);
+ underTest.handle(mock(Connection.class), ENFORCE_UTF8_FLAGS);
}
@Test
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
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
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 {
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 {
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
Arrays.<String[]>asList(new String[] {"utf8"}));
// no error
- underTest.handle(mock(Connection.class), true);
+ underTest.handle(mock(Connection.class), immutableEnumSet(ENFORCE_UTF8));
}
@Test
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
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 {