*/
package org.sonar.db.version;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
public abstract class AbstractColumnDef implements ColumnDef {
private final String columnName;
private final boolean isNullable;
+ @CheckForNull
+ private final Object defaultValue;
- public AbstractColumnDef(String columnName, boolean isNullable) {
+ public AbstractColumnDef(String columnName, boolean isNullable, @Nullable Object defaultValue) {
this.columnName = columnName;
this.isNullable = isNullable;
+ this.defaultValue = defaultValue;
}
@Override
public boolean isNullable() {
return isNullable;
}
+
+ @Override
+ public Object getDefaultValue() {
+ return defaultValue;
+ }
}
import org.sonar.db.dialect.PostgreSql;
import static com.google.common.collect.Lists.newArrayList;
+import static java.lang.String.format;
import static org.sonar.db.version.Validations.validateTableName;
/**
private void addColumn(StringBuilder sql, ColumnDef columnDef) {
sql.append(columnDef.getName()).append(" ").append(columnDef.generateSqlType(dialect));
+ Object defaultValue = columnDef.getDefaultValue();
+ if (defaultValue != null) {
+ sql.append(" DEFAULT ");
+ // TODO remove duplication with CreateTableBuilder
+ if (defaultValue instanceof String) {
+ sql.append(format("'%s'", defaultValue));
+ } else if (defaultValue instanceof Boolean) {
+ sql.append((boolean) defaultValue ? dialect.getTrueSqlValue() : dialect.getFalseSqlValue());
+ } else {
+ sql.append(defaultValue);
+ }
+ }
sql.append(columnDef.isNullable() ? " NULL" : " NOT NULL");
}
import org.sonar.db.dialect.Oracle;
import org.sonar.db.dialect.PostgreSql;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Lists.newArrayList;
/**
}
public AlterColumnsBuilder updateColumn(ColumnDef columnDef) {
+ // limitation of Oracle, only attribute changes must be defined in ALTER.
+ checkArgument(columnDef.getDefaultValue()==null, "Default value is not supported on alter of column '%s'", columnDef.getName());
columnDefs.add(columnDef);
return this;
}
if (addNotNullableProperty) {
sql.append(columnDef.isNullable() ? " NULL" : " NOT NULL");
}
+
}
}
public class BigIntegerColumnDef extends AbstractColumnDef {
private BigIntegerColumnDef(Builder builder) {
- super(builder.columnName, builder.isNullable);
+ super(builder.columnName, builder.isNullable, null);
}
public static Builder newBigIntegerColumnDefBuilder() {
public class BlobColumnDef extends AbstractColumnDef {
public BlobColumnDef(Builder builder) {
- super(builder.columnName, builder.isNullable);
+ super(builder.columnName, builder.isNullable, null);
}
@Override
*/
package org.sonar.db.version;
+import javax.annotation.Nullable;
import org.sonar.db.dialect.Dialect;
import org.sonar.db.dialect.H2;
import org.sonar.db.dialect.MsSql;
* Used to define VARCHAR column
*/
public class BooleanColumnDef extends AbstractColumnDef {
+
private BooleanColumnDef(Builder builder) {
- super(builder.columnName, builder.isNullable);
+ super(builder.columnName, builder.isNullable, builder.defaultValue);
}
public static Builder newBooleanColumnDefBuilder() {
public static class Builder {
private String columnName;
-
+ private Boolean defaultValue = null;
private boolean isNullable = true;
public Builder setColumnName(String columnName) {
return this;
}
+ public Builder setDefaultValue(@Nullable Boolean b) {
+ this.defaultValue = b;
+ return this;
+ }
+
public BooleanColumnDef build() {
validateColumnName(columnName);
return new BooleanColumnDef(this);
*/
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;
/**
* Used to define CLOB columns
- *
- * Warning, for the moment it's only supporting MsSQL
*/
public class ClobColumnDef extends AbstractColumnDef {
private ClobColumnDef(Builder builder) {
- super(builder.columnName, builder.isNullable);
+ super(builder.columnName, builder.isNullable, null);
}
public static Builder newClobColumnDefBuilder() {
}
public static class Builder {
- @CheckForNull
private String columnName;
-
private boolean isNullable = true;
public Builder setColumnName(String columnName) {
*/
package org.sonar.db.version;
+import javax.annotation.CheckForNull;
import org.sonar.db.dialect.Dialect;
public interface ColumnDef {
String generateSqlType(Dialect dialect);
+ @CheckForNull
+ Object getDefaultValue();
}
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Stream.of;
import static org.sonar.db.version.Validations.validateConstraintName;
res.append(columnDef.getName());
res.append(' ');
appendDataType(res, dialect, columnDef);
+ appendDefaultValue(res, columnDef);
appendNullConstraint(res, columnDef);
appendColumnFlags(res, dialect, columnDef);
if (columnDefIterator.hasNext()) {
}
}
+ private void appendDefaultValue(StringBuilder sql, ColumnDef columnDef) {
+ Object defaultValue = columnDef.getDefaultValue();
+ if (defaultValue != null) {
+ sql.append(" DEFAULT ");
+
+ if (defaultValue instanceof String) {
+ sql.append(format("'%s'", defaultValue));
+ } else if (defaultValue instanceof Boolean) {
+ sql.append((boolean) defaultValue ? dialect.getTrueSqlValue() : dialect.getFalseSqlValue());
+ } else {
+ sql.append(defaultValue);
+ }
+ }
+ }
+
private void appendColumnFlags(StringBuilder res, Dialect dialect, ColumnDef columnDef) {
Collection<ColumnFlag> columnFlags = this.flagsByColumn.get(columnDef);
if (columnFlags != null && columnFlags.contains(ColumnFlag.AUTO_INCREMENT)) {
private final int scale;
private DecimalColumnDef(Builder builder) {
- super(builder.columnName, builder.isNullable);
+ super(builder.columnName, builder.isNullable, null);
this.precision = builder.precision;
this.scale = builder.scale;
}
public class IntegerColumnDef extends AbstractColumnDef {
private IntegerColumnDef(Builder builder) {
- super(builder.columnName, builder.isNullable);
+ super(builder.columnName, builder.isNullable, null);
}
public static Builder newIntegerColumnDefBuilder() {
public class TinyIntColumnDef extends AbstractColumnDef {
private TinyIntColumnDef(Builder builder) {
- super(builder.columnName, builder.isNullable);
+ super(builder.columnName, builder.isNullable, null);
}
@Override
package org.sonar.db.version;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.sonar.db.dialect.Dialect;
import org.sonar.db.dialect.MsSql;
private final int columnSize;
private VarcharColumnDef(Builder builder) {
- super(builder.columnName, builder.isNullable);
+ super(builder.columnName, builder.isNullable, builder.defaultValue);
this.columnSize = builder.columnSize;
}
private boolean isNullable = true;
+ @CheckForNull
+ public String defaultValue = null;
+
public Builder setColumnName(String columnName) {
this.columnName = validateColumnName(columnName);
return this;
return this;
}
+ public Builder setDefaultValue(@Nullable String s) {
+ this.defaultValue = s;
+ return this;
+ }
+
public VarcharColumnDef build() {
validateColumnName(columnName);
requireNonNull(columnSize, "Limit cannot be null");
import org.sonar.db.dialect.PostgreSql;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.version.BooleanColumnDef.newBooleanColumnDefBuilder;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
public class AddColumnsBuilderTest {
- static final String TABLE_NAME = "issues";
+ private static final String TABLE_NAME = "issues";
+
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void add_columns_on_h2() {
assertThat(createSampleBuilder(new H2()).build())
- .isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)");
+ .isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL, col_with_default BOOLEAN DEFAULT false NOT NULL, varchar_col_with_default VARCHAR (3) DEFAULT 'foo' NOT NULL)");
}
@Test
public void add_columns_on_mysql() {
assertThat(createSampleBuilder(new MySql()).build())
- .isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)");
+ .isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL, col_with_default TINYINT(1) DEFAULT false NOT NULL, varchar_col_with_default VARCHAR (3) DEFAULT 'foo' NOT NULL)");
}
@Test
public void add_columns_on_oracle() {
assertThat(createSampleBuilder(new Oracle()).build())
- .isEqualTo("ALTER TABLE issues ADD (date_in_ms NUMBER (38) NULL, name VARCHAR (10) NOT NULL)");
+ .isEqualTo("ALTER TABLE issues ADD (date_in_ms NUMBER (38) NULL, name VARCHAR (10) NOT NULL, col_with_default NUMBER(1) DEFAULT 0 NOT NULL, varchar_col_with_default VARCHAR (3) DEFAULT 'foo' NOT NULL)");
}
@Test
public void add_columns_on_postgresql() {
assertThat(createSampleBuilder(new PostgreSql()).build())
- .isEqualTo("ALTER TABLE issues ADD COLUMN date_in_ms BIGINT NULL, ADD COLUMN name VARCHAR (10) NOT NULL");
+ .isEqualTo("ALTER TABLE issues ADD COLUMN date_in_ms BIGINT NULL, ADD COLUMN name VARCHAR (10) NOT NULL, ADD COLUMN col_with_default BOOLEAN DEFAULT false NOT NULL, ADD COLUMN varchar_col_with_default VARCHAR (3) DEFAULT 'foo' NOT NULL");
}
@Test
public void add_columns_on_mssql() {
assertThat(createSampleBuilder(new MsSql()).build())
- .isEqualTo("ALTER TABLE issues ADD date_in_ms BIGINT NULL, name NVARCHAR (10) NOT NULL");
+ .isEqualTo("ALTER TABLE issues ADD date_in_ms BIGINT NULL, name NVARCHAR (10) NOT NULL, col_with_default BIT DEFAULT 0 NOT NULL, varchar_col_with_default NVARCHAR (3) DEFAULT 'foo' NOT NULL");
}
@Test
private AddColumnsBuilder createSampleBuilder(Dialect dialect) {
return new AddColumnsBuilder(dialect, TABLE_NAME)
.addColumn(new BigIntegerColumnDef.Builder().setColumnName("date_in_ms").setIsNullable(true).build())
- .addColumn(new VarcharColumnDef.Builder().setColumnName("name").setLimit(10).setIsNullable(false).build());
+ .addColumn(new VarcharColumnDef.Builder().setColumnName("name").setLimit(10).setIsNullable(false).build())
+
+ // columns with default values
+ .addColumn(newBooleanColumnDefBuilder().setColumnName("col_with_default").setDefaultValue(false).setIsNullable(false).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("varchar_col_with_default").setLimit(3).setDefaultValue("foo").setIsNullable(false).build());
}
}
import org.sonar.db.dialect.PostgreSql;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.version.BooleanColumnDef.newBooleanColumnDefBuilder;
import static org.sonar.db.version.DecimalColumnDef.newDecimalColumnDefBuilder;
import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
public class AlterColumnsBuilderTest {
- static final String TABLE_NAME = "issues";
+ private static final String TABLE_NAME = "issues";
@Rule
public ExpectedException thrown = ExpectedException.none();
new AlterColumnsBuilder(new H2(), TABLE_NAME).build();
}
+ /**
+ * As we want DEFAULT value to be removed from all tables, it is supported
+ * only on creation of tables and columns, not on alter.
+ */
+ @Test
+ public void updateColumn_throws_IAE_if_default_value_is_defined() {
+ BooleanColumnDef column = newBooleanColumnDefBuilder()
+ .setColumnName("enabled")
+ .setIsNullable(false)
+ .setDefaultValue(false)
+ .build();
+ AlterColumnsBuilder alterColumnsBuilder = new AlterColumnsBuilder(new H2(), TABLE_NAME);
+
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Default value is not supported on alter of column 'enabled'");
+
+ alterColumnsBuilder.updateColumn(column);
+ }
+
private AlterColumnsBuilder createSampleBuilder(Dialect dialect) {
return new AlterColumnsBuilder(dialect, TABLE_NAME)
.updateColumn(
BooleanColumnDef def = new BooleanColumnDef.Builder()
.setColumnName("enabled")
.setIsNullable(false)
+ .setDefaultValue(true)
.build();
assertThat(def.getName()).isEqualTo("enabled");
assertThat(def.isNullable()).isFalse();
+ assertThat(def.getDefaultValue()).isEqualTo(true);
}
@Test
- public void build_column_def_with_default_values() throws Exception {
+ public void build_column_def_with_only_required_attributes() throws Exception {
BooleanColumnDef def = new BooleanColumnDef.Builder()
.setColumnName("enabled")
.build();
assertThat(def.getName()).isEqualTo("enabled");
assertThat(def.isNullable()).isTrue();
+ assertThat(def.getDefaultValue()).isNull();
}
@Test
public void build_string_column_def() throws Exception {
assertThat(underTest.getName()).isEqualTo("issues");
assertThat(underTest.isNullable()).isTrue();
+ assertThat(underTest.getDefaultValue()).isNull();
}
@Test
- public void build_string_column_def_with_default_values() throws Exception {
+ public void build_string_column_def_with_only_required_attributes() throws Exception {
ClobColumnDef def = new ClobColumnDef.Builder()
.setColumnName("issues")
.build();
assertThat(def.getName()).isEqualTo("issues");
assertThat(def.isNullable()).isTrue();
+ assertThat(def.getDefaultValue()).isNull();
}
@Test
});
}
+ @Test
+ public void build_adds_DEFAULT_clause_on_varchar_column_on_H2() {
+ verifyDefaultClauseOnVarcharColumn(H2, "CREATE TABLE table_42 (status VARCHAR (1) DEFAULT 'P' NOT NULL)");
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_varchar_column_on_MSSQL() {
+ verifyDefaultClauseOnVarcharColumn(MS_SQL, "CREATE TABLE table_42 (status NVARCHAR (1) DEFAULT 'P' NOT NULL)");
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_varchar_column_on_MySQL() {
+ verifyDefaultClauseOnVarcharColumn(MY_SQL, "CREATE TABLE table_42 (status VARCHAR (1) DEFAULT 'P' NOT NULL) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin");
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_varchar_column_on_Oracle() {
+ verifyDefaultClauseOnVarcharColumn(ORACLE, "CREATE TABLE table_42 (status VARCHAR (1) DEFAULT 'P' NOT NULL)");
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_varchar_column_on_PostgreSQL() {
+ verifyDefaultClauseOnVarcharColumn(POSTGRESQL, "CREATE TABLE table_42 (status VARCHAR (1) DEFAULT 'P' NOT NULL)");
+ }
+
+ private static void verifyDefaultClauseOnVarcharColumn(Dialect dialect, String expectedSql) {
+ List<String> stmts = new CreateTableBuilder(dialect, TABLE_NAME)
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("status").setLimit(1).setIsNullable(false).setDefaultValue("P").build())
+ .build();
+ assertThat(stmts).containsExactly(expectedSql);
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_boolean_column_on_H2() {
+ verifyDefaultClauseOnBooleanColumn(H2, "CREATE TABLE table_42 (enabled BOOLEAN DEFAULT true NOT NULL)");
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_boolean_column_on_MSSQL() {
+ verifyDefaultClauseOnBooleanColumn(MS_SQL, "CREATE TABLE table_42 (enabled BIT DEFAULT 1 NOT NULL)");
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_boolean_column_on_MySQL() {
+ verifyDefaultClauseOnBooleanColumn(MY_SQL, "CREATE TABLE table_42 (enabled TINYINT(1) DEFAULT true NOT NULL) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin");
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_boolean_column_on_Oracle() {
+ verifyDefaultClauseOnBooleanColumn(ORACLE, "CREATE TABLE table_42 (enabled NUMBER(1) DEFAULT 1 NOT NULL)");
+ }
+
+ @Test
+ public void build_adds_DEFAULT_clause_on_boolean_column_on_PostgreSQL() {
+ verifyDefaultClauseOnBooleanColumn(POSTGRESQL, "CREATE TABLE table_42 (enabled BOOLEAN DEFAULT true NOT NULL)");
+ }
+
+ private static void verifyDefaultClauseOnBooleanColumn(Dialect dialect, String expectedSql) {
+ List<String> stmts = new CreateTableBuilder(dialect, TABLE_NAME)
+ .addColumn(newBooleanColumnDefBuilder().setColumnName("enabled").setIsNullable(false).setDefaultValue(true).build())
+ .build();
+ assertThat(stmts).containsExactly(expectedSql);
+ }
+
private static String bigIntSqlType(Dialect dialect) {
return Oracle.ID.equals(dialect.getId()) ? "NUMBER (38)" : "BIGINT";
}
assertThat(def.getPrecision()).isEqualTo(30);
assertThat(def.getScale()).isEqualTo(20);
assertThat(def.isNullable()).isTrue();
+ assertThat(def.getDefaultValue()).isNull();
}
@Test
}
@Test
- public void create_builder_with_default_values() throws Exception {
+ public void create_builder_with_only_required_attributes() throws Exception {
DecimalColumnDef def = new DecimalColumnDef.Builder()
.setColumnName("issues")
.build();
assertThat(def.getPrecision()).isEqualTo(38);
assertThat(def.getScale()).isEqualTo(20);
assertThat(def.isNullable()).isTrue();
+ assertThat(def.getDefaultValue()).isNull();
}
@Test
assertThat(newIntegerColumnDefBuilder().setColumnName("a").build().getName()).isEqualTo("a");
}
+ @Test
+ public void getDefaultValue_always_returns_null() {
+ assertThat(newIntegerColumnDefBuilder().setColumnName("a").build().getDefaultValue()).isNull();
+ }
+
@Test
public void generateSqlType_for_MsSql() {
assertThat(underTest.generateSqlType(new MsSql())).isEqualTo("INT");
assertThat(def.getName()).isEqualTo("foo");
assertThat(def.isNullable()).isTrue();
+ assertThat(def.getDefaultValue()).isNull();
}
@Test
.setColumnName("issues")
.setLimit(10)
.setIsNullable(true)
+ .setDefaultValue("foo")
.build();
assertThat(def.getName()).isEqualTo("issues");
assertThat(def.getColumnSize()).isEqualTo(10);
assertThat(def.isNullable()).isTrue();
+ assertThat(def.getDefaultValue()).isEqualTo("foo");
}
@Test
- public void build_string_column_def_with_default_values() throws Exception {
+ public void build_string_column_def_with_only_required_attributes() throws Exception {
VarcharColumnDef def = new VarcharColumnDef.Builder()
.setColumnName("issues")
.setLimit(10)
assertThat(def.getName()).isEqualTo("issues");
assertThat(def.getColumnSize()).isEqualTo(10);
assertThat(def.isNullable()).isTrue();
+ assertThat(def.getDefaultValue()).isNull();
}
@Test