diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt index c7a5362afb..ddd6f4b0ef 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt @@ -3,6 +3,7 @@ package org.jetbrains.exposed.sql import org.jetbrains.exposed.exceptions.throwUnsupportedException import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.H2Dialect +import org.jetbrains.exposed.sql.vendors.SQLServerDialect import org.jetbrains.exposed.sql.vendors.SQLiteDialect import org.jetbrains.exposed.sql.vendors.currentDialect import org.jetbrains.exposed.sql.vendors.inProperCase @@ -85,7 +86,8 @@ class Column( fun descriptionDdl(modify: Boolean = false): String = buildString { val tr = TransactionManager.current() val column = this@Column - append(tr.identity(column)) + val columnName = tr.identity(column) + append(columnName) append(" ") val isPKColumn = table.primaryKey?.columns?.contains(column) == true val isSQLiteAutoIncColumn = currentDialect is SQLiteDialect && columnType.isAutoInc @@ -112,7 +114,13 @@ class Column( } exposedLogger.error("${currentDialect.name} ${tr.db.version} doesn't support expression '$expressionSQL' as default value.$clientDefault") } else { - append(" DEFAULT $expressionSQL") + if (currentDialect is SQLServerDialect) { + // Create a DEFAULT constraint with an explicit name to facilitate removing it later if needed + val tableName = tr.identity(column.table) + append(" CONSTRAINT DF_${tableName}_$columnName DEFAULT $expressionSQL") + } else { + append(" DEFAULT $expressionSQL") + } } } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt index 2d5e03968f..df28066b1c 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt @@ -251,9 +251,29 @@ open class SQLServerDialect : VendorDialect(dialectName, SQLServerDataTypeProvid return columnDefault !in nonAcceptableDefaults } - // EXPOSED-85 Fix changing default value on column in SQL Server as it requires to drop/create constraint override fun modifyColumn(column: Column<*>, columnDiff: ColumnDiff): List = - super.modifyColumn(column, columnDiff).map { it.replace("MODIFY COLUMN", "ALTER COLUMN") } + super.modifyColumn(column, columnDiff).map { statement -> + if (columnDiff.defaults) { + val transaction = TransactionManager.current() + val tableName = transaction.identity(column.table) + val colName = transaction.identity(column) + + val dropConstraint = "DROP CONSTRAINT IF EXISTS DF_${tableName}_$colName" + + column.dbDefaultValue?.let { + buildString { + append(statement.substringBefore("MODIFY COLUMN") + dropConstraint) + append("; ") + append( + statement.substringBefore("MODIFY COLUMN") + + "ADD CONSTRAINT DF_${tableName}_$colName DEFAULT ${SQLServerDataTypeProvider.processForDefaultValue(it)} for $colName" + ) + } + } ?: (statement.substringBefore("MODIFY COLUMN") + dropConstraint) + } else { + statement.replace("MODIFY COLUMN", "ALTER COLUMN") + } + } override fun createDatabase(name: String): String = "CREATE DATABASE ${name.inProperCase()}" @@ -284,7 +304,13 @@ open class SQLServerDialect : VendorDialect(dialectName, SQLServerDataTypeProvid return super.createIndex(index) } - override fun createIndexWithType(name: String, table: String, columns: String, type: String, filterCondition: String): String { + override fun createIndexWithType( + name: String, + table: String, + columns: String, + type: String, + filterCondition: String + ): String { return "CREATE $type INDEX $name ON $table $columns$filterCondition" } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateMissingTablesAndColumnsTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateMissingTablesAndColumnsTests.kt index c85d1b1120..641a50b731 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateMissingTablesAndColumnsTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateMissingTablesAndColumnsTests.kt @@ -355,9 +355,8 @@ class CreateMissingTablesAndColumnsTests : DatabaseTestsBase() { val actual = SchemaUtils.statementsRequiredToActualizeScheme(emptyTable) assertEquals(1, actual.size) - // SQL Server requires drop/create constraint to change defaults, unsupported for now // Oracle treat '' as NULL column and can't alter from NULL to NULL - if (testDb !in listOf(TestDB.SQLSERVER, TestDB.ORACLE)) { + if (testDb != TestDB.ORACLE) { // Apply changes actual.forEach { exec(it) } } else { @@ -399,8 +398,8 @@ class CreateMissingTablesAndColumnsTests : DatabaseTestsBase() { override val primaryKey = PrimaryKey(id) } - val excludeSettings = listOf(TestDB.SQLITE, TestDB.SQLSERVER) - val complexAlterTable = listOf(TestDB.POSTGRESQL, TestDB.POSTGRESQLNG, TestDB.ORACLE, TestDB.H2_PSQL) + val excludeSettings = listOf(TestDB.SQLITE) + val complexAlterTable = listOf(TestDB.POSTGRESQL, TestDB.POSTGRESQLNG, TestDB.ORACLE, TestDB.H2_PSQL, TestDB.SQLSERVER) withDb(excludeSettings = excludeSettings) { testDb -> try { SchemaUtils.createMissingTablesAndColumns(t1)