Skip to content

Commit

Permalink
feat: Add ability to pass custom sequence to auto-increment column
Browse files Browse the repository at this point in the history
  • Loading branch information
joc-a committed Aug 14, 2024
1 parent e63f35b commit ae61cc8
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 69 deletions.
4 changes: 4 additions & 0 deletions exposed-core/api/exposed-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,13 @@ public final class org/jetbrains/exposed/sql/ArrayColumnType : org/jetbrains/exp

public final class org/jetbrains/exposed/sql/AutoIncColumnType : org/jetbrains/exposed/sql/IColumnType {
public fun <init> (Lorg/jetbrains/exposed/sql/ColumnType;Ljava/lang/String;Ljava/lang/String;)V
public fun <init> (Lorg/jetbrains/exposed/sql/ColumnType;Lorg/jetbrains/exposed/sql/Sequence;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getAutoincSeq ()Ljava/lang/String;
public final fun getDelegate ()Lorg/jetbrains/exposed/sql/ColumnType;
public final fun getNextValExpression ()Lorg/jetbrains/exposed/sql/NextVal;
public fun getNullable ()Z
public final fun getSequence ()Lorg/jetbrains/exposed/sql/Sequence;
public fun hashCode ()I
public fun nonNullValueAsDefaultString (Ljava/lang/Object;)Ljava/lang/String;
public fun nonNullValueToString (Ljava/lang/Object;)Ljava/lang/String;
Expand Down Expand Up @@ -2132,6 +2134,7 @@ public final class org/jetbrains/exposed/sql/Sequence {
public final fun getIncrementBy ()Ljava/lang/Long;
public final fun getMaxValue ()Ljava/lang/Long;
public final fun getMinValue ()Ljava/lang/Long;
public final fun getName ()Ljava/lang/String;
public final fun getStartWith ()Ljava/lang/Long;
}

Expand Down Expand Up @@ -2424,6 +2427,7 @@ public class org/jetbrains/exposed/sql/Table : org/jetbrains/exposed/sql/ColumnS
public static synthetic fun array$default (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;Lorg/jetbrains/exposed/sql/ColumnType;Ljava/lang/Integer;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Column;
public final fun autoGenerate (Lorg/jetbrains/exposed/sql/Column;)Lorg/jetbrains/exposed/sql/Column;
public final fun autoIncrement (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public final fun autoIncrement (Lorg/jetbrains/exposed/sql/Column;Lorg/jetbrains/exposed/sql/Sequence;)Lorg/jetbrains/exposed/sql/Column;
public static synthetic fun autoIncrement$default (Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Column;
public final fun autoinc (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public static synthetic fun autoinc$default (Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Column;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,21 @@ class AutoIncColumnType<T>(
private val fallbackSeqName: String
) : IColumnType<T> by delegate {

private val nextValValue = run {
val sequence = Sequence(_autoincSeq ?: fallbackSeqName)
if (delegate is IntegerColumnType) sequence.nextIntVal() else sequence.nextLongVal()
private var _sequence: Sequence? = null

/** The sequence used to generate new values for this auto-increment column. */
val sequence: Sequence?
get() = _sequence ?: autoincSeq?.let {
Sequence(
it,
startWith = 1,
minValue = 1,
maxValue = Long.MAX_VALUE
)
}

constructor(delegate: ColumnType<T>, sequence: Sequence) : this(delegate, sequence.name, sequence.name) {
_sequence = sequence
}

/** The name of the sequence used to generate new values for this auto-increment column. */
Expand All @@ -148,7 +160,9 @@ class AutoIncColumnType<T>(

/** The SQL expression that advances the sequence of this auto-increment column. */
val nextValExpression: NextVal<*>?
get() = nextValValue.takeIf { autoincSeq != null }
get() = autoincSeq?.let {
if (delegate is IntegerColumnType) sequence?.nextIntVal() else sequence?.nextLongVal()
}

private fun resolveAutoIncType(columnType: IColumnType<*>): String = when {
columnType is EntityIDColumnType<*> -> resolveAutoIncType(columnType.idColumn.columnType)
Expand Down Expand Up @@ -178,6 +192,7 @@ class AutoIncColumnType<T>(
delegate != other.delegate -> false
_autoincSeq != other._autoincSeq -> false
fallbackSeqName != other.fallbackSeqName -> false
sequence != other.sequence -> false
else -> true
}
}
Expand All @@ -186,6 +201,7 @@ class AutoIncColumnType<T>(
var result = delegate.hashCode()
result = 31 * result + (_autoincSeq?.hashCode() ?: 0)
result = 31 * result + fallbackSeqName.hashCode()
result = 31 * result + (sequence?.hashCode() ?: 0)
return result
}
}
Expand Down Expand Up @@ -265,7 +281,7 @@ interface ColumnTransformer<Unwrapped, Wrapped> {
fun wrap(value: Unwrapped): Wrapped
}

fun <Unwrapped, Wrapped>columnTransformer(unwrap: (value: Wrapped) -> Unwrapped, wrap: (value: Unwrapped) -> Wrapped): ColumnTransformer<Unwrapped, Wrapped> {
fun <Unwrapped, Wrapped> columnTransformer(unwrap: (value: Wrapped) -> Unwrapped, wrap: (value: Unwrapped) -> Wrapped): ColumnTransformer<Unwrapped, Wrapped> {
return object : ColumnTransformer<Unwrapped, Wrapped> {
override fun unwrap(value: Wrapped): Unwrapped = unwrap(value)
override fun wrap(value: Unwrapped): Wrapped = wrap(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@ object SchemaUtils {
}

private fun tableDdlWithoutExistingSequence(table: Table): List<String> {
val existingAutoIncSeq = table.autoIncColumn?.autoIncColumnType?.autoincSeq
?.takeIf { currentDialect.sequenceExists(Sequence(it)) }
val existingAutoIncSeq = table.autoIncColumn?.autoIncColumnType?.sequence
?.takeIf { currentDialect.sequenceExists(it) }

return table.ddl.filter { statement ->
if (existingAutoIncSeq != null) {
!statement.lowercase().startsWith("create sequence") || !statement.contains(existingAutoIncSeq)
!statement.lowercase().startsWith("create sequence") || !statement.contains(existingAutoIncSeq.name)
} else {
true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import org.jetbrains.exposed.sql.vendors.currentDialect
* @param cache Specifies how many sequence numbers are to be pre-allocated and stored in memory for faster access.
*/
class Sequence(
private val name: String,
val name: String,
val startWith: Long? = null,
val incrementBy: Long? = null,
val minValue: Long? = null,
Expand Down
30 changes: 19 additions & 11 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,17 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
fun <N : Any> Column<N>.autoIncrement(idSeqName: String? = null): Column<N> =
cloneWithAutoInc(idSeqName).also { replaceColumn(this, it) }

/**
* Make @receiver column an auto-increment column to generate its values in a database.
* **Note:** Only integer and long columns are supported (signed and unsigned types).
* Some databases, like PostgreSQL, support auto-increment via sequences.
* In this case, a sequence should be provided using the [sequence] param.
*
* @param sequence a parameter to provide a sequence
*/
fun <N : Any> Column<N>.autoIncrement(sequence: Sequence): Column<N> =
cloneWithAutoInc(sequence).also { replaceColumn(this, it) }

/**
* Make @receiver column an auto-increment column to generate its values in a database.
* **Note:** Only integer and long columns are supported (signed and unsigned types).
Expand Down Expand Up @@ -1571,6 +1582,12 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
else -> error("Unsupported column type for auto-increment $columnType")
}

private fun <T> Column<T>.cloneWithAutoInc(sequence: Sequence): Column<T> = when (columnType) {
is AutoIncColumnType -> this
is ColumnType -> this.withColumnType(AutoIncColumnType(columnType, sequence))
else -> error("Unsupported column type for auto-increment $columnType")
}

// DDL statements

internal fun primaryKeyConstraint(): String? {
Expand Down Expand Up @@ -1636,16 +1653,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
}

private fun createAutoIncColumnSequence(): List<String> {
return autoIncColumn?.autoIncColumnType?.autoincSeq?.let {
Sequence(
it,
startWith = 1,
minValue = 1,
maxValue = Long.MAX_VALUE
)
}
?.createStatement()
.orEmpty()
return autoIncColumn?.autoIncColumnType?.sequence?.createStatement().orEmpty()
}

override fun modifyStatement(): List<String> =
Expand All @@ -1665,7 +1673,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
}
}

val dropSequence = autoIncColumn?.autoIncColumnType?.autoincSeq?.let { Sequence(it).dropStatement() }.orEmpty()
val dropSequence = autoIncColumn?.autoIncColumnType?.sequence?.dropStatement().orEmpty()

return listOf(dropTable) + dropSequence
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,14 +308,7 @@ class CreateTableTests : DatabaseTestsBase() {
withDb {
val t = TransactionManager.current()
val expected = listOfNotNull(
child.autoIncColumn?.autoIncColumnType?.autoincSeq?.let {
Sequence(
it,
startWith = 1,
minValue = 1,
maxValue = Long.MAX_VALUE
).createStatement().single()
},
child.autoIncColumn?.autoIncColumnType?.sequence?.createStatement()?.single(),
"CREATE TABLE " + addIfNotExistsIfSupported() + "${t.identity(child)} (" +
"${child.columns.joinToString { it.descriptionDdl(false) }}," +
" CONSTRAINT ${t.db.identifierManager.cutIfNecessaryAndQuote(fkName).inProperCase()}" +
Expand Down Expand Up @@ -389,14 +382,7 @@ class CreateTableTests : DatabaseTestsBase() {
withDb {
val t = TransactionManager.current()
val expected = listOfNotNull(
child.autoIncColumn?.autoIncColumnType?.autoincSeq?.let {
Sequence(
it,
startWith = 1,
minValue = 1,
maxValue = Long.MAX_VALUE
).createStatement().single()
},
child.autoIncColumn?.autoIncColumnType?.sequence?.createStatement()?.single(),
"CREATE TABLE " + addIfNotExistsIfSupported() + "${t.identity(child)} (" +
"${child.columns.joinToString { it.descriptionDdl(false) }}," +
" CONSTRAINT ${t.db.identifierManager.cutIfNecessaryAndQuote(fkName).inProperCase()}" +
Expand Down Expand Up @@ -424,14 +410,7 @@ class CreateTableTests : DatabaseTestsBase() {
withDb {
val t = TransactionManager.current()
val expected = listOfNotNull(
child.autoIncColumn?.autoIncColumnType?.autoincSeq?.let {
Sequence(
it,
startWith = 1,
minValue = 1,
maxValue = Long.MAX_VALUE
).createStatement().single()
},
child.autoIncColumn?.autoIncColumnType?.sequence?.createStatement()?.single(),
"CREATE TABLE " + addIfNotExistsIfSupported() + "${t.identity(child)} (" +
"${child.columns.joinToString { it.descriptionDdl(false) }}," +
" CONSTRAINT ${t.db.identifierManager.cutIfNecessaryAndQuote(fkName).inProperCase()}" +
Expand Down Expand Up @@ -462,14 +441,7 @@ class CreateTableTests : DatabaseTestsBase() {
withDb {
val t = TransactionManager.current()
val expected = listOfNotNull(
child.autoIncColumn?.autoIncColumnType?.autoincSeq?.let {
Sequence(
it,
startWith = 1,
minValue = 1,
maxValue = Long.MAX_VALUE
).createStatement().single()
},
child.autoIncColumn?.autoIncColumnType?.sequence?.createStatement()?.single(),
"CREATE TABLE " + addIfNotExistsIfSupported() + "${t.identity(child)} (" +
"${child.columns.joinToString { it.descriptionDdl(false) }}," +
" CONSTRAINT ${t.db.identifierManager.cutIfNecessaryAndQuote(fkName).inProperCase()}" +
Expand Down Expand Up @@ -507,14 +479,7 @@ class CreateTableTests : DatabaseTestsBase() {
val t = TransactionManager.current()
val updateCascadePart = if (testDb != TestDB.ORACLE) " ON UPDATE CASCADE" else ""
val expected = listOfNotNull(
child.autoIncColumn?.autoIncColumnType?.autoincSeq?.let {
Sequence(
it,
startWith = 1,
minValue = 1,
maxValue = Long.MAX_VALUE
).createStatement().single()
},
child.autoIncColumn?.autoIncColumnType?.sequence?.createStatement()?.single(),
"CREATE TABLE " + addIfNotExistsIfSupported() + "${t.identity(child)} (" +
"${child.columns.joinToString { it.descriptionDdl(false) }}," +
" CONSTRAINT ${t.db.identifierManager.cutIfNecessaryAndQuote(fkName).inProperCase()}" +
Expand Down Expand Up @@ -553,14 +518,7 @@ class CreateTableTests : DatabaseTestsBase() {
withDb {
val t = TransactionManager.current()
val expected = listOfNotNull(
child.autoIncColumn?.autoIncColumnType?.autoincSeq?.let {
Sequence(
it,
startWith = 1,
minValue = 1,
maxValue = Long.MAX_VALUE
).createStatement().single()
},
child.autoIncColumn?.autoIncColumnType?.sequence?.createStatement()?.single(),
"CREATE TABLE " + addIfNotExistsIfSupported() + "${t.identity(child)} (" +
"${child.columns.joinToString { it.descriptionDdl(false) }}," +
" CONSTRAINT ${t.db.identifierManager.cutIfNecessaryAndQuote(fkName).inProperCase()}" +
Expand Down
Loading

0 comments on commit ae61cc8

Please sign in to comment.