1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
/*
* 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 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.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.db.charset.DatabaseCharsetChecker.Flag.AUTO_REPAIR_COLLATION;
public class MssqlCharsetHandlerTest {
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();
SqlExecutor selectExecutor = mock(SqlExecutor.class);
MssqlCharsetHandler underTest = new MssqlCharsetHandler(selectExecutor);
@Test
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), Collections.<DatabaseCharsetChecker.Flag>emptySet());
}
@Test
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, 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 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)));
answerIndices(asList(
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")));
Connection connection = mock(Connection.class);
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");
verify(selectExecutor).executeUpdate(connection, "ALTER TABLE projects ALTER COLUMN name varchar(10) COLLATE Latin1_General_CS_AS NOT NULL");
verify(selectExecutor).executeUpdate(connection, "CREATE INDEX projects_name ON projects (name)");
verify(selectExecutor).executeUpdate(connection, "CREATE UNIQUE INDEX projects_login_and_name ON projects (login,name)");
}
@Test
public void support_the_max_size_of_varchar_column() throws Exception {
// returned size is -1
answerColumns(asList(new ColumnDef(TABLE_PROJECTS, COLUMN_NAME, "Latin1_General", "Latin1_General_CI_AI", "nvarchar", -1, false)));
answerIndices(Collections.<MssqlCharsetHandler.ColumnIndex>emptyList());
Connection connection = mock(Connection.class);
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");
}
@Test
public void do_not_repair_system_tables_of_sql_azure() throws Exception {
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, immutableEnumSet(AUTO_REPAIR_COLLATION));
verify(selectExecutor, never()).executeUpdate(any(Connection.class), anyString());
}
private void answerColumns(List<ColumnDef> columnDefs) throws SQLException {
when(selectExecutor.executeSelect(any(Connection.class), anyString(), eq(ColumnDef.ColumnDefRowConverter.INSTANCE))).thenReturn(columnDefs);
}
private void answerIndices(List<MssqlCharsetHandler.ColumnIndex> indices) throws SQLException {
when(selectExecutor.executeSelect(any(Connection.class), anyString(), eq(MssqlCharsetHandler.ColumnIndexConverter.INSTANCE))).thenReturn(indices);
}
}
|