Skip to content

Commit

Permalink
feat: Support DB-generated values for columns
Browse files Browse the repository at this point in the history
  • Loading branch information
joc-a committed Aug 22, 2023
1 parent b00f1d8 commit 58292dc
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class Column<T>(

fun defaultValueInDb() = dbDefaultValue

internal var isGeneratedInDb: Boolean = false

/** Appends the SQL representation of this column to the specified [queryBuilder]. */
override fun toQueryBuilder(queryBuilder: QueryBuilder): Unit = TransactionManager.current().fullIdentity(this@Column, queryBuilder)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,11 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
defaultValueFun = defaultValue
}

// Potential names: readOnly, generatable, dbGeneratable, dbGenerated, generated, generatedDefault, generatedInDb
fun <T> Column<T>.dbGenerated(): Column<T> = apply {
isGeneratedInDb = true
}

/** UUID column will auto generate its value on a client side just before an insert. */
fun Column<UUID>.autoGenerate(): Column<UUID> = clientDefault { UUID.randomUUID() }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ abstract class BaseBatchInsertStatement(

internal val data = ArrayList<MutableMap<Column<*>, Any?>>()

private fun Column<*>.isDefaultable() = columnType.nullable || defaultValueFun != null
private fun Column<*>.isDefaultable() = columnType.nullable || defaultValueFun != null || isGeneratedInDb

override operator fun <S> set(column: Column<S>, value: S) {
if (data.size > 1 && column !in data[data.size - 2] && !column.isDefaultable()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1400,4 +1400,64 @@ class EntityTests : DatabaseTestsBase() {
assertEquals(1, count)
}
}

object CreditCards : IntIdTable("CreditCards") {
val number = varchar("number", 16)
val spendingLimit = ulong("spendingLimit").dbGenerated()
}

class CreditCard(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<CreditCard>(CreditCards)

var number by CreditCards.number
var spendingLimit by CreditCards.spendingLimit
}

@Test
fun testDbGeneratedDefault() {
withTables(excludeSettings = listOf(TestDB.SQLITE), CreditCards) { testDb ->
addLogger(StdOutSqlLogger)
when (testDb) {
TestDB.POSTGRESQL, TestDB.POSTGRESQLNG -> {
// The value can also be set using a SQL trigger
exec(
"""
CREATE OR REPLACE FUNCTION set_spending_limit()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
NEW."spendingLimit" := 10000;
RETURN NEW;
END;
$$;
CREATE TRIGGER set_spending_limit
BEFORE INSERT
ON CreditCards
FOR EACH ROW
EXECUTE PROCEDURE set_spending_limit()
""".trimIndent()
)
}
else -> {
// This table is only used to get the statement that adds the DEFAULT value, and use it with exec
val CreditCards2 = object : IntIdTable("CreditCards") {
val spendingLimit = ulong("spendingLimit").default(10000uL)
}
val missingStatements = SchemaUtils.addMissingColumnsStatements(CreditCards2)
missingStatements.forEach {
exec(it)
}
}
}

val creditCard = CreditCard.new {
number = "0000 1111 2222 3333"
}

assertEquals(10000uL, creditCard.spendingLimit)
}
}
}

0 comments on commit 58292dc

Please sign in to comment.