aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2016-12-15 10:20:10 +0100
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>2016-12-16 14:35:27 +0100
commit695c9b54d7e7a97075180f17beba537114d404f1 (patch)
treea1c5e8fc370f09f5d667d521b0754474a2c73a7d /sonar-db
parentafe690ce40c3b178b0e0718ee9ac57c179a481a7 (diff)
downloadsonarqube-695c9b54d7e7a97075180f17beba537114d404f1.tar.gz
sonarqube-695c9b54d7e7a97075180f17beba537114d404f1.zip
SONAR-8445 move package charset from sonar-db to sonar-db-migration
Diffstat (limited to 'sonar-db')
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/CharsetHandler.java41
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/ColumnDef.java99
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/DatabaseCharsetChecker.java90
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/MssqlCharsetHandler.java153
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/MssqlMetadataReader.java73
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/MysqlCharsetHandler.java91
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/OracleCharsetHandler.java57
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/PostgresCharsetHandler.java93
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/PostgresMetadataReader.java36
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/SqlExecutor.java97
-rw-r--r--sonar-db/src/main/java/org/sonar/db/charset/package-info.java24
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/ColumnDefTest.java51
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/DatabaseCharsetCheckerTest.java110
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/MssqlCharsetHandlerTest.java240
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/MssqlMetadataReaderTest.java51
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/MysqlCharsetHandlerTest.java99
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/OracleCharsetHandlerTest.java109
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/PostgresCharsetHandlerTest.java134
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/PostgresMetadataReaderTest.java52
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/SelectExecutorTest.java56
-rw-r--r--sonar-db/src/test/java/org/sonar/db/charset/SqlExecutorTest.java83
21 files changed, 0 insertions, 1839 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
deleted file mode 100644
index de27d9925e0..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/CharsetHandler.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-abstract class CharsetHandler {
-
- protected static final String UTF8 = "utf8";
-
- private final SqlExecutor selectExecutor;
-
- protected CharsetHandler(SqlExecutor selectExecutor) {
- this.selectExecutor = selectExecutor;
- }
-
- abstract void handle(Connection connection, DatabaseCharsetChecker.State state) throws SQLException;
-
- protected SqlExecutor getSqlExecutor() {
- return selectExecutor;
- }
-
-}
diff --git a/sonar-db/src/main/java/org/sonar/db/charset/ColumnDef.java b/sonar-db/src/main/java/org/sonar/db/charset/ColumnDef.java
deleted file mode 100644
index c2c20f176bf..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/ColumnDef.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Locale;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.db.version.DatabaseVersion;
-
-/**
- * Result of standard SQL command "select * from INFORMATION_SCHEMA" (columns listed in {@link #SELECT_COLUMNS}).
- */
-@Immutable
-public class ColumnDef {
-
- public static final String SELECT_COLUMNS = "select table_name, column_name, character_set_name, collation_name, data_type, character_maximum_length, is_nullable ";
-
- private final String table;
- private final String column;
- private final String charset;
- private final String collation;
- private final String dataType;
- private final long size;
- private final boolean nullable;
-
- public ColumnDef(String table, String column, String charset, String collation, String dataType, long size, boolean nullable) {
- this.table = table;
- this.column = column;
- this.charset = charset;
- this.collation = collation;
- this.dataType = dataType;
- this.size = size;
- this.nullable = nullable;
- }
-
- public String getTable() {
- return table;
- }
-
- public String getColumn() {
- return column;
- }
-
- public String getCharset() {
- return charset;
- }
-
- public String getCollation() {
- return collation;
- }
-
- public String getDataType() {
- return dataType;
- }
-
- public long getSize() {
- return size;
- }
-
- public boolean isNullable() {
- return nullable;
- }
-
- public boolean isInSonarQubeTable() {
- String tableName = table.toLowerCase(Locale.ENGLISH);
- return DatabaseVersion.TABLES.contains(tableName) || DatabaseVersion.OLD_DROPPED_TABLES.contains(tableName);
- }
-
- public enum ColumnDefRowConverter implements SqlExecutor.RowConverter<ColumnDef> {
- INSTANCE;
-
- @Override
- public ColumnDef convert(ResultSet rs) throws SQLException {
- String nullableText = rs.getString(7);
- boolean nullable = "YES".equalsIgnoreCase(nullableText);
-
- return new ColumnDef(
- rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getLong(6), nullable);
- }
- }
-}
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
deleted file mode 100644
index 5a91685dc2e..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/DatabaseCharsetChecker.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.db.charset;
-
-import com.google.common.annotations.VisibleForTesting;
-import java.sql.Connection;
-import java.sql.SQLException;
-import javax.annotation.CheckForNull;
-import org.sonar.db.Database;
-import org.sonar.db.dialect.Dialect;
-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 org.sonar.db.dialect.PostgreSql;
-
-/**
- * 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
- * case-sensitive.
- *
- * See SONAR-6171 and SONAR-7549
- */
-public class DatabaseCharsetChecker {
-
- public enum State {
- FRESH_INSTALL, UPGRADE, STARTUP
- }
-
- private final Database db;
- private final SqlExecutor sqlExecutor;
-
- public DatabaseCharsetChecker(Database db) {
- this(db, new SqlExecutor());
- }
-
- @VisibleForTesting
- DatabaseCharsetChecker(Database db, SqlExecutor sqlExecutor) {
- this.db = db;
- this.sqlExecutor = sqlExecutor;
- }
-
- public void check(State state) {
- try (Connection connection = db.getDataSource().getConnection()) {
- CharsetHandler handler = getHandler(db.getDialect());
- if (handler != null) {
- handler.handle(connection, state);
- }
- } catch (SQLException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @VisibleForTesting
- @CheckForNull
- CharsetHandler getHandler(Dialect dialect) {
- switch (dialect.getId()) {
- case H2.ID:
- // nothing to check
- return null;
- case Oracle.ID:
- return new OracleCharsetHandler(sqlExecutor);
- case PostgreSql.ID:
- return new PostgresCharsetHandler(sqlExecutor, new PostgresMetadataReader(sqlExecutor));
- case MySql.ID:
- return new MysqlCharsetHandler(sqlExecutor);
- case MsSql.ID:
- return new MssqlCharsetHandler(sqlExecutor, new MssqlMetadataReader(sqlExecutor));
- default:
- 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
deleted file mode 100644
index 818714966dc..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/MssqlCharsetHandler.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.db.charset;
-
-import com.google.common.annotations.VisibleForTesting;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.sonar.api.utils.MessageException;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-
-import static java.lang.String.format;
-import static org.apache.commons.lang.StringUtils.containsIgnoreCase;
-
-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";
- private static final String BIN = "BIN";
- private static final String BIN2 = "BIN2";
-
- private final MssqlMetadataReader metadata;
-
- MssqlCharsetHandler(SqlExecutor selectExecutor, MssqlMetadataReader metadataReader) {
- super(selectExecutor);
- this.metadata = metadataReader;
- }
-
- @Override
- void handle(Connection connection, DatabaseCharsetChecker.State state) throws SQLException {
- expectCaseSensitiveDefaultCollation(connection);
- if (state == DatabaseCharsetChecker.State.UPGRADE || state == DatabaseCharsetChecker.State.STARTUP) {
- repairColumns(connection);
- }
- }
-
- private void expectCaseSensitiveDefaultCollation(Connection connection) throws SQLException {
- LOGGER.info("Verify that database collation is case-sensitive and accent-sensitive");
- String defaultCollation = metadata.getDefaultCollation(connection);
-
- if (!isCollationCorrect(defaultCollation)) {
- String fixedCollation = toCaseSensitive(defaultCollation);
- throw MessageException.of(format(
- "Database collation must be case-sensitive and accent-sensitive. It is %s but should be %s.", defaultCollation, fixedCollation));
- }
- }
-
- private void repairColumns(Connection connection) throws SQLException {
- String defaultCollation = metadata.getDefaultCollation(connection);
-
- // All VARCHAR columns are returned. No need to check database general collation.
- // Example of row:
- // issues | kee | Latin1_General_CS_AS or Latin1_General_100_CI_AS_KS_WS
- List<ColumnDef> columns = metadata.getColumnDefs(connection);
- for (ColumnDef column : columns.stream().filter(ColumnDef::isInSonarQubeTable).collect(Collectors.toList())) {
- String collation = column.getCollation();
- if (!isCollationCorrect(collation)) {
- repairColumnCollation(connection, column, toCaseSensitive(collation));
- } else if ("Latin1_General_CS_AS".equals(collation) && !collation.equals(defaultCollation)) {
- repairColumnCollation(connection, column, defaultCollation);
- }
- }
- }
-
- /**
- * Collation is correct if contains {@link #CASE_SENSITIVE_ACCENT_SENSITIVE} or {@link #BIN} or {@link #BIN2}.
- */
- private static boolean isCollationCorrect(String collation) {
- return containsIgnoreCase(collation, CASE_SENSITIVE_ACCENT_SENSITIVE)
- || containsIgnoreCase(collation, BIN)
- || containsIgnoreCase(collation, BIN2);
- }
-
- private void repairColumnCollation(Connection connection, ColumnDef column, String expectedCollation) throws SQLException {
- // 1. select the indices defined on this column
- List<ColumnIndex> indices = metadata.getColumnIndices(connection, column);
-
- // 2. drop indices
- for (ColumnIndex index : indices) {
- getSqlExecutor().executeDdl(connection, format("DROP INDEX %s.%s", column.getTable(), index.name));
- }
-
- // 3. alter collation of column
- String nullability = column.isNullable() ? "NULL" : "NOT NULL";
- String size = column.getSize() >= 0 ? String.valueOf(column.getSize()) : "max";
- String alterSql = format("ALTER TABLE %s ALTER COLUMN %s %s(%s) COLLATE %s %s",
- column.getTable(), column.getColumn(), column.getDataType(), size, expectedCollation, nullability);
- LOGGER.info("Changing collation of column [{}.{}] from {} to {} | sql=", column.getTable(), column.getColumn(), column.getCollation(), expectedCollation, alterSql);
- getSqlExecutor().executeDdl(connection, alterSql);
-
- // 4. re-create indices
- for (ColumnIndex index : indices) {
- String uniqueSql = index.unique ? "UNIQUE" : "";
- String createIndexSql = format("CREATE %s INDEX %s ON %s (%s)", uniqueSql, index.name, column.getTable(), index.csvColumns);
- getSqlExecutor().executeDdl(connection, createIndexSql);
- }
- }
-
- @VisibleForTesting
- static String toCaseSensitive(String collation) {
- // 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 collation
- .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
- static class ColumnIndex {
- private final String name;
- private final boolean unique;
- private final String csvColumns;
-
- public ColumnIndex(String name, boolean unique, String csvColumns) {
- this.name = name;
- this.unique = unique;
- this.csvColumns = csvColumns;
- }
- }
-
- @VisibleForTesting
- enum ColumnIndexConverter implements SqlExecutor.RowConverter<ColumnIndex> {
- INSTANCE;
- @Override
- public ColumnIndex convert(ResultSet rs) throws SQLException {
- return new ColumnIndex(rs.getString(1), rs.getBoolean(2), rs.getString(3));
- }
- }
-
-}
diff --git a/sonar-db/src/main/java/org/sonar/db/charset/MssqlMetadataReader.java b/sonar-db/src/main/java/org/sonar/db/charset/MssqlMetadataReader.java
deleted file mode 100644
index 0f7aa18afe7..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/MssqlMetadataReader.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.List;
-
-import static java.lang.String.format;
-
-public class MssqlMetadataReader {
- private final SqlExecutor sqlExecutor;
-
- public MssqlMetadataReader(SqlExecutor sqlExecutor) {
- this.sqlExecutor = sqlExecutor;
- }
-
- public String getDefaultCollation(Connection connection) throws SQLException {
- return sqlExecutor.selectSingleString(connection, "SELECT CONVERT(VARCHAR, DATABASEPROPERTYEX(DB_NAME(), 'Collation'))");
- }
-
- public List<ColumnDef> getColumnDefs(Connection connection) throws SQLException {
- return sqlExecutor.select(connection,
- ColumnDef.SELECT_COLUMNS +
- "FROM [INFORMATION_SCHEMA].[COLUMNS] " +
- "WHERE collation_name is not null " +
- "ORDER BY table_name,column_name",
- ColumnDef.ColumnDefRowConverter.INSTANCE);
- }
-
- public List<MssqlCharsetHandler.ColumnIndex> getColumnIndices(Connection connection, ColumnDef column) throws SQLException {
- String selectIndicesSql = format("SELECT I.name as index_name, I.is_unique as unik, IndexedColumns " +
- " FROM sys.indexes I " +
- " JOIN sys.tables T ON T.Object_id = I.Object_id " +
- " JOIN (SELECT * FROM ( " +
- " SELECT IC2.object_id, IC2.index_id, " +
- " STUFF((SELECT ' ,' + C.name " +
- " FROM sys.index_columns IC1 " +
- " JOIN sys.columns C " +
- " ON C.object_id = IC1.object_id " +
- " AND C.column_id = IC1.column_id " +
- " AND IC1.is_included_column = 0 " +
- " WHERE IC1.object_id = IC2.object_id " +
- " AND IC1.index_id = IC2.index_id " +
- " GROUP BY IC1.object_id,C.name,index_id " +
- " ORDER BY MAX(IC1.key_ordinal) " +
- " FOR XML PATH('')), 1, 2, '') IndexedColumns " +
- " FROM sys.index_columns IC2 " +
- " GROUP BY IC2.object_id ,IC2.index_id) tmp1 )tmp2 " +
- " ON I.object_id = tmp2.object_id AND I.Index_id = tmp2.index_id " +
- " WHERE I.is_primary_key = 0 AND I.is_unique_constraint = 0 " +
- " and T.name =('%s') " +
- " and CHARINDEX ('%s',IndexedColumns)>0", column.getTable(), column.getColumn());
- return sqlExecutor.select(connection, selectIndicesSql, MssqlCharsetHandler.ColumnIndexConverter.INSTANCE);
- }
-}
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
deleted file mode 100644
index 2190e6a3bb1..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/MysqlCharsetHandler.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-
-import static java.lang.String.format;
-import static org.apache.commons.lang.StringUtils.endsWithIgnoreCase;
-
-class MysqlCharsetHandler extends CharsetHandler {
-
- private static final Logger LOGGER = Loggers.get(MysqlCharsetHandler.class);
- private static final String TYPE_LONGTEXT = "longtext";
-
- MysqlCharsetHandler(SqlExecutor selectExecutor) {
- super(selectExecutor);
- }
-
- @Override
- void handle(Connection connection, DatabaseCharsetChecker.State state) throws SQLException {
- // all the VARCHAR columns have always been created with UTF8 charset on mysql
- // (since SonarQube 2.12 to be precise). The default charset does not require
- // to be UTF8. It is not used. No need to verify it.
- // Still if a column has been accidentally created with a case-insensitive collation,
- // then we can repair it by moving to the same case-sensitive collation. That should
- // never occur.
- if (state == DatabaseCharsetChecker.State.UPGRADE) {
- repairCaseInsensitiveColumns(connection);
- }
- }
-
- private void repairCaseInsensitiveColumns(Connection connection) throws SQLException {
- // All VARCHAR columns are returned. No need to check database general collation.
- // Example of row:
- // issues | kee | utf8 | utf8_bin
- List<ColumnDef> columns = getSqlExecutor().select(connection,
- 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<ColumnDef> invalidColumns = columns.stream()
- .filter(ColumnDef::isInSonarQubeTable)
- .filter(column -> endsWithIgnoreCase(column.getCollation(), "_ci"))
- .collect(Collectors.toList());
- for (ColumnDef column : invalidColumns) {
- repairCaseInsensitiveColumn(connection, column);
- }
- }
-
- private void repairCaseInsensitiveColumn(Connection connection, ColumnDef column)
- throws SQLException {
- String csCollation = toCaseSensitive(column.getCollation());
-
- String nullability = column.isNullable() ? "NULL" : "NOT NULL";
- String type = column.getDataType().equalsIgnoreCase(TYPE_LONGTEXT) ? TYPE_LONGTEXT : format("%s(%d)", column.getDataType(), column.getSize());
- String alterSql = format("ALTER TABLE %s MODIFY %s %s CHARACTER SET '%s' COLLATE '%s' %s",
- column.getTable(), column.getColumn(), type, column.getCharset(), csCollation, nullability);
- LOGGER.info("Changing collation of column [{}.{}] from {} to {} | sql={}", column.getTable(), column.getColumn(), column.getCollation(), csCollation, alterSql);
- getSqlExecutor().executeDdl(connection, alterSql);
- }
-
- private static String toCaseSensitive(String caseInsensitiveCollation) {
- // Example: big5_chinese_ci becomes big5_bin
- // Full list of collations is available with SQL request "show collation"
- return StringUtils.substringBefore(caseInsensitiveCollation, "_") + "_bin";
- }
-}
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
deleted file mode 100644
index 09f855958e6..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/OracleCharsetHandler.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-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;
-
-class OracleCharsetHandler extends CharsetHandler {
-
- OracleCharsetHandler(SqlExecutor selectExecutor) {
- super(selectExecutor);
- }
-
- @Override
- public void handle(Connection connection, DatabaseCharsetChecker.State state) throws SQLException {
- // Charset is a global setting on Oracle, it can't be set on a specified schema with a
- // different value. To not block users who already have a SonarQube schema, charset
- // is verified only on fresh installs but not on upgrades. Let's hope they won't face
- // any errors related to charset if they didn't follow the UTF8 requirement when creating
- // the schema in previous SonarQube versions.
- if (state == DatabaseCharsetChecker.State.FRESH_INSTALL) {
- Loggers.get(getClass()).info("Verify that database charset is UTF8");
- expectUtf8(connection);
- }
- }
-
- private void expectUtf8(Connection connection) throws SQLException {
- // Oracle does not allow to override character set on tables. Only global charset is verified.
- String charset = getSqlExecutor().selectSingleString(connection, "select value from nls_database_parameters where parameter='NLS_CHARACTERSET'");
- String sort = getSqlExecutor().selectSingleString(connection, "select value from nls_database_parameters where parameter='NLS_SORT'");
- if (!containsIgnoreCase(charset, UTF8) || !"BINARY".equalsIgnoreCase(sort)) {
- throw MessageException.of(format("Oracle must be have UTF8 charset and BINARY sort. NLS_CHARACTERSET is %s and NLS_SORT is %s.", charset, sort));
- }
- }
-}
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
deleted file mode 100644
index 69539cfb74e..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/PostgresCharsetHandler.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.db.charset;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
-import java.sql.Connection;
-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.Loggers;
-
-import static java.lang.String.format;
-import static org.apache.commons.lang.StringUtils.containsIgnoreCase;
-import static org.apache.commons.lang.StringUtils.isBlank;
-
-class PostgresCharsetHandler extends CharsetHandler {
-
- private final PostgresMetadataReader metadata;
-
- PostgresCharsetHandler(SqlExecutor selectExecutor, PostgresMetadataReader metadata) {
- super(selectExecutor);
- this.metadata = metadata;
- }
-
- @Override
- void handle(Connection connection, DatabaseCharsetChecker.State state) throws SQLException {
- // PostgreSQL does not have concept of case-sensitive collation. Only charset ("encoding" in postgresql terminology)
- // must be verified.
- expectUtf8AsDefault(connection);
-
- if (state == DatabaseCharsetChecker.State.UPGRADE || state == DatabaseCharsetChecker.State.STARTUP) {
- // no need to check columns on fresh installs... as they are not supposed to exist!
- expectUtf8Columns(connection);
- }
- }
-
- private void expectUtf8AsDefault(Connection connection) throws SQLException {
- Loggers.get(getClass()).info("Verify that database charset supports UTF8");
- String collation = metadata.getDefaultCharset(connection);
- if (!containsIgnoreCase(collation, UTF8)) {
- throw MessageException.of(format("Database charset is %s. It must support UTF8.", collation));
- }
- }
-
- private void expectUtf8Columns(Connection connection) throws SQLException {
- // Charset is defined globally and can be overridden on each column.
- // This request returns all VARCHAR columns. Charset may be empty.
- // Examples:
- // issues | key | ''
- // projects | name | utf8
- List<String[]> rows = getSqlExecutor().select(connection, "select table_name, column_name, collation_name " +
- "from information_schema.columns " +
- "where table_schema='public' " +
- "and udt_name='varchar' " +
- "order by table_name, column_name", new SqlExecutor.StringsConverter(3 /* columns returned by SELECT */));
- Set<String> errors = new LinkedHashSet<>();
- for (String[] row : rows) {
- if (!isBlank(row[2]) && !containsIgnoreCase(row[2], UTF8)) {
- errors.add(format("%s.%s", row[0], row[1]));
- }
- }
-
- if (!errors.isEmpty()) {
- throw MessageException.of(format("Database columns [%s] must have UTF8 charset.", Joiner.on(", ").join(errors)));
- }
- }
-
- @VisibleForTesting
- PostgresMetadataReader getMetadata() {
- return metadata;
- }
-
-}
diff --git a/sonar-db/src/main/java/org/sonar/db/charset/PostgresMetadataReader.java b/sonar-db/src/main/java/org/sonar/db/charset/PostgresMetadataReader.java
deleted file mode 100644
index eecca8d1c01..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/PostgresMetadataReader.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-public class PostgresMetadataReader {
-
- private final SqlExecutor sqlExecutor;
-
- public PostgresMetadataReader(SqlExecutor sqlExecutor) {
- this.sqlExecutor = sqlExecutor;
- }
-
- public String getDefaultCharset(Connection connection) throws SQLException {
- return sqlExecutor.selectSingleString(connection, "select pg_encoding_to_char(encoding) from pg_database where datname = current_database()");
- }
-}
diff --git a/sonar-db/src/main/java/org/sonar/db/charset/SqlExecutor.java b/sonar-db/src/main/java/org/sonar/db/charset/SqlExecutor.java
deleted file mode 100644
index f9e56cd7596..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/SqlExecutor.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.List;
-import javax.annotation.CheckForNull;
-import org.sonar.db.DatabaseUtils;
-
-public class SqlExecutor {
-
- public <T> List<T> select(Connection connection, String sql, RowConverter<T> rowConverter) throws SQLException {
- PreparedStatement stmt = null;
- ResultSet rs = null;
- try {
- stmt = connection.prepareStatement(sql);
- rs = stmt.executeQuery();
- List<T> result = new ArrayList<>();
- while (rs.next()) {
- result.add(rowConverter.convert(rs));
- }
- return result;
-
- } finally {
- DatabaseUtils.closeQuietly(rs);
- DatabaseUtils.closeQuietly(stmt);
- }
- }
-
- public void executeDdl(Connection connection, String sql) throws SQLException {
- try (Statement stmt = connection.createStatement()) {
- stmt.execute(sql);
- }
- }
-
- @CheckForNull
- public final String selectSingleString(Connection connection, String sql) throws SQLException {
- String[] cols = selectSingleRow(connection, sql, new SqlExecutor.StringsConverter(1));
- return cols == null ? null : cols[0];
- }
-
- @CheckForNull
- public final <T> T selectSingleRow(Connection connection, String sql, SqlExecutor.RowConverter<T> rowConverter) throws SQLException {
- List<T> rows = select(connection, sql, rowConverter);
- if (rows.isEmpty()) {
- return null;
- }
- if (rows.size() == 1) {
- return rows.get(0);
- }
- throw new IllegalStateException("Expecting only one result for [" + sql + "]");
- }
-
- @FunctionalInterface
- public interface RowConverter<T> {
- T convert(ResultSet rs) throws SQLException;
- }
-
- public static class StringsConverter implements RowConverter<String[]> {
- private final int nbColumns;
-
- public StringsConverter(int nbColumns) {
- this.nbColumns = nbColumns;
- }
-
- @Override
- public String[] convert(ResultSet rs) throws SQLException {
- String[] row = new String[nbColumns];
- for (int i = 0; i < nbColumns; i++) {
- row[i] = DatabaseUtils.getString(rs, i + 1);
- }
- return row;
- }
- }
-}
diff --git a/sonar-db/src/main/java/org/sonar/db/charset/package-info.java b/sonar-db/src/main/java/org/sonar/db/charset/package-info.java
deleted file mode 100644
index 25a4faf160e..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/charset/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.db.charset;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
diff --git a/sonar-db/src/test/java/org/sonar/db/charset/ColumnDefTest.java b/sonar-db/src/test/java/org/sonar/db/charset/ColumnDefTest.java
deleted file mode 100644
index 55b02e0ea1e..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/ColumnDefTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.db.charset;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ColumnDefTest {
-
- @Test
- public void isInSonarQubeTable_returns_false_if_sqlazure_system_table() {
- ColumnDef underTest = new ColumnDef("sys.sysusers", "login", "charset", "collation", "NVARCHAR", 100L, false);
- assertThat(underTest.isInSonarQubeTable()).isFalse();
-
- underTest = new ColumnDef("SYS.SYSUSERS", "login", "charset", "collation", "NVARCHAR", 100L, false);
- assertThat(underTest.isInSonarQubeTable()).isFalse();
- }
-
- @Test
- public void isInSonarQubeTable_returns_true_if_table_created_by_sonarqube() {
- ColumnDef underTest = new ColumnDef("project_measures", "text_value", "charset", "collation", "NVARCHAR", 100L, false);
- assertThat(underTest.isInSonarQubeTable()).isTrue();
-
- underTest = new ColumnDef("PROJECT_MEASURES", "text_value", "charset", "collation", "NVARCHAR", 100L, false);
- assertThat(underTest.isInSonarQubeTable()).isTrue();
- }
-
- @Test
- public void isInSonarQubeTable_returns_true_if_table_existed_in_previous_versions_of_sonarqube() {
- ColumnDef underTest = new ColumnDef("activities", "kee", "charset", "collation", "NVARCHAR", 100L, false);
- assertThat(underTest.isInSonarQubeTable()).isTrue();
- }
-}
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
deleted file mode 100644
index d707ff9ee0b..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/DatabaseCharsetCheckerTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.mockito.Mockito;
-import org.sonar.db.Database;
-import org.sonar.db.dialect.Dialect;
-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 org.sonar.db.dialect.PostgreSql;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-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;
-
-public class DatabaseCharsetCheckerTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private Database db = mock(Database.class, Mockito.RETURNS_MOCKS);
- private CharsetHandler handler = mock(CharsetHandler.class);
- private DatabaseCharsetChecker underTest = spy(new DatabaseCharsetChecker(db));
-
- @Test
- public void executes_handler() throws Exception {
- Oracle dialect = new Oracle();
- when(underTest.getHandler(dialect)).thenReturn(handler);
- when(db.getDialect()).thenReturn(dialect);
-
- underTest.check(DatabaseCharsetChecker.State.UPGRADE);
- verify(handler).handle(any(Connection.class), eq(DatabaseCharsetChecker.State.UPGRADE));
- }
-
- @Test
- public void throws_ISE_if_handler_fails() throws Exception {
- 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), any(DatabaseCharsetChecker.State.class));
-
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("failure");
- underTest.check(DatabaseCharsetChecker.State.UPGRADE);
- }
-
- @Test
- public void does_nothing_if_h2() throws Exception {
- assertThat(underTest.getHandler(new H2())).isNull();
- }
-
- @Test
- public void getHandler_returns_MysqlCharsetHandler_if_mysql() throws Exception {
- assertThat(underTest.getHandler(new MySql())).isInstanceOf(MysqlCharsetHandler.class);
- }
-
- @Test
- public void getHandler_returns_MssqlCharsetHandler_if_mssql() throws Exception {
- assertThat(underTest.getHandler(new MsSql())).isInstanceOf(MssqlCharsetHandler.class);
- }
-
- @Test
- public void getHandler_returns_OracleCharsetHandler_if_oracle() throws Exception {
- assertThat(underTest.getHandler(new Oracle())).isInstanceOf(OracleCharsetHandler.class);
- }
-
- @Test
- public void getHandler_returns_PostgresCharsetHandler_if_postgres() throws Exception {
- assertThat(underTest.getHandler(new PostgreSql())).isInstanceOf(PostgresCharsetHandler.class);
- }
-
- @Test
- public void getHandler_throws_IAE_if_unsupported_db() throws Exception {
- Dialect unsupportedDialect = mock(Dialect.class);
- when(unsupportedDialect.getId()).thenReturn("foo");
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Database not supported: foo");
- underTest.getHandler(unsupportedDialect);
- }
-}
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
deleted file mode 100644
index 13e9dd78c7e..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/MssqlCharsetHandlerTest.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * 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.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.List;
-import java.util.stream.Stream;
-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 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.Matchers.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(DataProviderRunner.class)
-public class MssqlCharsetHandlerTest {
-
- private static final String TABLE_ISSUES = "issues";
- private static final String TABLE_PROJECTS = "projects";
- private static final String COLUMN_KEE = "kee";
- private static final String COLUMN_NAME = "name";
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private SqlExecutor sqlExecutor = mock(SqlExecutor.class);
- private MssqlMetadataReader metadata = mock(MssqlMetadataReader.class);
- private MssqlCharsetHandler underTest = new MssqlCharsetHandler(sqlExecutor, metadata);
- private Connection connection = mock(Connection.class);
-
- @Test
- public void fresh_install_verifies_that_default_collation_is_CS_AS() throws SQLException {
- answerDefaultCollation("Latin1_General_CS_AS");
-
- underTest.handle(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
-
- verify(metadata).getDefaultCollation(connection);
- }
-
- @Test
- public void fresh_install_fails_if_default_collation_is_not_CS_AS() throws SQLException {
- answerDefaultCollation("Latin1_General_CI_AI");
-
- expectedException.expect(MessageException.class);
- expectedException.expectMessage("Database collation must be case-sensitive and accent-sensitive. It is Latin1_General_CI_AI but should be Latin1_General_CS_AS.");
- underTest.handle(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
- }
-
- @Test
- public void upgrade_fails_if_default_collation_is_not_CS_AS() throws SQLException {
- answerDefaultCollation("Latin1_General_CI_AI");
-
- expectedException.expect(MessageException.class);
- expectedException.expectMessage("Database collation must be case-sensitive and accent-sensitive. It is Latin1_General_CI_AI but should be Latin1_General_CS_AS.");
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
- }
-
- @Test
- public void upgrade_checks_that_columns_are_CS_AS() throws SQLException {
- answerDefaultCollation("Latin1_General_CS_AS");
- answerColumnDefs(
- 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));
-
- // do not fail
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
- }
-
- @Test
- public void upgrade_repairs_CI_AI_columns() throws SQLException {
- answerDefaultCollation("Latin1_General_CS_AS");
- answerColumnDefs(
- 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));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor).executeDdl(connection, "ALTER TABLE projects ALTER COLUMN name varchar(10) COLLATE Latin1_General_CS_AS NOT NULL");
- }
-
- @Test
- public void upgrade_repairs_indexed_CI_AI_columns() throws SQLException {
- answerDefaultCollation("Latin1_General_CS_AS");
- answerColumnDefs(
- 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));
- answerIndices(
- new MssqlCharsetHandler.ColumnIndex("projects_name", false, "name"),
- // This index is on two columns. Note that it does not make sense for table "projects" !
- new MssqlCharsetHandler.ColumnIndex("projects_login_and_name", true, "login,name"));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor).executeDdl(connection, "DROP INDEX projects.projects_name");
- verify(sqlExecutor).executeDdl(connection, "DROP INDEX projects.projects_login_and_name");
- verify(sqlExecutor).executeDdl(connection, "ALTER TABLE projects ALTER COLUMN name varchar(10) COLLATE Latin1_General_CS_AS NOT NULL");
- verify(sqlExecutor).executeDdl(connection, "CREATE INDEX projects_name ON projects (name)");
- verify(sqlExecutor).executeDdl(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 {
- answerDefaultCollation("Latin1_General_CS_AS");
- answerColumnDefs(new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "Latin1_General", collation, "varchar", 10, false));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor).executeDdl(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 sensitivity : asList("CI_AI", "CI_AS", "CS_AI")) {
- for (String suffix : asList("", "_KS_WS")) {
- res.add(new String[] {
- format("Latin1_General_%s%s", sensitivity, 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 {
- answerDefaultCollation("Latin1_General_CS_AS");
- // returned size is -1
- answerColumnDefs(new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", "Latin1_General_CI_AI", "nvarchar", -1, false));
- answerIndices();
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor).executeDdl(connection, "ALTER TABLE projects ALTER COLUMN name nvarchar(max) COLLATE Latin1_General_CS_AS NOT NULL");
- }
-
- @Test
- public void do_not_repair_system_tables_of_sql_azure() throws Exception {
- answerDefaultCollation("Latin1_General_CS_AS");
- answerColumnDefs(new ColumnDef("sys.sysusers", COLUMN_NAME, "Latin1_General", "Latin1_General_CI_AI", "varchar", 10, false));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor, never()).executeDdl(any(Connection.class), anyString());
- }
-
- @Test
- @UseDataProvider("combinationOfBinAndSuffix")
- public void do_not_repair_if_collation_contains_BIN(String collation) throws Exception {
- answerDefaultCollation("Latin1_General_CS_AS");
- answerColumnDefs(new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", collation, "varchar", 10, false));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor, never()).executeDdl(any(Connection.class), anyString());
- }
-
- @DataProvider
- public static Object[][] combinationOfBinAndSuffix() {
- return Stream.of("", "_KS_WS")
- .map(suffix -> new String[] {format("Latin1_General_BIN%s", suffix)})
- .toArray(Object[][]::new);
- }
-
- @Test
- @UseDataProvider("combinationOfBin2AndSuffix")
- public void do_not_repair_if_collation_contains_BIN2(String collation) throws Exception {
- answerDefaultCollation("Latin1_General_CS_AS");
- answerColumnDefs(new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", collation, "varchar", 10, false));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor, never()).executeDdl(any(Connection.class), anyString());
- }
-
- @DataProvider
- public static Object[][] combinationOfBin2AndSuffix() {
- return Stream.of("", "_KS_WS")
- .map(suffix -> new String[] {format("Latin1_General_BIN2%s", suffix)})
- .toArray(Object[][]::new);
- }
-
- /**
- * SONAR-7988
- */
- @Test
- public void fix_Latin1_CS_AS_columns_created_in_5_x() throws SQLException {
- answerDefaultCollation("SQL_Latin1_General_CP1_CS_AS");
- answerColumnDefs(new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", "Latin1_General_CS_AS", "nvarchar", 10, false));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor).executeDdl(connection, "ALTER TABLE projects ALTER COLUMN name nvarchar(10) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
- }
-
- private void answerColumnDefs(ColumnDef... columnDefs) throws SQLException {
- when(metadata.getColumnDefs(connection)).thenReturn(asList(columnDefs));
- }
-
- private void answerDefaultCollation(String defaultCollation) throws SQLException {
- when(metadata.getDefaultCollation(connection)).thenReturn(defaultCollation);
- }
-
- private void answerIndices(MssqlCharsetHandler.ColumnIndex... indices) throws SQLException {
- when(metadata.getColumnIndices(same(connection), any(ColumnDef.class))).thenReturn(asList(indices));
- }
-}
diff --git a/sonar-db/src/test/java/org/sonar/db/charset/MssqlMetadataReaderTest.java b/sonar-db/src/test/java/org/sonar/db/charset/MssqlMetadataReaderTest.java
deleted file mode 100644
index 3901c201f68..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/MssqlMetadataReaderTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.List;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class MssqlMetadataReaderTest {
-
- private SqlExecutor sqlExecutor = mock(SqlExecutor.class);
- private Connection connection = mock(Connection.class);
- private MssqlMetadataReader underTest = new MssqlMetadataReader(sqlExecutor);
-
- @Test
- public void test_getDefaultCollation() throws SQLException {
- answerSelect(Arrays.<String[]>asList(new String[] {"Latin1_General_CS_AS"}));
-
- assertThat(underTest.getDefaultCollation(connection)).isEqualTo("Latin1_General_CS_AS");
- }
-
- private void answerSelect(List<String[]> firstRequest) throws SQLException {
- when(sqlExecutor.select(same(connection), anyString(), any(SqlExecutor.StringsConverter.class))).thenReturn(firstRequest);
- }
-}
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
deleted file mode 100644
index 8fc85450221..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/MysqlCharsetHandlerTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static java.util.Arrays.asList;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class MysqlCharsetHandlerTest {
-
- private static final String TABLE_ISSUES = "issues";
- private static final String TABLE_PROJECTS = "projects";
- private static final String COLUMN_KEE = "kee";
- private static final String COLUMN_NAME = "name";
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private SqlExecutor sqlExecutor = mock(SqlExecutor.class);
- private Connection connection = mock(Connection.class);
- private MysqlCharsetHandler underTest = new MysqlCharsetHandler(sqlExecutor);
-
- @Test
- public void upgrade_verifies_that_columns_are_utf8_and_case_sensitive() throws Exception {
- answerColumnDef(
- 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(connection, DatabaseCharsetChecker.State.UPGRADE);
- }
-
- @Test
- public void fresh_install_does_not_verify_anything() throws Exception {
- underTest.handle(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
- verifyZeroInteractions(sqlExecutor);
- }
-
- @Test
- public void regular_startup_does_not_verify_anything() throws Exception {
- underTest.handle(connection, DatabaseCharsetChecker.State.STARTUP);
- verifyZeroInteractions(sqlExecutor);
- }
-
- @Test
- public void repair_case_insensitive_column() throws Exception {
- answerColumnDef(
- new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "big5_chinese", "big5_chinese_ci", "varchar", 10, false),
- new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "latin1", "latin1_swedish_ci", "varchar", 10, false));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor).executeDdl(connection, "ALTER TABLE issues MODIFY kee varchar(10) CHARACTER SET 'big5_chinese' COLLATE 'big5_bin' NOT NULL");
- verify(sqlExecutor).executeDdl(connection, "ALTER TABLE projects MODIFY name varchar(10) CHARACTER SET 'latin1' COLLATE 'latin1_bin' NOT NULL");
- }
-
- @Test
- public void size_should_be_ignored_on_longtext_column() throws Exception {
- answerColumnDef(
- new ColumnDef(TABLE_ISSUES, COLUMN_KEE, "latin1", "latin1_german1_ci", "longtext", 4_294_967_295L, false));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verify(sqlExecutor).executeDdl(connection, "ALTER TABLE " + TABLE_ISSUES + " MODIFY " + COLUMN_KEE + " longtext CHARACTER SET 'latin1' COLLATE 'latin1_bin' NOT NULL");
- }
-
- private void answerColumnDef(ColumnDef... columnDefs) throws SQLException {
- when(sqlExecutor.select(any(Connection.class), anyString(), eq(ColumnDef.ColumnDefRowConverter.INSTANCE)))
- .thenReturn(asList(columnDefs));
- }
-}
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
deleted file mode 100644
index f0474c2475b..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/OracleCharsetHandlerTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-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 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.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class OracleCharsetHandlerTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private SqlExecutor sqlExecutor = mock(SqlExecutor.class);
- private Connection connection = mock(Connection.class);
- private OracleCharsetHandler underTest = new OracleCharsetHandler(sqlExecutor);
-
- @Test
- public void fresh_install_verifies_utf8_charset() throws Exception {
- answerSql(singletonList(new String[] {"UTF8"}), singletonList(new String[] {"BINARY"}));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
- }
-
- @Test
- public void upgrade_does_not_verify_utf8_charset() throws Exception {
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
-
- verifyZeroInteractions(sqlExecutor);
- }
-
- @Test
- public void fresh_install_supports_al32utf8() throws Exception {
- answerSql(
- singletonList(new String[] {"AL32UTF8"}), singletonList(new String[] {"BINARY"}));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
- }
-
- @Test
- public void fresh_install_fails_if_charset_is_not_utf8() throws Exception {
- answerSql(
- singletonList(new String[] {"LATIN"}), singletonList(new String[] {"BINARY"}));
-
- 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(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
- }
-
- @Test
- public void fresh_install_fails_if_not_case_sensitive() throws Exception {
- answerSql(
- singletonList(new String[] {"UTF8"}), singletonList(new String[] {"LINGUISTIC"}));
-
- 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(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
- }
-
- @Test
- public void fails_if_can_not_get_charset() throws Exception {
- answerSql(Collections.emptyList(), Collections.emptyList());
-
- expectedException.expect(MessageException.class);
-
- underTest.handle(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
- }
-
- @Test
- public void does_nothing_if_regular_startup() throws Exception {
- underTest.handle(connection, DatabaseCharsetChecker.State.STARTUP);
- verifyZeroInteractions(sqlExecutor);
- }
-
- private void answerSql(List<String[]> firstRequest, List<String[]>... otherRequests) throws SQLException {
- when(sqlExecutor.select(any(Connection.class), anyString(), any(SqlExecutor.StringsConverter.class))).thenReturn(firstRequest, otherRequests);
- }
-}
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
deleted file mode 100644
index 69f0fb76720..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/PostgresCharsetHandlerTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Arrays;
-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 java.util.Arrays.asList;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class PostgresCharsetHandlerTest {
-
- private static final String TABLE_ISSUES = "issues";
- private static final String TABLE_PROJECTS = "projects";
- private static final String COLUMN_KEE = "kee";
- private static final String COLUMN_NAME = "name";
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private SqlExecutor sqlExecutor = mock(SqlExecutor.class);
- private Connection connection = mock(Connection.class);
- private PostgresMetadataReader metadata = mock(PostgresMetadataReader.class);
- private PostgresCharsetHandler underTest = new PostgresCharsetHandler(sqlExecutor, metadata);
-
- @Test
- public void fresh_install_verifies_that_default_charset_is_utf8() throws SQLException {
- answerDefaultCharset("utf8");
-
- underTest.handle(connection, DatabaseCharsetChecker.State.FRESH_INSTALL);
- // no errors, charset has been verified
- verify(metadata).getDefaultCharset(same(connection));
- verifyZeroInteractions(sqlExecutor);
- }
-
- @Test
- public void upgrade_verifies_that_default_charset_and_columns_are_utf8() throws Exception {
- answerDefaultCharset("utf8");
- answerColumns(asList(
- new String[] {TABLE_ISSUES, COLUMN_KEE, "utf8"},
- new String[] {TABLE_PROJECTS, COLUMN_NAME, "utf8"}));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
- // no errors, charsets have been verified
- verify(metadata).getDefaultCharset(same(connection));
- }
-
- @Test
- public void regular_startup_verifies_that_default_charset_and_columns_are_utf8() throws Exception {
- answerDefaultCharset("utf8");
- answerColumns(asList(
- new String[] {TABLE_ISSUES, COLUMN_KEE, "utf8"},
- new String[] {TABLE_PROJECTS, COLUMN_NAME, "utf8"}));
-
- underTest.handle(connection, DatabaseCharsetChecker.State.STARTUP);
- // no errors, charsets have been verified
- verify(metadata).getDefaultCharset(same(connection));
- }
-
- @Test
- public void column_charset_can_be_empty() throws Exception {
- answerDefaultCharset("utf8");
- answerColumns(asList(
- new String[] {TABLE_ISSUES, COLUMN_KEE, "utf8"},
- new String[] {TABLE_PROJECTS, COLUMN_NAME, "" /* unset -> uses db collation */}));
-
- // no error
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
- }
-
- @Test
- public void upgrade_fails_if_non_utf8_column() throws Exception {
- // default charset is ok but two columns are not
- answerDefaultCharset("utf8");
- answerColumns(asList(
- new String[] {TABLE_ISSUES, COLUMN_KEE, "utf8"},
- new String[] {TABLE_PROJECTS, COLUMN_KEE, "latin"},
- new String[] {TABLE_PROJECTS, COLUMN_NAME, "latin"}));
-
- expectedException.expect(MessageException.class);
- expectedException.expectMessage("Database columns [projects.kee, projects.name] must have UTF8 charset.");
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
- }
-
- @Test
- public void upgrade_fails_if_default_charset_is_not_utf8() throws Exception {
- answerDefaultCharset("latin");
- answerColumns(
- Arrays.<String[]>asList(new String[] {TABLE_ISSUES, COLUMN_KEE, "utf8"}));
-
- expectedException.expect(MessageException.class);
- expectedException.expectMessage("Database charset is latin. It must support UTF8.");
-
- underTest.handle(connection, DatabaseCharsetChecker.State.UPGRADE);
- }
-
- private void answerDefaultCharset(String defaultCollation) throws SQLException {
- when(metadata.getDefaultCharset(same(connection))).thenReturn(defaultCollation);
- }
-
- private void answerColumns(List<String[]> firstRequest) throws SQLException {
- when(sqlExecutor.select(same(connection), anyString(), any(SqlExecutor.StringsConverter.class))).thenReturn(firstRequest);
- }
-}
diff --git a/sonar-db/src/test/java/org/sonar/db/charset/PostgresMetadataReaderTest.java b/sonar-db/src/test/java/org/sonar/db/charset/PostgresMetadataReaderTest.java
deleted file mode 100644
index edcb4a823e9..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/PostgresMetadataReaderTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.List;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class PostgresMetadataReaderTest {
-
- private SqlExecutor sqlExecutor = mock(SqlExecutor.class);
- private Connection connection = mock(Connection.class);
- private PostgresMetadataReader underTest = new PostgresMetadataReader(sqlExecutor);
-
- @Test
- public void test_getDefaultCharset() throws SQLException {
- answerSelect(Arrays.<String[]>asList(new String[] {"latin"}));
-
- assertThat(underTest.getDefaultCharset(connection)).isEqualTo("latin");
- }
-
- private void answerSelect(List<String[]> firstRequest) throws SQLException {
- when(sqlExecutor.select(same(connection), anyString(), any(SqlExecutor.StringsConverter.class))).thenReturn(firstRequest);
- }
-
-}
diff --git a/sonar-db/src/test/java/org/sonar/db/charset/SelectExecutorTest.java b/sonar-db/src/test/java/org/sonar/db/charset/SelectExecutorTest.java
deleted file mode 100644
index bdcee828c2a..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/SelectExecutorTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.util.List;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.user.UserDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class SelectExecutorTest {
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- SqlExecutor underTest = new SqlExecutor();
-
- @Test
- public void testExecuteQuery() throws Exception {
- DbSession session = dbTester.getSession();
- dbTester.getDbClient().userDao().insert(session, new UserDto().setLogin("him").setName("Him"));
- dbTester.getDbClient().userDao().insert(session, new UserDto().setLogin("her").setName("Her"));
- session.commit();
-
- try (Connection connection = dbTester.openConnection()) {
- List<String[]> rows = underTest.select(connection, "select login, name from users order by login", new SqlExecutor.StringsConverter(2));
- assertThat(rows).hasSize(2);
- assertThat(rows.get(0)[0]).isEqualTo("her");
- assertThat(rows.get(0)[1]).isEqualTo("Her");
- assertThat(rows.get(1)[0]).isEqualTo("him");
- assertThat(rows.get(1)[1]).isEqualTo("Him");
- }
- }
-}
diff --git a/sonar-db/src/test/java/org/sonar/db/charset/SqlExecutorTest.java b/sonar-db/src/test/java/org/sonar/db/charset/SqlExecutorTest.java
deleted file mode 100644
index c9ac2e5594a..00000000000
--- a/sonar-db/src/test/java/org/sonar/db/charset/SqlExecutorTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.db.charset;
-
-import java.sql.Connection;
-import java.util.List;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbTester;
-import org.sonar.db.dialect.H2;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assume.assumeTrue;
-
-public class SqlExecutorTest {
-
- private static final String LOGIN_DB_COLUMN = "login";
- private static final String NAME_DB_COLUMN = "name";
- private static final String USERS_DB_TABLE = "users";
- private static final String IS_ROOT_DB_COLUMN = "is_root";
-
- SqlExecutor underTest = new SqlExecutor();
-
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
- @Before
- public void disableIfNotH2() {
- // TODO dbTester.selectFirst() returns keys with different case
- // depending on target db (lower-case for MySQL but upper-case for H2).
- // It has to be fixed in order to reactive this test for all dbs.
- assumeTrue(dbTester.database().getDialect().getId().equals(H2.ID));
- }
-
- @Test
- public void executeSelect_executes_PreparedStatement() throws Exception {
- dbTester.executeInsert(USERS_DB_TABLE, LOGIN_DB_COLUMN, "login1", NAME_DB_COLUMN, "name one", IS_ROOT_DB_COLUMN, false);
- dbTester.executeInsert(USERS_DB_TABLE, LOGIN_DB_COLUMN, "login2", NAME_DB_COLUMN, "name two", IS_ROOT_DB_COLUMN, false);
-
- try (Connection connection = dbTester.openConnection()) {
- List<String[]> users = underTest.select(connection, "select " + LOGIN_DB_COLUMN + ", " + NAME_DB_COLUMN + " from users order by id", new SqlExecutor.StringsConverter(
- 2));
- assertThat(users).hasSize(2);
- assertThat(users.get(0)[0]).isEqualTo("login1");
- assertThat(users.get(0)[1]).isEqualTo("name one");
- assertThat(users.get(1)[0]).isEqualTo("login2");
- assertThat(users.get(1)[1]).isEqualTo("name two");
- }
- }
-
- @Test
- public void executeUpdate_executes_PreparedStatement() throws Exception {
- dbTester.executeInsert(USERS_DB_TABLE, LOGIN_DB_COLUMN, "the_login", NAME_DB_COLUMN, "the name", IS_ROOT_DB_COLUMN, false);
-
- try (Connection connection = dbTester.openConnection()) {
- underTest.executeDdl(connection, "update users set " + NAME_DB_COLUMN + "='new name' where " + LOGIN_DB_COLUMN + "='the_login'");
- }
- Map<String, Object> row = dbTester.selectFirst("select " + NAME_DB_COLUMN + " from users where " + LOGIN_DB_COLUMN + "='the_login'");
- assertThat(row).isNotEmpty();
- assertThat(row.get("NAME")).isEqualTo("new name");
- }
-
-}