From f1fe6abd05f4e619c67043d0b76d452bcb812347 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 17 Sep 2015 18:14:59 +0200 Subject: [PATCH] SONAR-6840 Group migrations touching same table Migrations that are done on the same table are now executed with only one SQL query --- ... => 931_increase_precision_of_numerics.rb} | 10 +- .../sonar/db/version/AlterColumnsBuilder.java | 118 +++++++++++++++ .../org/sonar/db/version/DatabaseVersion.java | 2 +- .../sonar/db/version/DecimalColumnDef.java | 108 ++++++++++++++ .../sonar/db/version/MigrationStepModule.java | 4 +- .../v52/IncreasePrecisionOfNumerics.java | 74 +++++++++ .../org/sonar/db/version/rows-h2.sql | 2 +- .../db/version/AlterColumnsBuilderTest.java | 97 ++++++++++++ .../db/version/DecimalColumnDefTest.java | 141 ++++++++++++++++++ .../db/version/MigrationStepModuleTest.java | 3 +- .../v52/IncreasePrecisionOfNumericsTest.java | 60 ++++++++ 11 files changed, 605 insertions(+), 14 deletions(-) rename server/sonar-web/src/main/webapp/WEB-INF/db/migrate/{928_increase_precision_of_numerics.rb => 931_increase_precision_of_numerics.rb} (50%) create mode 100644 sonar-db/src/main/java/org/sonar/db/version/AlterColumnsBuilder.java create mode 100644 sonar-db/src/main/java/org/sonar/db/version/DecimalColumnDef.java create mode 100644 sonar-db/src/main/java/org/sonar/db/version/v52/IncreasePrecisionOfNumerics.java create mode 100644 sonar-db/src/test/java/org/sonar/db/version/AlterColumnsBuilderTest.java create mode 100644 sonar-db/src/test/java/org/sonar/db/version/DecimalColumnDefTest.java create mode 100644 sonar-db/src/test/java/org/sonar/db/version/v52/IncreasePrecisionOfNumericsTest.java diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/928_increase_precision_of_numerics.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/931_increase_precision_of_numerics.rb similarity index 50% rename from server/sonar-web/src/main/webapp/WEB-INF/db/migrate/928_increase_precision_of_numerics.rb rename to server/sonar-web/src/main/webapp/WEB-INF/db/migrate/931_increase_precision_of_numerics.rb index cf8874fcb05..aa86563db8f 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/928_increase_precision_of_numerics.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/931_increase_precision_of_numerics.rb @@ -25,15 +25,7 @@ class IncreasePrecisionOfNumerics < ActiveRecord::Migration def self.up - change_column :metrics, :worst_value, :decimal, :null => true, :precision => 30, :scale => 5 - change_column :metrics, :best_value, :decimal, :null => true, :precision => 30, :scale => 5 - change_column :project_measures, :value, :decimal, :null => true, :precision => 30, :scale => 5 - change_column :project_measures, :variation_value_1, :decimal, :null => true, :precision => 30, :scale => 5 - change_column :project_measures, :variation_value_2, :decimal, :null => true, :precision => 30, :scale => 5 - change_column :project_measures, :variation_value_3, :decimal, :null => true, :precision => 30, :scale => 5 - change_column :project_measures, :variation_value_4, :decimal, :null => true, :precision => 30, :scale => 5 - change_column :project_measures, :variation_value_5, :decimal, :null => true, :precision => 30, :scale => 5 - change_column :manual_measures, :value, :decimal, :null => true, :precision => 30, :scale => 5 + execute_java_migration('org.sonar.db.version.v52.IncreasePrecisionOfNumerics') end end diff --git a/sonar-db/src/main/java/org/sonar/db/version/AlterColumnsBuilder.java b/sonar-db/src/main/java/org/sonar/db/version/AlterColumnsBuilder.java new file mode 100644 index 00000000000..eda2a56abbf --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/AlterColumnsBuilder.java @@ -0,0 +1,118 @@ +/* + * 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 java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import org.sonar.db.dialect.Dialect; +import org.sonar.db.dialect.MySql; +import org.sonar.db.dialect.Oracle; +import org.sonar.db.dialect.PostgreSql; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * Generate SQL queries to update multiple column types from a table. + * + * Note that it's not possible to change the nullable state of a column (Because on Oracle, sending a query to set a column to be nullable when it's already nullable will fail). + */ +public class AlterColumnsBuilder { + + private final Dialect dialect; + private final String tableName; + private final List columnDefs = newArrayList(); + + public AlterColumnsBuilder(Dialect dialect, String tableName) { + this.dialect = dialect; + this.tableName = tableName; + } + + public AlterColumnsBuilder updateColumn(ColumnDef columnDef) { + columnDefs.add(columnDef); + return this; + } + + public List build() { + if (columnDefs.isEmpty()) { + throw new IllegalStateException("No column has been defined"); + } + switch (dialect.getId()) { + case PostgreSql.ID: + return createPostgresQuery(); + case MySql.ID: + return createMySqlQuery(); + case Oracle.ID: + return createOracleQuery(); + default: + return createMySqlAndH2Queries(); + } + } + + private List createPostgresQuery() { + StringBuilder sql = new StringBuilder("ALTER TABLE " + tableName + " "); + addColumns(sql, "ALTER COLUMN ", "TYPE "); + return Collections.singletonList(sql.toString()); + } + + private List createMySqlQuery() { + StringBuilder sql = new StringBuilder("ALTER TABLE " + tableName + " "); + addColumns(sql, "MODIFY COLUMN ", ""); + return Collections.singletonList(sql.toString()); + } + + private List createOracleQuery() { + StringBuilder sql = new StringBuilder("ALTER TABLE " + tableName + " ").append("MODIFY ("); + addColumns(sql, "", ""); + sql.append(")"); + return Collections.singletonList(sql.toString()); + } + + private List createMySqlAndH2Queries() { + List sqls = new ArrayList<>(); + for (ColumnDef columnDef : columnDefs) { + StringBuilder defaultQuery = new StringBuilder("ALTER TABLE " + tableName + " "); + defaultQuery.append("ALTER COLUMN "); + addColumn(defaultQuery, columnDef, ""); + sqls.add(defaultQuery.toString()); + } + return sqls; + } + + private void addColumns(StringBuilder sql, String updateKeyword, String typePrefix) { + for (Iterator columnDefIterator = columnDefs.iterator(); columnDefIterator.hasNext();) { + sql.append(updateKeyword); + addColumn(sql, columnDefIterator.next(), typePrefix); + if (columnDefIterator.hasNext()) { + sql.append(", "); + } + } + } + + private void addColumn(StringBuilder sql, ColumnDef columnDef, String typePrefix) { + sql.append(columnDef.getName()) + .append(" ") + .append(typePrefix) + .append(columnDef.generateSqlType(dialect)); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java index f362a4c9693..c8a0ded3816 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java +++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java @@ -29,7 +29,7 @@ import org.sonar.db.MyBatis; public class DatabaseVersion { - public static final int LAST_VERSION = 930; + public static final int LAST_VERSION = 931; /** * The minimum supported version which can be upgraded. Lower diff --git a/sonar-db/src/main/java/org/sonar/db/version/DecimalColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/DecimalColumnDef.java new file mode 100644 index 00000000000..e131f917496 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/DecimalColumnDef.java @@ -0,0 +1,108 @@ +/* + * 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 javax.annotation.CheckForNull; +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.sonar.db.version.ColumnDefValidation.validateColumnName; + +public class DecimalColumnDef extends AbstractColumnDef { + + public static int DEFAULT_PRECISION = 38; + public static int DEFAULT_SACLE = 20; + + private final int precision; + private final int scale; + + private DecimalColumnDef(Builder builder) { + super(builder.columnName, builder.isNullable); + this.precision = builder.precision; + this.scale = builder.scale; + } + + public static Builder newDecimalColumnDefBuilder() { + return new Builder(); + } + + public int getPrecision() { + return precision; + } + + public int getScale() { + return scale; + } + + @Override + public String generateSqlType(Dialect dialect) { + switch (dialect.getId()) { + case PostgreSql.ID: + case Oracle.ID: + return String.format("NUMERIC (%s,%s)", precision, scale); + case MySql.ID: + case MsSql.ID: + return String.format("DECIMAL (%s,%s)", precision, scale); + case H2.ID: + return "DOUBLE"; + default: + throw new UnsupportedOperationException(String.format("Unknown dialect '%s'", dialect.getId())); + } + } + + public static class Builder { + @CheckForNull + private String columnName; + private int precision = DEFAULT_PRECISION; + private int scale = DEFAULT_SACLE; + private boolean isNullable = false; + + public Builder setColumnName(String columnName) { + this.columnName = validateColumnName(columnName); + return this; + } + + public Builder setIsNullable(boolean isNullable) { + this.isNullable = isNullable; + return this; + } + + public Builder setPrecision(int precision) { + this.precision = precision; + return this; + } + + public Builder setScale(int scale) { + this.scale = scale; + return this; + } + + public DecimalColumnDef build() { + validateColumnName(columnName); + return new DecimalColumnDef(this); + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java index 4e1ea5cd811..9f60c133660 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java +++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java @@ -52,6 +52,7 @@ import org.sonar.db.version.v52.FeedFileSourcesDataType; import org.sonar.db.version.v52.FeedManualMeasuresComponentUuid; import org.sonar.db.version.v52.FeedMetricsBooleans; import org.sonar.db.version.v52.FeedProjectLinksComponentUuid; +import org.sonar.db.version.v52.IncreasePrecisionOfNumerics; import org.sonar.db.version.v52.MoveProjectProfileAssociation; import org.sonar.db.version.v52.RemoveComponentLibraries; import org.sonar.db.version.v52.RemoveDuplicatedComponentKeys; @@ -103,6 +104,7 @@ public class MigrationStepModule extends Module { FeedManualMeasuresComponentUuid.class, RemoveSnapshotLibraries.class, RemoveComponentLibraries.class, - RemoveDuplicatedComponentKeys.class); + RemoveDuplicatedComponentKeys.class, + IncreasePrecisionOfNumerics.class); } } diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/IncreasePrecisionOfNumerics.java b/sonar-db/src/main/java/org/sonar/db/version/v52/IncreasePrecisionOfNumerics.java new file mode 100644 index 00000000000..9c4ad3ee737 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/IncreasePrecisionOfNumerics.java @@ -0,0 +1,74 @@ +/* + * 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.v52; + +import java.sql.SQLException; +import java.util.List; +import org.sonar.db.Database; +import org.sonar.db.version.AlterColumnsBuilder; +import org.sonar.db.version.DdlChange; + +import static org.sonar.db.version.DecimalColumnDef.newDecimalColumnDefBuilder; + +/** + * Update numeric type from numeric(30,20) to numeric(38,20) : + *
    + *
  • metrics.worst_value
  • + *
  • metrics.best_value
  • + *
  • project_measures.value
  • + *
  • project_measures.variation_value_1
  • + *
  • project_measures.variation_value_2
  • + *
  • project_measures.variation_value_3
  • + *
  • project_measures.variation_value_4
  • + *
  • project_measures.variation_value_5
  • + *
  • manual_measures.value
  • + *
+ * + * The goal is to be able to manage DATE (DATE is using 13 digits) in these columns + */ +public class IncreasePrecisionOfNumerics extends DdlChange { + + public IncreasePrecisionOfNumerics(Database db) { + super(db); + } + + @Override + public void execute(DdlChange.Context context) throws SQLException { + executeSql(context, "metrics", "worst_value", "best_value"); + executeSql(context, "project_measures", "value", "variation_value_1", "variation_value_2", "variation_value_3", "variation_value_4", "variation_value_5"); + executeSql(context, "manual_measures", "value"); + } + + private void executeSql(DdlChange.Context context, String table, String... columns) throws SQLException { + for (String sql : generateSql(table, columns)) { + context.execute(sql); + } + } + + private List generateSql(String table, String... columns) { + AlterColumnsBuilder columnsBuilder = new AlterColumnsBuilder(getDatabase().getDialect(), table); + for (String column : columns) { + columnsBuilder.updateColumn(newDecimalColumnDefBuilder().setColumnName(column).build()); + } + return columnsBuilder.build(); + } + +} diff --git a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql index 9a4170eaf70..d8b4310588d 100644 --- a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql +++ b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql @@ -346,9 +346,9 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('924'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('925'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('926'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('927'); -INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('928'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('929'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('930'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('931'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/sonar-db/src/test/java/org/sonar/db/version/AlterColumnsBuilderTest.java b/sonar-db/src/test/java/org/sonar/db/version/AlterColumnsBuilderTest.java new file mode 100644 index 00000000000..d801cc37fb7 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/version/AlterColumnsBuilderTest.java @@ -0,0 +1,97 @@ +/* + * 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.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.sonar.db.version.DecimalColumnDef.newDecimalColumnDefBuilder; +import static org.sonar.db.version.StringColumnDef.newStringColumnDefBuilder; + +public class AlterColumnsBuilderTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + static final String TABLE_NAME = "issues"; + + @Test + public void update_columns_on_h2() { + assertThat(createSampleBuilder(new H2()).build()) + .containsOnly("ALTER TABLE issues ALTER COLUMN value DOUBLE", "ALTER TABLE issues ALTER COLUMN name VARCHAR (10)"); + } + + @Test + public void update_columns_on_mssql() { + assertThat(createSampleBuilder(new MsSql()).build()) + .containsOnly("ALTER TABLE issues ALTER COLUMN value DECIMAL (30,20)", "ALTER TABLE issues ALTER COLUMN name VARCHAR (10)"); + } + + @Test + public void update_columns_on_postgres() { + assertThat(createSampleBuilder(new PostgreSql()).build()) + .containsOnly("ALTER TABLE issues ALTER COLUMN value TYPE NUMERIC (30,20), ALTER COLUMN name TYPE VARCHAR (10)"); + } + + @Test + public void update_columns_on_mysql() { + assertThat(createSampleBuilder(new MySql()).build()) + .containsOnly("ALTER TABLE issues MODIFY COLUMN value DECIMAL (30,20), MODIFY COLUMN name VARCHAR (10)"); + } + + @Test + public void update_columns_on_oracle() { + assertThat(createSampleBuilder(new Oracle()).build()) + .containsOnly("ALTER TABLE issues MODIFY (value NUMERIC (30,20), name VARCHAR (10))"); + } + + @Test + public void fail_with_ISE_if_no_column() { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("No column has been defined"); + + new AlterColumnsBuilder(new H2(), TABLE_NAME).build(); + } + + private AlterColumnsBuilder createSampleBuilder(Dialect dialect) { + return new AlterColumnsBuilder(dialect, TABLE_NAME) + .updateColumn( + newDecimalColumnDefBuilder() + .setColumnName("value") + .setPrecision(30) + .setScale(20) + .build()) + .updateColumn( + newStringColumnDefBuilder() + .setColumnName("name") + .setLimit(10) + .build()); + } + +} diff --git a/sonar-db/src/test/java/org/sonar/db/version/DecimalColumnDefTest.java b/sonar-db/src/test/java/org/sonar/db/version/DecimalColumnDefTest.java new file mode 100644 index 00000000000..f54250ebe07 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/version/DecimalColumnDefTest.java @@ -0,0 +1,141 @@ +/* + * 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.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.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DecimalColumnDefTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void build_string_column_def() throws Exception { + DecimalColumnDef def = new DecimalColumnDef.Builder() + .setColumnName("issues") + .setPrecision(30) + .setScale(20) + .setIsNullable(true) + .build(); + + assertThat(def.getName()).isEqualTo("issues"); + assertThat(def.getPrecision()).isEqualTo(30); + assertThat(def.getScale()).isEqualTo(20); + assertThat(def.isNullable()).isTrue(); + } + + @Test + public void fail_with_NPE_if_name_is_null() throws Exception { + thrown.expect(NullPointerException.class); + thrown.expectMessage("Column name cannot be null"); + + new DecimalColumnDef.Builder() + .setColumnName(null); + } + + @Test + public void fail_with_NPE_if_no_name() throws Exception { + thrown.expect(NullPointerException.class); + thrown.expectMessage("Column name cannot be null"); + + new DecimalColumnDef.Builder() + .build(); + } + + @Test + public void default_precision_is_38() throws Exception { + DecimalColumnDef def = new DecimalColumnDef.Builder() + .setColumnName("issues") + .setScale(20) + .setIsNullable(true) + .build(); + + assertThat(def.getPrecision()).isEqualTo(38); + } + + @Test + public void default_precision_is_20() throws Exception { + DecimalColumnDef def = new DecimalColumnDef.Builder() + .setColumnName("issues") + .setPrecision(30) + .setIsNullable(true) + .build(); + + assertThat(def.getScale()).isEqualTo(20); + } + + @Test + public void create_builder_with_default_values() throws Exception { + DecimalColumnDef def = new DecimalColumnDef.Builder() + .setColumnName("issues") + .build(); + + assertThat(def.getPrecision()).isEqualTo(38); + assertThat(def.getScale()).isEqualTo(20); + assertThat(def.isNullable()).isFalse(); + } + + @Test + public void generate_sql_type() throws Exception { + DecimalColumnDef def = new DecimalColumnDef.Builder() + .setColumnName("issues") + .setPrecision(30) + .setScale(20) + .setIsNullable(true) + .build(); + + assertThat(def.generateSqlType(new H2())).isEqualTo("DOUBLE"); + assertThat(def.generateSqlType(new PostgreSql())).isEqualTo("NUMERIC (30,20)"); + assertThat(def.generateSqlType(new MsSql())).isEqualTo("DECIMAL (30,20)"); + assertThat(def.generateSqlType(new MySql())).isEqualTo("DECIMAL (30,20)"); + assertThat(def.generateSqlType(new Oracle())).isEqualTo("NUMERIC (30,20)"); + } + + @Test + public void fail_with_UOE_to_generate_sql_type_when_unknown_dialect() throws Exception { + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("Unknown dialect 'unknown'"); + + DecimalColumnDef def = new DecimalColumnDef.Builder() + .setColumnName("issues") + .setPrecision(30) + .setScale(20) + .setIsNullable(true) + .build(); + + Dialect dialect = mock(Dialect.class); + when(dialect.getId()).thenReturn("unknown"); + def.generateSqlType(dialect); + } + +} diff --git a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java index 6c3fb4cba25..933def51755 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java +++ b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java @@ -21,7 +21,6 @@ package org.sonar.db.version; import org.junit.Test; import org.sonar.core.platform.ComponentContainer; -import org.sonar.db.version.MigrationStepModule; import static org.assertj.core.api.Assertions.assertThat; @@ -30,6 +29,6 @@ public class MigrationStepModuleTest { public void verify_count_of_added_MigrationStep_types() { ComponentContainer container = new ComponentContainer(); new MigrationStepModule().configure(container); - assertThat(container.size()).isEqualTo(38); + assertThat(container.size()).isEqualTo(39); } } diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/IncreasePrecisionOfNumericsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/IncreasePrecisionOfNumericsTest.java new file mode 100644 index 00000000000..6defc825040 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/version/v52/IncreasePrecisionOfNumericsTest.java @@ -0,0 +1,60 @@ +/* + * 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.v52; + +import org.junit.Test; +import org.sonar.db.Database; +import org.sonar.db.dialect.PostgreSql; +import org.sonar.db.version.DdlChange; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * It's not possible to test the change of type to NUMERIC (38,20) with a H2 db because on H2 numeric type is a DOUBLE, without precision and scale. + * That's why mocks are used in this test. + */ +public class IncreasePrecisionOfNumericsTest { + + DdlChange.Context context = mock(DdlChange.Context.class); + Database db = mock(Database.class); + + IncreasePrecisionOfNumerics underTest = new IncreasePrecisionOfNumerics(db); + + @Test + public void update_column_types() throws Exception { + when(db.getDialect()).thenReturn(new PostgreSql()); + + underTest.execute(context); + + verify(context).execute("ALTER TABLE metrics ALTER COLUMN worst_value TYPE NUMERIC (38,20), ALTER COLUMN best_value TYPE NUMERIC (38,20)"); + verify(context).execute("ALTER TABLE project_measures " + + "ALTER COLUMN value TYPE NUMERIC (38,20), " + + "ALTER COLUMN variation_value_1 TYPE NUMERIC (38,20), " + + "ALTER COLUMN variation_value_2 TYPE NUMERIC (38,20), " + + "ALTER COLUMN variation_value_3 TYPE NUMERIC (38,20), " + + "ALTER COLUMN variation_value_4 TYPE NUMERIC (38,20), " + + "ALTER COLUMN variation_value_5 TYPE NUMERIC (38,20)"); + verify(context).execute("ALTER TABLE manual_measures ALTER COLUMN value TYPE NUMERIC (38,20)"); + } + +} -- 2.39.5