You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I refined UpdateBuilder#set methods in #1277, and I noticed set(column: CompositeColumn<S>, value: S) is not very elegant.
Then it turned out that all usages of CompositeColumn,getRealColumnsWithValues are forEach {...} which end up with Any? kind o API since Map can't express <Column<T>, T> for individual entries.
In practice, ColumnValue needs two APIs: one for "creating composite value out of parts" (e.g. to construct Kotlin object out of database values) and the second one for "producing parts out of composite" (e.g. to store them into database or to write them in WHERE clause)
The current issues include:
All usages of CompositeColumn#getRealColumnsWithValues introduce UNCHECKED_CAST
RawResult#getRaw is ill defined for CompositeColumn. For instance, ResultRow#hasValue(c), ResultRow#getOrNull is likely to produce wrong results for CompositeColumn, and it does not really use individual column value conversion when parsing the value to composite. For instance, the current CompositeMoneyColumn#transformToValue implementation does have to basically duplicate CurrencyColumnType logic.
What do you think of the following idea?
Producing parts out of composite
Current API: abstract fun getRealColumnsWithValues(compositeValue: T): Map<Column<*>, Any?>
Unfortunately, object: .. syntax is a bit verbose, however, it does make types safe for the callers (UNCHECKED_CAST no longer needed).
The implementation of splitValues would be safer too.
Here's a sample implementation for BiCompositeColumn:
abstractclassBiCompositeColumn<C1, C2, T>(
valtransformFromValue: (T) ->Pair<C1, C2>
) {
overridefunsplitParts(compositeValue:T, consumer:ForEachColumnValue) {
val (v1, v2) = transformFromValue(compositeValue)
consumer.consume(column1, v1) // <-- note that types of column1 and v1 are verified here
consumer.consume(column2, v2)
}
Creating composite value out of parts
Make "raw" value for composites non-existing. In other words, composites are pure virtual, and there's no way to tell if the money value is null without converting amount + currency to money
Add interface so composite implementation can "query" the needed value:
interfaceGetColumnValue {
operatorfun <U> get(column:Column<U>): U
}
abstractclassCompositeColumn<T> : Expression<T>() {
abstractfunrestoreValueFromParts(parts:GetColumnValue): T
The implementation for BiCompositeColumn would be trivial:
overridefunrestoreValueFromParts(parts:GetColumnValue): T {
val result = transformToValue(parts[column1], parts[column2]) // <-- no "unchecked cast here" !
require(result !=null|| nullable) {
"Null value received from DB for non-nullable ${this::class.simpleName} column"
}
return result
}
privatefun <T> getRaw(c:Expression<T>): T? {
if (c isCompositeColumn<T>) {
val rawParts = c.getRealColumns().associateWith { getRaw(it) }
return c.restoreValueFromParts(rawParts) // <-- this is bug. restoreValueFromParts produces non-raw value
}
The better implementation would be behind the lines of
operatorfun <T> get(c:Expression<T>): T {
if (c isCompositeColumn<T>) {
return c.restoreValueFromParts(object:GetColumnValue {
overridefun <U> get(column:Column<U>): U= get(column) // <-- it might need to account for withDialect
})
}
The text was updated successfully, but these errors were encountered:
Then the implementation of CompositeColumn would be slightly easier to read:
overridefunsplitParts(compositeValue:T, columns:SetColumnValue) {
val (v1, v2) = transformFromValue(compositeValue)
columns[column1] = v1 // <-- note that types of column1 and v1 are verified here
columns[column2] = v2
}
However, the usage of splitParts method might be slightly more confusing:
UpdateBuilder:
openoperatorfun <S> set(column:CompositeColumn<S>, value:S) {
column.splitParts(value, object:SetColumnValue {
overridefun <U> set(column:Column<U>, value:U) {
this@UpdateBuilder[column] = value // <-- this@.. is needed to avoid recursive set call
}
})
I refined
UpdateBuilder#set
methods in #1277, and I noticedset(column: CompositeColumn<S>, value: S)
is not very elegant.Then it turned out that all usages of
CompositeColumn,getRealColumnsWithValues
areforEach {...}
which end up withAny?
kind o API since Map can't express<Column<T>, T>
for individual entries.In practice,
ColumnValue
needs two APIs: one for "creating composite value out of parts" (e.g. to construct Kotlin object out of database values) and the second one for "producing parts out of composite" (e.g. to store them into database or to write them in WHERE clause)The current issues include:
CompositeColumn#getRealColumnsWithValues
introduceUNCHECKED_CAST
RawResult#getRaw
is ill defined forCompositeColumn
. For instance,ResultRow#hasValue(c)
,ResultRow#getOrNull
is likely to produce wrong results forCompositeColumn
, and it does not really useindividual column value conversion
when parsing the value to composite. For instance, the currentCompositeMoneyColumn#transformToValue
implementation does have to basically duplicateCurrencyColumnType
logic.What do you think of the following idea?
Producing parts out of composite
Current API:
abstract fun getRealColumnsWithValues(compositeValue: T): Map<Column<*>, Any?>
Suggested API:
Sample usage:
UpdateBuilder
Exposed/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/UpdateBuilder.kt
Lines 42 to 44 in d592472
Table
Exposed/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt
Lines 675 to 682 in d592472
Entity
Exposed/exposed-dao/src/main/kotlin/org/jetbrains/exposed/dao/Entity.kt
Lines 133 to 139 in d592472
Unfortunately,
object: ..
syntax is a bit verbose, however, it does make types safe for the callers (UNCHECKED_CAST
no longer needed).The implementation of
splitValues
would be safer too.Here's a sample implementation for
BiCompositeColumn
:Creating composite value out of parts
Make "raw" value for composites non-existing. In other words, composites are pure virtual, and there's no way to tell if the
money
value is null without convertingamount + currency
tomoney
Add interface so composite implementation can "query" the needed value:
The implementation for
BiCompositeColumn
would be trivial:Sample usages:
Entity
ResultRow
The better implementation would be behind the lines of
The text was updated successfully, but these errors were encountered: