Skip to content

Commit

Permalink
Add custom name for constraints (#269)
Browse files Browse the repository at this point in the history
* Add custom name for constraints
* Use Index instead of IndexDefinition in Table
  • Loading branch information
mduesterhoeft authored and Tapac committed Mar 28, 2018
1 parent 28e5493 commit ad7c308
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 36 deletions.
18 changes: 10 additions & 8 deletions src/main/kotlin/org/jetbrains/exposed/sql/Constraints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,18 @@ data class ForeignKeyConstraint(val fkName: String, val refereeTable: String, va

}

data class Index(val indexName: String, val table: Table, val columns: List<Column<*>>, val unique: Boolean) : DdlAware {
companion object {
fun forColumns(vararg columns: Column<*>, unique: Boolean): Index {
assert(columns.isNotEmpty())
assert(columns.groupBy { it.table }.size == 1) { "Columns from different tables can't persist in one index" }
val indexName = "${columns.first().table.nameInDatabaseCase()}_${columns.joinToString("_"){it.name.inProperCase()}}" + (if (unique) "_unique".inProperCase() else "")
return Index(indexName, columns.first().table, columns.toList(), unique)
}
data class Index(val columns: List<Column<*>>, val unique: Boolean, val customName: String? = null) : DdlAware {
val table: Table

init {
assert(columns.isNotEmpty())
assert(columns.groupBy { it.table }.size == 1) { "Columns from different tables can't persist in one index" }
table = columns.first().table
}

val indexName
get() = customName?: "${table.nameInDatabaseCase()}_${columns.joinToString("_"){it.name.inProperCase()}}" + (if (unique) "_unique".inProperCase() else "")

override fun createStatement() = listOf(currentDialect.createIndex(this))
override fun dropStatement() = listOf(currentDialect.dropIndex(table.nameInDatabaseCase(), indexName))

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/org/jetbrains/exposed/sql/Queries.kt
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ private fun checkMissingIndices(vararg tables: Table): List<Index> {
val nameDiffers = HashSet<Index>()
for (table in tables) {
val existingTableIndices = currentDialect.existingIndices(table)[table].orEmpty().filterFKeys()
val mappedIndices = table.indices.map { Index.forColumns(*it.first, unique = it.second)}.filterFKeys()
val mappedIndices = table.indices.filterFKeys()

existingTableIndices.forEach { index ->
mappedIndices.firstOrNull { it.onlyNameDiffer(index) }?.let {
Expand Down
17 changes: 10 additions & 7 deletions src/main/kotlin/org/jetbrains/exposed/sql/SchemaUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ object SchemaUtils {
statements.addAll(table.ddl)

// create indices
for ((columns, isUnique) in table.indices) {
statements.addAll(createIndex(columns, isUnique))
for (index in table.indices) {
statements.addAll(createIndex(index))
}
}

Expand All @@ -34,7 +34,10 @@ object SchemaUtils {

fun createFKey(reference: Column<*>) = ForeignKeyConstraint.from(reference).createStatement()

fun createIndex(columns: Array<out Column<*>>, isUnique: Boolean) = Index.forColumns(*columns, unique = isUnique).createStatement()
@Deprecated(message = "pass instance of Index", replaceWith = ReplaceWith("SchemaUtils.createIndex(index)"))
fun createIndex(columns: Array<out Column<*>>, isUnique: Boolean) = createIndex(Index(columns.toList(), unique = isUnique))

fun createIndex(index: Index) = index.createStatement()

private fun addMissingColumnsStatements(vararg tables: Table): List<String> {
with(TransactionManager.current()) {
Expand All @@ -54,9 +57,9 @@ object SchemaUtils {

if (db.supportsAlterTableWithAddColumn) {
// create indexes with new columns
for ((columns, isUnique) in table.indices) {
if (columns.any { missingTableColumns.contains(it) }) {
statements.addAll(createIndex(columns, isUnique))
for (index in table.indices) {
if (index.columns.any { missingTableColumns.contains(it) }) {
statements.addAll(createIndex(index))
}
}

Expand Down Expand Up @@ -192,4 +195,4 @@ object SchemaUtils {
}
currentDialect.resetCaches()
}
}
}
22 changes: 15 additions & 7 deletions src/main/kotlin/org/jetbrains/exposed/sql/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ open class Table(name: String = ""): ColumnSet(), DdlAware {

override fun describe(s: Transaction): String = s.identity(this)

val indices = ArrayList<Pair<Array<out Column<*>>, Boolean>>()
val indices = ArrayList<Index>()

override val fields: List<Expression<*>>
get() = columns
Expand Down Expand Up @@ -397,17 +397,25 @@ open class Table(name: String = ""): ColumnSet(), DdlAware {
}

fun index (isUnique: Boolean = false, vararg columns: Column<*>) {
indices.add(columns to isUnique)
index(null, isUnique, *columns)
}

fun<T> Column<T>.index(isUnique: Boolean = false) : Column<T> = apply {
table.index(isUnique, this)
fun index (customIndexName:String? = null, isUnique: Boolean = false, vararg columns: Column<*>) {
indices.add(Index(columns.toList(), isUnique, customIndexName))
}

fun<T> Column<T>.uniqueIndex() : Column<T> = index(true)
fun<T> Column<T>.index(customIndexName:String? = null, isUnique: Boolean = false) : Column<T> = apply {
table.index(customIndexName, isUnique, this)
}

fun<T> Column<T>.uniqueIndex(customIndexName:String? = null) : Column<T> = index(customIndexName,true)

fun uniqueIndex(vararg columns: Column<*>) {
index(true, *columns)
index(null,true, *columns)
}

fun uniqueIndex(customIndexName:String? = null, vararg columns: Column<*>) {
index(customIndexName,true, *columns)
}

val ddl: List<String>
Expand Down Expand Up @@ -496,4 +504,4 @@ fun ColumnSet.targetTables(): List<Table> = when (this) {
is Table -> listOf(this)
is Join -> this.table.targetTables() + this.joinParts.flatMap { it.joinPart.targetTables() }
else -> error("No target provided for update")
}
}
4 changes: 2 additions & 2 deletions src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ internal abstract class VendorDialect(override val name: String,
val tColumns = table.columns.associateBy { transaction.identity(it) }
tmpIndices.filterNot { it.key.first in pkNames }
.mapNotNull {
it.value.mapNotNull { tColumns[it] }.takeIf { c-> c.size == it.value.size }?.let { c-> Index(it.key.first, table, c, it.key.second) }
it.value.mapNotNull { tColumns[it] }.takeIf { c-> c.size == it.value.size }?.let { c-> Index(c, it.key.second) }
}
})
}
Expand Down Expand Up @@ -327,4 +327,4 @@ internal val currentDialectIfAvailable : DatabaseDialect? get() =

internal fun String.inProperCase(): String = (currentDialectIfAvailable as? VendorDialect)?.run {
this@inProperCase.inProperCase
} ?: this
} ?: this
4 changes: 2 additions & 2 deletions src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal class H2Dialect: VendorDialect(dialectName, H2DataTypeProvider) {
super.existingIndices(*tables).mapValues { it.value.filterNot { it.indexName.startsWith("PRIMARY_KEY_") } }.filterValues { it.isNotEmpty() }

override fun insert(ignore: Boolean, table: Table, columns: List<Column<*>>, expr: String, transaction: Transaction): String {
val uniqueIdxCols = table.indices.filter { it.second }.flatMap { it.first.toList() }
val uniqueIdxCols = table.indices.filter { it.unique }.flatMap { it.columns.toList() }
val uniqueCols = columns.filter { it.indexInPK != null || it in uniqueIdxCols}
return if (ignore && uniqueCols.isNotEmpty() && isMySQLMode) {
val def = super.insert(false, table, columns, expr, transaction)
Expand All @@ -59,4 +59,4 @@ internal class H2Dialect: VendorDialect(dialectName, H2DataTypeProvider) {
companion object {
const val dialectName = "h2"
}
}
}
55 changes: 46 additions & 9 deletions src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class DDLTests : DatabaseTestsBase() {
}

withTables(t) {
val alter = SchemaUtils.createIndex(t.indices[0].first, t.indices[0].second)
val alter = SchemaUtils.createIndex(t.indices[0])
assertEquals("CREATE INDEX ${"t1_name".inProperCase()} ON ${"t1".inProperCase()} (${"name".inProperCase()})", alter)
}
}
Expand All @@ -260,10 +260,10 @@ class DDLTests : DatabaseTestsBase() {
}

withTables(t) {
val a1 = SchemaUtils.createIndex(t.indices[0].first, t.indices[0].second)
val a1 = SchemaUtils.createIndex(t.indices[0])
assertEquals("CREATE INDEX ${"t2_name".inProperCase()} ON ${"t2".inProperCase()} (${"name".inProperCase()})", a1)

val a2 = SchemaUtils.createIndex(t.indices[1].first, t.indices[1].second)
val a2 = SchemaUtils.createIndex(t.indices[1])
assertEquals("CREATE INDEX ${"t2_lvalue_rvalue".inProperCase()} ON ${"t2".inProperCase()} " +
"(${"lvalue".inProperCase()}, ${"rvalue".inProperCase()})", a2)
}
Expand All @@ -276,15 +276,31 @@ class DDLTests : DatabaseTestsBase() {
}

withTables(t) {
val alter = SchemaUtils.createIndex(t.indices[0].first, t.indices[0].second)
val alter = SchemaUtils.createIndex(t.indices[0])
if (currentDialect is SQLiteDialect)
assertEquals("CREATE UNIQUE INDEX ${"t1_name_unique".inProperCase()} ON ${"t1".inProperCase()} (${"name".inProperCase()})", alter)
assertEquals("CREATE UNIQUE INDEX ${"t1_name".inProperCase()} ON ${"t1".inProperCase()} (${"name".inProperCase()})", alter)
else
assertEquals("ALTER TABLE ${"t1".inProperCase()} ADD CONSTRAINT ${"t1_name_unique".inProperCase()} UNIQUE (${"name".inProperCase()})", alter)

}
}

@Test fun testUniqueIndicesCustomName() {
val t = object : Table("t1") {
val id = integer("id").primaryKey()
val name = varchar("name", 255).uniqueIndex("U_T1_NAME")
}

withTables(t) {
val alter = SchemaUtils.createIndex(t.indices[0])
if (currentDialect is SQLiteDialect)
assertEquals("CREATE UNIQUE INDEX ${"U_T1_NAME"} ON ${"t1".inProperCase()} (${"name".inProperCase()})", alter)
else
assertEquals("ALTER TABLE ${"t1".inProperCase()} ADD CONSTRAINT ${"U_T1_NAME"} UNIQUE (${"name".inProperCase()})", alter)

}
}

@Test fun testMultiColumnIndex() {
val t = object : Table("t1") {
val type = varchar("type", 255)
Expand All @@ -296,16 +312,37 @@ class DDLTests : DatabaseTestsBase() {
}

withTables(t) {
val indexAlter = SchemaUtils.createIndex(t.indices[0].first, t.indices[0].second)
val uniqueAlter = SchemaUtils.createIndex(t.indices[1].first, t.indices[1].second)
val indexAlter = SchemaUtils.createIndex(t.indices[0])
val uniqueAlter = SchemaUtils.createIndex(t.indices[1])
assertEquals("CREATE INDEX ${"t1_name_type".inProperCase()} ON ${"t1".inProperCase()} (${"name".inProperCase()}, ${"type".inProperCase()})", indexAlter)
if (currentDialect is SQLiteDialect)
assertEquals("CREATE UNIQUE INDEX ${"t1_type_name_unique".inProperCase()} ON ${"t1".inProperCase()} (${"type".inProperCase()}, ${"name".inProperCase()})", uniqueAlter)
assertEquals("CREATE UNIQUE INDEX ${"t1_type_name".inProperCase()} ON ${"t1".inProperCase()} (${"type".inProperCase()}, ${"name".inProperCase()})", uniqueAlter)
else
assertEquals("ALTER TABLE ${"t1".inProperCase()} ADD CONSTRAINT ${"t1_type_name_unique".inProperCase()} UNIQUE (${"type".inProperCase()}, ${"name".inProperCase()})", uniqueAlter)
}
}

@Test fun testMultiColumnIndexCustomName() {
val t = object : Table("t1") {
val type = varchar("type", 255)
val name = varchar("name", 255)
init {
index("I_T1_NAME_TYPE", false, name, type)
uniqueIndex("U_T1_TYPE_NAME", type, name)
}
}

withTables(t) {
val indexAlter = SchemaUtils.createIndex(t.indices[0])
val uniqueAlter = SchemaUtils.createIndex(t.indices[1])
assertEquals("CREATE INDEX ${"I_T1_NAME_TYPE"} ON ${"t1".inProperCase()} (${"name".inProperCase()}, ${"type".inProperCase()})", indexAlter)
if (currentDialect is SQLiteDialect)
assertEquals("CREATE UNIQUE INDEX ${"U_T1_TYPE_NAME"} ON ${"t1".inProperCase()} (${"type".inProperCase()}, ${"name".inProperCase()})", uniqueAlter)
else
assertEquals("ALTER TABLE ${"t1".inProperCase()} ADD CONSTRAINT ${"U_T1_TYPE_NAME"} UNIQUE (${"type".inProperCase()}, ${"name".inProperCase()})", uniqueAlter)
}
}

@Test fun testBlob() {
val t = object: Table("t1") {
val id = integer("id").autoIncrement("t1_seq").primaryKey()
Expand Down Expand Up @@ -473,4 +510,4 @@ private fun String.inProperCase(): String = TransactionManager.currentOrNull()?.
(currentDialect as? VendorDialect)?.run {
this@inProperCase.inProperCase
}
} ?: this
} ?: this

0 comments on commit ad7c308

Please sign in to comment.