diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/ResultRow.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/ResultRow.kt index 644845760b..eeb6d84616 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/ResultRow.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/ResultRow.kt @@ -14,78 +14,86 @@ class ResultRow( /** * Retrieves value of a given expression on this row. * - * @param c expression to evaluate + * @param expression expression to evaluate * @throws IllegalStateException if expression is not in record set or if result value is uninitialized * * @see [getOrNull] to get null in the cases an exception would be thrown */ - operator fun get(c: Expression): T { - if (c in lookUpCache) return lookUpCache[c] as T + operator fun get(expression: Expression): T { + if (expression in lookUpCache) return lookUpCache[expression] as T - val d = getRaw(c) + val d = getRaw(expression) - if (d == null && c is Column<*> && c.dbDefaultValue != null && !c.columnType.nullable) { + if (d == null && expression is Column<*> && expression.dbDefaultValue != null && !expression.columnType.nullable) { exposedLogger.warn( - "Column ${TransactionManager.current().fullIdentity(c)} is marked as not null, " + + "Column ${TransactionManager.current().fullIdentity(expression)} is marked as not null, " + "has default db value, but returns null. Possible have to re-read it from DB." ) } val result = database?.dialect?.let { withDialect(it) { - rawToColumnValue(d, c) + rawToColumnValue(d, expression) } - } ?: rawToColumnValue(d, c) - lookUpCache[c] = result + } ?: rawToColumnValue(d, expression) + lookUpCache[expression] = result return result } - operator fun set(c: Expression, value: T) { - setInternal(c, value) - lookUpCache.remove(c) + operator fun set(expression: Expression, value: T) { + setInternal(expression, value) + lookUpCache.remove(expression) } - private fun setInternal(c: Expression, value: T) { - val index = fieldIndex[c] ?: error("$c is not in record set") + private fun setInternal(expression: Expression, value: T) { + val index = getExpressionIndex(expression) data[index] = value } - fun hasValue(c: Expression): Boolean = fieldIndex[c]?.let { data[it] != NotInitializedValue } ?: false + fun hasValue(expression: Expression): Boolean = fieldIndex[expression]?.let { data[it] != NotInitializedValue } ?: false - fun getOrNull(c: Expression): T? = if (hasValue(c)) get(c) else null + fun getOrNull(expression: Expression): T? = if (hasValue(expression)) get(expression) else null @Suppress("UNCHECKED_CAST") - private fun rawToColumnValue(raw: T?, c: Expression): T { + private fun rawToColumnValue(raw: T?, expression: Expression): T { return when { raw == null -> null - raw == NotInitializedValue -> error("$c is not initialized yet") - c is ExpressionAlias && c.delegate is ExpressionWithColumnType -> c.delegate.columnType.valueFromDB(raw) - c is ExpressionWithColumnType -> c.columnType.valueFromDB(raw) - c is Op.OpBoolean -> BooleanColumnType.INSTANCE.valueFromDB(raw) + raw == NotInitializedValue -> error("$expression is not initialized yet") + expression is ExpressionAlias && expression.delegate is ExpressionWithColumnType -> expression.delegate.columnType.valueFromDB(raw) + expression is ExpressionWithColumnType -> expression.columnType.valueFromDB(raw) + expression is Op.OpBoolean -> BooleanColumnType.INSTANCE.valueFromDB(raw) else -> raw } as T } @Suppress("UNCHECKED_CAST") - private fun getRaw(c: Expression): T? { - if (c is CompositeColumn) { - val rawParts = c.getRealColumns().associateWith { getRaw(it) } - return c.restoreValueFromParts(rawParts) + private fun getRaw(expression: Expression): T? { + if (expression is CompositeColumn) { + val rawParts = expression.getRealColumns().associateWith { getRaw(it) } + return expression.restoreValueFromParts(rawParts) } - val index = fieldIndex[c] - ?: ((c as? Column<*>)?.columnType as? EntityIDColumnType<*>)?.let { fieldIndex[it.idColumn] } + val index = getExpressionIndex(expression) + return data[index] as T? + } + + /** + * Retrieves the index of a given expression in the [fieldIndex] map. + * + * @param expression expression for which to get the index + * @throws IllegalStateException if expression is not in record set + */ + private fun getExpressionIndex(expression: Expression): Int { + return fieldIndex[expression] ?: fieldIndex.keys.firstOrNull { exp -> when (exp) { // exp is Column<*> && exp.table is Alias<*> -> exp.table.delegate == c - is Column<*> -> (exp.columnType as? EntityIDColumnType<*>)?.idColumn == c - is ExpressionAlias<*> -> exp.delegate == c + is Column<*> -> (exp.columnType as? EntityIDColumnType<*>)?.idColumn == expression + is ExpressionAlias<*> -> exp.delegate == expression else -> false } - }?.let { fieldIndex[it] } - ?: error("$c is not in record set") - - return data[index] as T? + }?.let { exp -> fieldIndex[exp] } + ?: error("$expression is not in record set") } override fun toString(): String = diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/entities/EntityTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/entities/EntityTests.kt index 801dc1b0de..c675fdece9 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/entities/EntityTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/entities/EntityTests.kt @@ -1319,4 +1319,28 @@ class EntityTests : DatabaseTestsBase() { ) } } + + object RequestsTable : IdTable() { + val requestId: Column = varchar("requestId", 256) + override val primaryKey = PrimaryKey(requestId) + override val id: Column> = requestId.entityId() + } + + class Request(id: EntityID) : Entity(id) { + companion object : EntityClass(RequestsTable) + + var requestId by RequestsTable.requestId + } + + @Test + fun testSelectFromStringIdTableWithPrimaryKeyByColumn() { + withTables(RequestsTable) { + Request.new { + requestId = "123" + } + + val count = Request.all().count() + assertEquals(1, count) + } + } }