Skip to content

Commit

Permalink
fix: EXPOSED-281 Timestamp with time zone column default falsely trig…
Browse files Browse the repository at this point in the history
…gers ALTER statement

-`KotlinOffsetDateTimeColumnType` now overrides `nonNullValueAsDefaultString` to match the default value obtained from the metadata for PostgreSQL, MySQL, and H2 Oracle.
  • Loading branch information
joc-a committed Feb 5, 2024
1 parent 33bab96 commit 1a4ceeb
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,29 @@ internal val ORACLE_OFFSET_DATE_TIME_FORMATTER by lazy {
)
}

// Example result: 2023-07-07 14:42:29.343+00
internal val POSTGRESQL_OFFSET_DATE_TIME_AS_DEFAULT_FORMATTER by lazy {
DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss.SSS[x]",
Locale.ROOT
).withZone(ZoneId.of("UTC"))
}

// Example result: 2023-07-07 14:42:29.343
internal val H2_ORACLE_OFFSET_DATE_TIME_AS_DEFAULT_FORMATTER by lazy {
DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss.SSS",
Locale.ROOT
)
}

internal val MYSQL_OFFSET_DATE_TIME_AS_DEFAULT_FORMATTER by lazy {
DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss.SSSSSS",
Locale.ROOT
).withZone(ZoneId.of("UTC"))
}

internal val DEFAULT_OFFSET_DATE_TIME_FORMATTER by lazy {
DateTimeFormatter.ISO_OFFSET_DATE_TIME.withLocale(Locale.ROOT)
}
Expand Down Expand Up @@ -400,6 +423,19 @@ class KotlinOffsetDateTimeColumnType : ColumnType(), IDateColumnType {
else -> error("Unexpected value: $value of ${value::class.qualifiedName}")
}

override fun nonNullValueAsDefaultString(value: Any): String = when (value) {
is OffsetDateTime -> {
when {
currentDialect is PostgreSQLDialect -> "'${value.format(POSTGRESQL_OFFSET_DATE_TIME_AS_DEFAULT_FORMATTER)}'::timestamp with time zone"
currentDialect is H2Dialect && currentDialect.h2Mode == H2Dialect.H2CompatibilityMode.Oracle ->
"'${value.format(H2_ORACLE_OFFSET_DATE_TIME_AS_DEFAULT_FORMATTER)}'"
currentDialect is MysqlDialect -> "'${value.format(MYSQL_OFFSET_DATE_TIME_AS_DEFAULT_FORMATTER)}'"
else -> super.nonNullValueAsDefaultString(value)
}
}
else -> super.nonNullValueAsDefaultString(value)
}

companion object {
internal val INSTANCE = KotlinOffsetDateTimeColumnType()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,4 +547,29 @@ class DefaultsTest : DatabaseTestsBase() {
assertEquals(0, statements.size)
}
}

@Test
fun testTimestampWithTimeZoneDefaultDoesNotTriggerAlterStatement() {
val offsetDateTime = OffsetDateTime.now(ZoneId.of("Japan"))

val tester = object : Table("tester") {
val timestampWithTimeZoneWithDefault = timestampWithTimeZone("timestampWithTimeZoneWithDefault").default(offsetDateTime)
}

// SQLite does not support ALTER TABLE on a column that has a default value
// MariaDB does not support TIMESTAMP WITH TIME ZONE column type
val unsupportedDatabases = listOf(TestDB.SQLITE, TestDB.MARIADB)
withDb(excludeSettings = unsupportedDatabases) {
if (!isOldMySql()) {
try {
SchemaUtils.drop(tester)
SchemaUtils.create(tester)
val statements = SchemaUtils.addMissingColumnsStatements(tester)
assertEquals(0, statements.size)
} finally {
SchemaUtils.drop(tester)
}
}
}
}
}

0 comments on commit 1a4ceeb

Please sign in to comment.