From d5444a85336112e5d2eebd3eeaee88a93092fb26 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 17 Sep 2015 14:49:15 +0200 Subject: [PATCH] SONAR-6840 Extract ColumnDef from AddColumnsBuilder And add a check when no column has been defined --- .../sonar/db/version/AddColumnsBuilder.java | 56 +------- .../java/org/sonar/db/version/ColumnDef.java | 90 +++++++++++++ .../db/version/v51/AddIssuesColumns.java | 25 ++-- .../AddManualMeasuresComponentUuidColumn.java | 5 +- .../db/version/AddColumnsBuilderTest.java | 120 ++++++------------ .../org/sonar/db/version/ColumnDefTest.java | 92 ++++++++++++++ 6 files changed, 244 insertions(+), 144 deletions(-) create mode 100644 sonar-db/src/main/java/org/sonar/db/version/ColumnDef.java create mode 100644 sonar-db/src/test/java/org/sonar/db/version/ColumnDefTest.java diff --git a/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java b/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java index ab6543fffe7..2bfb7653545 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java +++ b/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java @@ -19,11 +19,7 @@ */ package org.sonar.db.version; -import com.google.common.base.CharMatcher; -import com.google.common.base.Preconditions; import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; import org.sonar.db.dialect.Dialect; import org.sonar.db.dialect.MsSql; import org.sonar.db.dialect.Oracle; @@ -48,6 +44,10 @@ public class AddColumnsBuilder { } public String build() { + if (columnDefs.isEmpty()) { + throw new IllegalStateException("No column has been defined"); + } + StringBuilder sql = new StringBuilder().append("ALTER TABLE ").append(tableName).append(" "); switch (dialect.getId()) { case PostgreSql.ID: @@ -95,52 +95,4 @@ public class AddColumnsBuilder { } } - public static class ColumnDef { - private String name; - private Type type; - private boolean isNullable; - private Integer limit; - - public enum Type { - STRING, BIG_INTEGER - } - - public ColumnDef setNullable(boolean isNullable) { - this.isNullable = isNullable; - return this; - } - - public ColumnDef setLimit(@Nullable Integer limit) { - this.limit = limit; - return this; - } - - public ColumnDef setName(String name) { - Preconditions.checkArgument(CharMatcher.JAVA_LOWER_CASE.or(CharMatcher.anyOf("_")).matchesAllOf(name), "Column name should only contains lowercase and _ characters"); - this.name = name; - return this; - } - - public ColumnDef setType(Type type) { - this.type = type; - return this; - } - - public boolean isNullable() { - return isNullable; - } - - @CheckForNull - public Integer getLimit() { - return limit; - } - - public String getName() { - return name; - } - - public Type getType() { - return type; - } - } } diff --git a/sonar-db/src/main/java/org/sonar/db/version/ColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/ColumnDef.java new file mode 100644 index 00000000000..a95fba6464f --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/ColumnDef.java @@ -0,0 +1,90 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.version; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Preconditions; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.db.dialect.Dialect; +import org.sonar.db.dialect.Oracle; + +public class ColumnDef { + + private String name; + private Type type; + private boolean isNullable; + private Integer limit; + + public enum Type { + STRING, BIG_INTEGER + } + + public ColumnDef setNullable(boolean isNullable) { + this.isNullable = isNullable; + return this; + } + + public ColumnDef setLimit(@Nullable Integer limit) { + this.limit = limit; + return this; + } + + public ColumnDef setName(String name) { + Preconditions.checkArgument(CharMatcher.JAVA_LOWER_CASE.or(CharMatcher.anyOf("_")).matchesAllOf(name), "Column name should only contains lowercase and _ characters"); + this.name = name; + return this; + } + + public ColumnDef setType(Type type) { + this.type = type; + return this; + } + + public boolean isNullable() { + return isNullable; + } + + @CheckForNull + public Integer getLimit() { + return limit; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public String getSqlType(Dialect dialect) { + switch (type) { + case STRING: + return "VARCHAR"; + case BIG_INTEGER: + return dialect.getId().equals(Oracle.ID) ? "NUMBER (38)" : "BIGINT"; + default: + throw new IllegalArgumentException("Unsupported type : " + type); + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java b/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java index 09ffbdb94a8..f8070a7b34c 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java @@ -23,6 +23,7 @@ package org.sonar.db.version.v51; import java.sql.SQLException; import org.sonar.db.Database; import org.sonar.db.version.AddColumnsBuilder; +import org.sonar.db.version.ColumnDef; import org.sonar.db.version.DdlChange; /** @@ -51,39 +52,39 @@ public class AddIssuesColumns extends DdlChange { private String generateSql() { return new AddColumnsBuilder(db.getDialect(), "issues") .addColumn( - new AddColumnsBuilder.ColumnDef() + new ColumnDef() .setName("issue_creation_date_ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) + .setType(ColumnDef.Type.BIG_INTEGER) .setNullable(true) ) .addColumn( - new AddColumnsBuilder.ColumnDef() + new ColumnDef() .setName("issue_update_date_ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) + .setType(ColumnDef.Type.BIG_INTEGER) .setNullable(true) ) .addColumn( - new AddColumnsBuilder.ColumnDef() + new ColumnDef() .setName("issue_close_date_ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) + .setType(ColumnDef.Type.BIG_INTEGER) .setNullable(true) ) .addColumn( - new AddColumnsBuilder.ColumnDef() + new ColumnDef() .setName("tags") - .setType(AddColumnsBuilder.ColumnDef.Type.STRING) + .setType(ColumnDef.Type.STRING) .setLimit(4000) .setNullable(true)) .addColumn( - new AddColumnsBuilder.ColumnDef() + new ColumnDef() .setName("component_uuid") - .setType(AddColumnsBuilder.ColumnDef.Type.STRING) + .setType(ColumnDef.Type.STRING) .setLimit(50) .setNullable(true)) .addColumn( - new AddColumnsBuilder.ColumnDef() + new ColumnDef() .setName("project_uuid") - .setType(AddColumnsBuilder.ColumnDef.Type.STRING) + .setType(ColumnDef.Type.STRING) .setLimit(50) .setNullable(true)) .build(); diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java b/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java index 7a16b8a5154..9f5e23218b0 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java @@ -23,9 +23,10 @@ package org.sonar.db.version.v52; import java.sql.SQLException; import org.sonar.db.Database; import org.sonar.db.version.AddColumnsBuilder; +import org.sonar.db.version.ColumnDef; import org.sonar.db.version.DdlChange; -import static org.sonar.db.version.AddColumnsBuilder.ColumnDef.Type.STRING; +import static org.sonar.db.version.ColumnDef.Type.STRING; /** * Add the following column to the manual_measures table : @@ -45,7 +46,7 @@ public class AddManualMeasuresComponentUuidColumn extends DdlChange { private String generateSql() { return new AddColumnsBuilder(getDatabase().getDialect(), "manual_measures") .addColumn( - new AddColumnsBuilder.ColumnDef() + new ColumnDef() .setName("component_uuid") .setType(STRING) .setLimit(50) diff --git a/sonar-db/src/test/java/org/sonar/db/version/AddColumnsBuilderTest.java b/sonar-db/src/test/java/org/sonar/db/version/AddColumnsBuilderTest.java index e5160243c7f..58ac7014cd7 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/AddColumnsBuilderTest.java +++ b/sonar-db/src/test/java/org/sonar/db/version/AddColumnsBuilderTest.java @@ -19,118 +19,82 @@ */ package org.sonar.db.version; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +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 org.sonar.db.version.AddColumnsBuilder; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; +import static org.sonar.db.version.ColumnDef.Type.BIG_INTEGER; +import static org.sonar.db.version.ColumnDef.Type.STRING; public class AddColumnsBuilderTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + static final String TABLE_NAME = "issues"; + + static final H2 H2_DIALECT = new H2(); + static final MySql MYSQL_DIALECT = new MySql(); + static final Oracle ORACLE_DIALECT = new Oracle(); + static final PostgreSql POSTGRES_DIALECT = new PostgreSql(); + static final MsSql MSSQL_DIALECT = new MsSql(); + @Test public void add_columns_on_h2() { - assertThat(new AddColumnsBuilder(new H2(), "issues") - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("date_in_ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) - .setNullable(true)) - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("name") - .setType(AddColumnsBuilder.ColumnDef.Type.STRING) - .setNullable(false) - .setLimit(10)) - .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)"); + assertThat(createSampleBuilder(H2_DIALECT).build()) + .isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)"); } @Test public void add_columns_on_mysql() { - assertThat(new AddColumnsBuilder(new MySql(), "issues") - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("date_in_ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) - .setNullable(true)) - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("name") - .setType(AddColumnsBuilder.ColumnDef.Type.STRING) - .setNullable(false) - .setLimit(10)) - .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)"); + assertThat(createSampleBuilder(MYSQL_DIALECT).build()) + .isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)"); } @Test public void add_columns_on_oracle() { - assertThat(new AddColumnsBuilder(new Oracle(), "issues") - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("date_in_ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) - .setNullable(true)) - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("name") - .setType(AddColumnsBuilder.ColumnDef.Type.STRING) - .setNullable(false) - .setLimit(10)) - .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms NUMBER (38) NULL, name VARCHAR (10) NOT NULL)"); + assertThat(createSampleBuilder(ORACLE_DIALECT).build()) + .isEqualTo("ALTER TABLE issues ADD (date_in_ms NUMBER (38) NULL, name VARCHAR (10) NOT NULL)"); } @Test public void add_columns_on_postgresql() { - assertThat(new AddColumnsBuilder(new PostgreSql(), "issues") - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("date_in_ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) - .setNullable(true)) - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("name") - .setType(AddColumnsBuilder.ColumnDef.Type.STRING) - .setNullable(false) - .setLimit(10)) - .build()).isEqualTo("ALTER TABLE issues ADD COLUMN date_in_ms BIGINT NULL, ADD COLUMN name VARCHAR (10) NOT NULL"); + assertThat(createSampleBuilder(POSTGRES_DIALECT).build()) + .isEqualTo("ALTER TABLE issues ADD COLUMN date_in_ms BIGINT NULL, ADD COLUMN name VARCHAR (10) NOT NULL"); } @Test public void add_columns_on_mssql() { - assertThat(new AddColumnsBuilder(new MsSql(), "issues") - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("date_in_ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) - .setNullable(true)) - .addColumn(new AddColumnsBuilder.ColumnDef() - .setName("name") - .setType(AddColumnsBuilder.ColumnDef.Type.STRING) - .setNullable(false) - .setLimit(10)) - .build()).isEqualTo("ALTER TABLE issues ADD date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL"); + assertThat(createSampleBuilder(MSSQL_DIALECT).build()) + .isEqualTo("ALTER TABLE issues ADD date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL"); } @Test - public void fail_when_column_name_is_in_upper_case() { - try { - new AddColumnsBuilder.ColumnDef() - .setName("DATE_IN_MS") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) - .setNullable(true); - failBecauseExceptionWasNotThrown(IllegalArgumentException.class); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessage("Column name should only contains lowercase and _ characters"); - } + public void fail_with_ISE_if_no_column() { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("No column has been defined"); + + new AddColumnsBuilder(H2_DIALECT, TABLE_NAME).build(); } - @Test - public void fail_when_column_name_contains_invalid_character() { - try { - new AddColumnsBuilder.ColumnDef() - .setName("date-in/ms") - .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) - .setNullable(true); - failBecauseExceptionWasNotThrown(IllegalArgumentException.class); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessage("Column name should only contains lowercase and _ characters"); - } + private AddColumnsBuilder createSampleBuilder(Dialect dialect) { + return new AddColumnsBuilder(dialect, TABLE_NAME) + .addColumn(new ColumnDef() + .setName("date_in_ms") + .setType(BIG_INTEGER) + .setNullable(true)) + .addColumn(new ColumnDef() + .setName("name") + .setType(STRING) + .setNullable(false) + .setLimit(10)); } } diff --git a/sonar-db/src/test/java/org/sonar/db/version/ColumnDefTest.java b/sonar-db/src/test/java/org/sonar/db/version/ColumnDefTest.java new file mode 100644 index 00000000000..c018d84b6d5 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/version/ColumnDefTest.java @@ -0,0 +1,92 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.version; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +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 static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.version.ColumnDef.Type.BIG_INTEGER; +import static org.sonar.db.version.ColumnDef.Type.STRING; + +public class ColumnDefTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void create_column_def() throws Exception { + ColumnDef def = new ColumnDef() + .setName("date_in_ms") + .setType(STRING) + .setLimit(10) + .setNullable(true); + + assertThat(def.getName()).isEqualTo("date_in_ms"); + assertThat(def.getType()).isEqualTo(STRING); + assertThat(def.getLimit()).isEqualTo(10); + assertThat(def.isNullable()).isTrue(); + } + + @Test + public void convert_varchar_type_to_sql() throws Exception { + assertThat(new ColumnDef().setType(STRING).getSqlType(new H2())).isEqualTo("VARCHAR"); + assertThat(new ColumnDef().setType(STRING).getSqlType(new Oracle())).isEqualTo("VARCHAR"); + assertThat(new ColumnDef().setType(STRING).getSqlType(new MsSql())).isEqualTo("VARCHAR"); + assertThat(new ColumnDef().setType(STRING).getSqlType(new MySql())).isEqualTo("VARCHAR"); + } + + @Test + public void convert_big_integer_type_to_sql() throws Exception { + assertThat(new ColumnDef().setType(BIG_INTEGER).getSqlType(new H2())).isEqualTo("BIGINT"); + assertThat(new ColumnDef().setType(BIG_INTEGER).getSqlType(new Oracle())).isEqualTo("NUMBER (38)"); + assertThat(new ColumnDef().setType(BIG_INTEGER).getSqlType(new MsSql())).isEqualTo("BIGINT"); + assertThat(new ColumnDef().setType(BIG_INTEGER).getSqlType(new MySql())).isEqualTo("BIGINT"); + } + + + @Test + public void fail_when_column_name_is_in_upper_case() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Column name should only contains lowercase and _ characters"); + + new ColumnDef() + .setName("DATE_IN_MS") + .setType(BIG_INTEGER) + .setNullable(true); + } + + @Test + public void fail_when_column_name_contains_invalid_character() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Column name should only contains lowercase and _ characters"); + + new ColumnDef() + .setName("date-in/ms") + .setType(BIG_INTEGER) + .setNullable(true); + } +} -- 2.39.5