Exposed icon indicating copy to clipboard operation
Exposed copied to clipboard

Can't reference on binary primaryKey

Open stanbar opened this issue 7 years ago • 5 comments

Hello, Is it a bug, that I can't reference on binary primaryKey ? Here is my case:

object Books : Table() {
    val hash = binary("hash", 16).primaryKey()
    val title = varchar("title", 256)
    val author = varchar("author", 64)
    val rating = integer("rating")
    val goodreadsId = integer("goodreadsId").nullable()
}

and Quote references Book by it's hash

object Quotes : Table() {
    val text = text("text").primaryKey()
    val author = varchar("author", 64)
    val bookHash = (binary("bookHash", 16) references Books.hash).nullable()
                                           ^^^^^^^^^^
}

And I'm getting error

None of the following substitutions
receiver: Column<ByteArray>  arguments: (Column<ByteArray>)
receiver: Column<ByteArray>  arguments: (Column<Comparable<Nothing>>)
receiver: Column<ByteArray>  arguments: (Column<Comparable<ByteArray>>)
can be applied to
receiver: Column<ByteArray>  arguments: (Column<ByteArray>)

Simple workaround would be replace binary(16) with varchar(32), but I just want to know.

Btw. here is how the hash is calculated using md5 function (I want to guarantee that only one book identified by title and author exists in the database), I wanted to use combined primaryKeys, but I couldn't figure out how to create references with them either

data class Book(
    val title: String,
    val author: String,
    val rating: Int,
    val goodreadsId: Int? = null,
) {
    val hash: ByteArray = DigestUtils.md5(title + author)
}

stanbar avatar Jan 08 '19 15:01 stanbar

At the moment only comparable types are allowed to be referenced, so as a workaround you could define your own function:

    fun Column<ByteArray>.references(ref: Column<ByteArray>, onDelete: ReferenceOption? = null, onUpdate: ReferenceOption? = null) : Column<ByteArray> = apply {
        referee = ref
        this.onUpdate = onUpdate
        this.onDelete = onDelete
    }

To ensure that the only one title+author exists in database you could define a unique index for that field and use simple auto-increment field as a primary key. Another option is to put DigestUtils.md5(title + author) as a clientDefault{ } value on a hash column.

Tapac avatar Jan 10 '19 21:01 Tapac

@Tapac thanks for answer, could you please describe how clientDefault{ } works ? I cant find any documentation about it.

stanbar avatar Jan 11 '19 12:01 stanbar

clientDefault function will be invoked before insert new value into a database if no explicit value was provided and value generated by a function will be inserted instead of null. But I'm not sure that it will be possible to receive values from another column.

Also, you could look for EntityHook object, but there is no documentation too :(

Tapac avatar Jan 11 '19 13:01 Tapac

@Tapac For now I will stick with my current implementation of manually computing md5 hadh. But thank you very much for your help :).

stanbar avatar Jan 16 '19 12:01 stanbar

Same issue.

I tried to create a column like this: val id = binary("id", 8) and made a reference in another table: val userId = reference("userId", UserTable.id)

To make this work I had to create the following function:

fun Table.reference(name: String, refColumn: Column<ByteArray>): Column<ByteArray> {
    return binary(name, (refColumn.columnType as BinaryColumnType).length)
}

olegcherr avatar May 13 '20 19:05 olegcherr