Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why setting value multiple times to UpdateBuilder fails? #1177

Closed
nallwhy opened this issue Feb 19, 2021 · 2 comments
Closed

Why setting value multiple times to UpdateBuilder fails? #1177

nallwhy opened this issue Feb 19, 2021 · 2 comments

Comments

@nallwhy
Copy link

nallwhy commented Feb 19, 2021

I'm trying to make factory for test like below.

// Factory Example

val car0 = insert(carFactory())
val car1 = insert(carFactory()) { it[name] = "Model Y" }
abstract class EntityIdTable<Key : Comparable<Key>, E>(name: String = "") : IdTable<Key>(name) {
    abstract fun toEntity(row: ResultRow): E
}

typealias AttributeApplier<Key, E> = EntityIdTable<Key, E>.(InsertStatement<EntityID<Key>>) -> Unit
typealias Factory<Key, E> = Pair<EntityIdTable<Key, E>, AttributeApplier<Key, E>>

fun <Key : Comparable<Key>, E> get(table: EntityIdTable<Key, E>, id: Key): E? {
    return transaction { table.select { table.id eq id }.map(table::toEntity).firstOrNull() }
}

fun <Key : Comparable<Key>, E> insert(
    factory: Factory<Key, E>,
    attributeApplier: AttributeApplier<Key, E> = {}
): E {
    val (table, defaultAttributeApplier) = factory

    return transaction {
        table
            .insertAndGetId {
                ///////// here's the problem /////////
                defaultAttributeApplier(it)
                attributeApplier(it)
                /////////
            }
            .value
            .let { get(table, it)!! }
    }
}

fun carFactory(): Factory<Long, Car> {
    return Pair(CarsTable, {
        it[name] = "default name"
    })
}

It apply default attributes first and overwrite custom attributes.
But when it tries to overwrite custom attributes, it fails.

java.lang.IllegalStateException: com.example.Car.name is already initialized

	at org.jetbrains.exposed.sql.statements.UpdateBuilder.set(UpdateBuilder.kt:17)

And I found that UpdateBuilder blocks setting value multiple times to single key.

abstract class UpdateBuilder<out T>(type: StatementType, targets: List<Table>): Statement<T>(type, targets) {
    ....

    open operator fun <S> set(column: Column<S>, value: S) {
        when {
            values.containsKey(column) -> error("$column is already initialized")
            !column.columnType.nullable && value == null -> error("Trying to set null to not nullable column $column")
            else -> values[column] = value
        }
    }
    ...
}

I have no idea why it is necessary.
I want to bypass this constraint. Can I do that?

Thanks.

@vlsi
Copy link

vlsi commented Jun 19, 2021

@Tapac , sorry for pinging, however, can you please clarify what is the point behind "is already initialized" verification?
I ran across the very same case as nallwhy: I thought I could set "default value" first, and then override it with a more specific one, however, Exposed rejects that code.

Do you think "$column is already initialized" verification could be removed?

@Tapac
Copy link
Contributor

Tapac commented Jul 17, 2021

Agree, it's a legacy check. Will be removed in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants