From bd37913b3aed793fe665bf51b4b0b4ff140fa2f0 Mon Sep 17 00:00:00 2001 From: "Andrey.Tarashevskiy" Date: Sun, 18 Jul 2021 22:58:06 +0300 Subject: [PATCH] SQLite table ignoring primaryKey override #1258 --- .../org/jetbrains/exposed/sql/Column.kt | 43 ++++++++++++------- .../kotlin/org/jetbrains/exposed/sql/Table.kt | 2 +- .../exposed/sql/tests/shared/DDLTests.kt | 9 +++- .../sql/tests/shared/ddl/CreateTableTests.kt | 10 ++++- 4 files changed, 46 insertions(+), 18 deletions(-) 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 b6e1d28f16..9b1cbd9bae 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 @@ -45,17 +45,25 @@ class Column( fun nameInDatabaseCase(): String = name.inProperCase() + private val isLastColumnInPK: Boolean get() = table.primaryKey?.columns?.last() == this + + internal val isPrimaryConstraintWillBeDefined: Boolean get() = when { + currentDialect is SQLiteDialect && columnType.isAutoInc -> false + table.isCustomPKNameDefined() -> isLastColumnInPK + isOneColumnPK() -> false + else -> isLastColumnInPK + } + override fun createStatement(): List { val alterTablePrefix = "ALTER TABLE ${TransactionManager.current().identity(table)} ADD" - val isLastColumnInPK = table.primaryKey?.columns?.last() == this + val isH2withCustomPKConstraint = currentDialect is H2Dialect && isLastColumnInPK val columnDefinition = when { - isOneColumnPK() && table.isCustomPKNameDefined() && isLastColumnInPK && currentDialect !is H2Dialect -> descriptionDdl(false) + ", ADD ${table.primaryKeyConstraint()}" - isOneColumnPK() && (currentDialect is H2Dialect || currentDialect is SQLiteDialect) -> descriptionDdl(false).removeSuffix(" PRIMARY KEY") - !isOneColumnPK() && isLastColumnInPK && currentDialect !is H2Dialect -> descriptionDdl(false) + ", ADD ${table.primaryKeyConstraint()}" + isPrimaryConstraintWillBeDefined && isLastColumnInPK && !isH2withCustomPKConstraint -> descriptionDdl(false) + ", ADD ${table.primaryKeyConstraint()}" + isH2withCustomPKConstraint -> descriptionDdl(true) else -> descriptionDdl(false) } - val addConstr = if (isLastColumnInPK && currentDialect is H2Dialect) "$alterTablePrefix ${table.primaryKeyConstraint()}" else null + val addConstr = if (isH2withCustomPKConstraint) "$alterTablePrefix ${table.primaryKeyConstraint()}" else null return listOfNotNull("$alterTablePrefix $columnDefinition", addConstr) } @@ -71,16 +79,21 @@ class Column( /** Returns the SQL representation of this column. */ fun descriptionDdl(modify: Boolean = false): String = buildString { val tr = TransactionManager.current() - append(tr.identity(this@Column)) + val column = this@Column + append(tr.identity(column)) append(" ") - val isPKColumn = table.primaryKey?.columns?.contains(this@Column) == true - val colType = columnType - val isSQLiteAutoIncColumn = currentDialect is SQLiteDialect && colType.isAutoInc + val isPKColumn = table.primaryKey?.columns?.contains(column) == true + val isSQLiteAutoIncColumn = currentDialect is SQLiteDialect && columnType.isAutoInc when { !isPKColumn && isSQLiteAutoIncColumn -> tr.throwUnsupportedException("Auto-increment could be applied only to primary key column") - isSQLiteAutoIncColumn && (!isOneColumnPK() || table.isCustomPKNameDefined()) && table.primaryKey != null -> append(currentDialect.dataTypeProvider.integerType()) - else -> append(colType.sqlType()) + isSQLiteAutoIncColumn && !isOneColumnPK() -> tr.throwUnsupportedException("Auto-increment could be applied only to a single column primary key") + isSQLiteAutoIncColumn && table.isCustomPKNameDefined() -> { + val rawType = columnType.sqlType().substringBefore("PRIMARY KEY") + val constraintPart = table.primaryKeyConstraint()!!.substringBefore("(") + append("$rawType $constraintPart AUTOINCREMENT") + } + else -> append(columnType.sqlType()) } val defaultValue = dbDefaultValue @@ -89,7 +102,7 @@ class Column( if (!currentDialect.isAllowedAsColumnDefault(defaultValue)) { val clientDefault = when { defaultValueFun != null -> " Expression will be evaluated on the client." - !colType.nullable -> " Column will be created with NULL marker." + !columnType.nullable -> " Column will be created with NULL marker." else -> "" } exposedLogger.error("${currentDialect.name} ${tr.db.version} doesn't support expression '$expressionSQL' as default value.$clientDefault") @@ -98,13 +111,13 @@ class Column( } } - if (colType.nullable || (defaultValue != null && defaultValueFun == null && !currentDialect.isAllowedAsColumnDefault(defaultValue))) { + if (columnType.nullable || (defaultValue != null && defaultValueFun == null && !currentDialect.isAllowedAsColumnDefault(defaultValue))) { append(" NULL") - } else if (!isPKColumn || (currentDialect is SQLiteDialect && !colType.isAutoInc)) { + } else if (!isPKColumn || (currentDialect is SQLiteDialect && !isSQLiteAutoIncColumn)) { append(" NOT NULL") } - if (!modify && !table.isCustomPKNameDefined() && isOneColumnPK() && !isSQLiteAutoIncColumn) { + if (!modify && isOneColumnPK() && !isPrimaryConstraintWillBeDefined && !isSQLiteAutoIncColumn) { append(" PRIMARY KEY") } } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt index b9c4870251..cdd98eb1db 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt @@ -1080,7 +1080,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware { if (columns.isNotEmpty()) { columns.joinTo(this, prefix = " (") { it.descriptionDdl(false) } - if (isCustomPKNameDefined() || columns.none { it.isOneColumnPK() }) { + if (columns.any { it.isPrimaryConstraintWillBeDefined }) { primaryKeyConstraint()?.let { append(", $it") } } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt index f10a7c24b1..738f213c09 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt @@ -6,6 +6,7 @@ import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.dao.id.IdTable import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.LongIdTable +import org.jetbrains.exposed.exceptions.UnsupportedByDialectException import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.statements.api.ExposedBlob import org.jetbrains.exposed.sql.tests.DatabaseTestsBase @@ -176,7 +177,7 @@ class DDLTests : DatabaseTestsBase() { override val primaryKey = PrimaryKey(bar, id) } - withTables(Foo) { + withTables(excludeSettings = listOf(TestDB.SQLITE), Foo) { Foo.insert { it[Foo.bar] = 1 } @@ -189,6 +190,12 @@ class DDLTests : DatabaseTestsBase() { assertEquals(1, result[0].second) assertEquals(2, result[1].second) } + + withDb(TestDB.SQLITE) { + expectException { + SchemaUtils.create(Foo) + } + } } @Test fun testIndices01() { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt index 035f855376..31e9d00ac3 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateTableTests.kt @@ -10,6 +10,7 @@ import org.jetbrains.exposed.sql.tests.inProperCase import org.jetbrains.exposed.sql.tests.shared.assertEqualCollections import org.jetbrains.exposed.sql.tests.shared.assertEquals import org.jetbrains.exposed.sql.transactions.TransactionManager +import org.jetbrains.exposed.sql.vendors.SQLiteDialect import org.jetbrains.exposed.sql.vendors.currentDialect import org.junit.Test import java.util.* @@ -144,7 +145,14 @@ class CreateTableTests : DatabaseTestsBase() { val id1ProperName = Book.id.name.inProperCase() val ddlId1 = Book.id.ddl - assertEquals("ALTER TABLE $tableProperName ADD ${Book.id.descriptionDdl(false)}, ADD CONSTRAINT $pkConstraintName PRIMARY KEY ($id1ProperName)", ddlId1.first()) + if (currentDialectTest !is SQLiteDialect) { + 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()) + } } }