Skip to content

Commit

Permalink
fix!: EXPOSED-278 Invalid Oracle statement when adding a new column t…
Browse files Browse the repository at this point in the history
…hat is used in a primary key

When adding a new column to an existing table in Oracle, if that new column is used in a primary key, then a separate statement is needed for the primary key otherwise there will be a syntax error.
  • Loading branch information
joc-a committed Sep 27, 2024
1 parent fb98340 commit 926f881
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 44 deletions.
17 changes: 8 additions & 9 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,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.MysqlDialect
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
import java.util.*
import org.jetbrains.exposed.sql.vendors.*

private val comparator: Comparator<Column<*>> = compareBy({ it.table.tableName }, { it.name })

Expand Down Expand Up @@ -76,15 +70,20 @@ class Column<T>(
override fun createStatement(): List<String> {
val alterTablePrefix = "ALTER TABLE ${TransactionManager.current().identity(table)} ADD"
val isH2withCustomPKConstraint = currentDialect is H2Dialect && isLastColumnInPK
val isOracle = currentDialect is OracleDialect
val columnDefinition = when {
isPrimaryConstraintWillBeDefined && isLastColumnInPK && !isH2withCustomPKConstraint ->
isPrimaryConstraintWillBeDefined && isLastColumnInPK && !isH2withCustomPKConstraint && !isOracle ->
descriptionDdl(false) + ", ADD ${table.primaryKeyConstraint()}"

isH2withCustomPKConstraint -> descriptionDdl(true)
else -> descriptionDdl(false)
}

val addConstr = if (isH2withCustomPKConstraint) "$alterTablePrefix ${table.primaryKeyConstraint()}" else null
val addConstr = if (isH2withCustomPKConstraint || (isOracle && isPrimaryConstraintWillBeDefined)) {
"$alterTablePrefix ${table.primaryKeyConstraint()}"
} else {
null
}
return listOfNotNull("$alterTablePrefix $columnDefinition", addConstr)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.tests.DatabaseTestsBase
import org.jetbrains.exposed.sql.tests.TestDB
import org.jetbrains.exposed.sql.tests.currentDialectTest
import org.jetbrains.exposed.sql.tests.inProperCase
import org.jetbrains.exposed.sql.tests.shared.Category
import org.jetbrains.exposed.sql.tests.shared.Item
import org.jetbrains.exposed.sql.tests.shared.assertEqualCollections
import org.jetbrains.exposed.sql.tests.shared.assertEquals
import org.jetbrains.exposed.sql.tests.shared.assertTrue
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.vendors.SQLiteDialect
import org.junit.Test
import java.util.*
import kotlin.test.assertFails
Expand Down Expand Up @@ -204,28 +202,8 @@ class CreateTableTests : DatabaseTestsBase() {
}

@Test
fun addCompositePrimaryKeyToTableH2Test() {
withDb(TestDB.H2_V2) {
val tableName = Person.tableName
val tableProperName = tableName.inProperCase()
val id1ProperName = Person.id1.name.inProperCase()
val ddlId1 = Person.id1.ddl
val id2ProperName = Person.id2.name.inProperCase()
val ddlId2 = Person.id2.ddl
val pkConstraintName = Person.primaryKey.name

assertEquals(1, ddlId1.size)
assertEquals("ALTER TABLE $tableProperName ADD ${Person.id1.descriptionDdl(false)}", ddlId1.first())

assertEquals(2, ddlId2.size)
assertEquals("ALTER TABLE $tableProperName ADD $id2ProperName ${Person.id2.columnType.sqlType()}", ddlId2.first())
assertEquals("ALTER TABLE $tableProperName ADD CONSTRAINT $pkConstraintName PRIMARY KEY ($id1ProperName, $id2ProperName)", Person.id2.ddl.last())
}
}

@Test
fun addCompositePrimaryKeyToTableNotH2Test() {
withTables(excludeSettings = TestDB.ALL_H2, tables = arrayOf(Person)) {
fun addCompositePrimaryKeyToTableTest() {
withDb { testDb ->
val tableName = Person.tableName
val tableProperName = tableName.inProperCase()
val id1ProperName = Person.id1.name.inProperCase()
Expand All @@ -237,29 +215,45 @@ class CreateTableTests : DatabaseTestsBase() {
assertEquals(1, ddlId1.size)
assertEquals("ALTER TABLE $tableProperName ADD ${Person.id1.descriptionDdl(false)}", ddlId1.first())

assertEquals(1, ddlId2.size)
assertEquals(
"ALTER TABLE $tableProperName ADD ${Person.id2.descriptionDdl(false)}, ADD CONSTRAINT $pkConstraintName PRIMARY KEY ($id1ProperName, $id2ProperName)",
ddlId2.first()
)
when (testDb) {
in TestDB.ALL_H2, TestDB.ORACLE -> {
assertEquals(2, ddlId2.size)
assertEquals("ALTER TABLE $tableProperName ADD $id2ProperName ${Person.id2.columnType.sqlType()}", ddlId2.first())
assertEquals("ALTER TABLE $tableProperName ADD CONSTRAINT $pkConstraintName PRIMARY KEY ($id1ProperName, $id2ProperName)", Person.id2.ddl.last())
}
else -> {
assertEquals(1, ddlId2.size)
assertEquals(
"ALTER TABLE $tableProperName ADD ${Person.id2.descriptionDdl(false)}, " +
"ADD CONSTRAINT $pkConstraintName PRIMARY KEY ($id1ProperName, $id2ProperName)",
ddlId2.first()
)
}
}
}
}

@Test
fun addOneColumnPrimaryKeyToTableNotH2Test() {
withTables(excludeSettings = TestDB.ALL_H2, tables = arrayOf(Book)) {
fun addOneColumnPrimaryKeyToTableTest() {
withTables(Book) { testDb ->
val tableProperName = Book.tableName.inProperCase()
val pkConstraintName = Book.primaryKey.name
val id1ProperName = Book.id.name.inProperCase()
val ddlId1 = Book.id.ddl

if (currentDialectTest !is SQLiteDialect) {
assertEquals(
when (testDb) {
TestDB.SQLITE -> assertEquals("ALTER TABLE $tableProperName ADD ${Book.id.descriptionDdl(false)}", ddlId1.first())
in TestDB.ALL_H2, TestDB.ORACLE -> assertEqualCollections(
listOf(
"ALTER TABLE $tableProperName ADD ${Book.id.descriptionDdl(false)}",
"ALTER TABLE $tableProperName ADD CONSTRAINT $pkConstraintName PRIMARY KEY ($id1ProperName)"
),
ddlId1
)
else -> assertEquals(
"ALTER TABLE $tableProperName ADD ${Book.id.descriptionDdl(false)}, ADD CONSTRAINT $pkConstraintName PRIMARY KEY ($id1ProperName)",
ddlId1.first()
)
} else {
assertEquals("ALTER TABLE $tableProperName ADD ${Book.id.descriptionDdl(false)}", ddlId1.first())
}
}
}
Expand Down

0 comments on commit 926f881

Please sign in to comment.