diff --git a/src/main/kotlin/org/jetbrains/exposed/dao/Entity.kt b/src/main/kotlin/org/jetbrains/exposed/dao/Entity.kt index 1a1ff7672a..047f7600c9 100644 --- a/src/main/kotlin/org/jetbrains/exposed/dao/Entity.kt +++ b/src/main/kotlin/org/jetbrains/exposed/dao/Entity.kt @@ -11,7 +11,7 @@ import kotlin.reflect.KProperty /** * @author max */ -class EntityID(id: T?, val table: IdTable) { +class EntityID>(id: T?, val table: IdTable) : Comparable> { var _value: Any? = id val value: T get() { if (_value == null) { @@ -32,6 +32,8 @@ class EntityID(id: T?, val table: IdTable) { return other._value == _value && other.table == table } + + override fun compareTo(other: EntityID): Int = value.compareTo(other.value) } private fun ?>checkReference(reference: Column, factoryTable: IdTable<*>) { @@ -42,33 +44,33 @@ private fun ?>checkReference(reference: Column, factoryTable: I } } -class Reference> (val reference: Column>, val factory: EntityClass) { +class Reference, out Target : Entity> (val reference: Column>, val factory: EntityClass) { init { checkReference(reference, factory.table) } } -class OptionalReference> (val reference: Column?>, val factory: EntityClass) { +class OptionalReference, out Target: Entity> (val reference: Column?>, val factory: EntityClass) { init { checkReference(reference, factory.table) } } -class BackReference, ChildID:Any, in Child:Entity> +class BackReference, out Parent:Entity, ChildID:Comparable, in Child:Entity> (reference: Column>, factory: EntityClass) { private val delegate = Referrers(reference, factory, true) operator fun getValue(o: Child, desc: KProperty<*>) = delegate.getValue(o.apply { o.id.value }, desc).single() // flush entity before to don't miss newly created entities } -class OptionalBackReference, ChildID:Any, in Child:Entity> +class OptionalBackReference, out Parent:Entity, ChildID:Comparable, in Child:Entity> (reference: Column?>, factory: EntityClass) { private val delegate = OptionalReferrers(reference, factory, true) operator fun getValue(o: Child, desc: KProperty<*>) = delegate.getValue(o.apply { o.id.value }, desc).singleOrNull() // flush entity before to don't miss newly created entities } -class Referrers, ChildID:Any, out Child:Entity> +class Referrers, in Parent:Entity, ChildID:Comparable, out Child:Entity> (val reference: Column>, val factory: EntityClass, val cache: Boolean) { init { reference.referee ?: error("Column $reference is not a reference") @@ -85,7 +87,7 @@ class Referrers, ChildID:Any, out Child } } -class OptionalReferrers, ChildID:Any, out Child:Entity> +class OptionalReferrers, in Parent:Entity, ChildID:Comparable, out Child:Entity> (val reference: Column?>, val factory: EntityClass, val cache: Boolean) { init { reference.referee ?: error("Column $reference is not a reference") @@ -116,7 +118,7 @@ class View> (val op : Op, val factory: EntityClas } @Suppress("UNCHECKED_CAST") -class InnerTableLink>(val table: Table, +class InnerTableLink, Target: Entity>(val table: Table, val target: EntityClass) { private fun getSourceRefColumn(o: Entity<*>): Column> { val sourceRefColumn = table.columns.singleOrNull { it.referee == o.klass.table.id } as? Column> ?: error("Table does not reference source") @@ -141,7 +143,7 @@ class InnerTableLink>(val table: Table, return TransactionManager.current().entityCache.getOrPutReferrers(o.id, sourceRefColumn, query) } - operator fun setValue(o: Entity, desc: KProperty<*>, value: SizedIterable) { + operator fun> setValue(o: Entity, desc: KProperty<*>, value: SizedIterable) { val sourceRefColumn = getSourceRefColumn(o) val targetRefColumn = getTargetRefColumn() @@ -171,7 +173,7 @@ class InnerTableLink>(val table: Table, } } -open class Entity(val id: EntityID) { +open class Entity>(val id: EntityID) { var klass: EntityClass> by Delegates.notNull() val writeValues = LinkedHashMap, Any?>() @@ -183,20 +185,20 @@ open class Entity(val id: EntityID) { _readValues!! } - operator fun > Reference.getValue(o: Entity, desc: KProperty<*>): T { + operator fun , T: Entity> Reference.getValue(o: Entity, desc: KProperty<*>): T { val id = reference.getValue(o, desc) return factory.findById(id) ?: error("Cannot find ${factory.table.tableName} WHERE id=$id") } - operator fun > Reference.setValue(o: Entity, desc: KProperty<*>, value: T) { + operator fun , T: Entity> Reference.setValue(o: Entity, desc: KProperty<*>, value: T) { value.id.value // flush before creating reference on it reference.setValue(o, desc, value.id) } - operator fun > OptionalReference.getValue(o: Entity, desc: KProperty<*>): T? = + operator fun , T: Entity> OptionalReference.getValue(o: Entity, desc: KProperty<*>): T? = reference.getValue(o, desc)?.let{factory.findById(it)} - operator fun > OptionalReference.setValue(o: Entity, desc: KProperty<*>, value: T?) { + operator fun , T: Entity> OptionalReference.setValue(o: Entity, desc: KProperty<*>, value: T?) { value?.id?.value // flush before creating reference on it reference.setValue(o, desc, value?.id) } @@ -241,10 +243,10 @@ open class Entity(val id: EntityID) { column.setValue(o, desc, toColumn(value)) } - infix fun > EntityClass.via(table: Table): InnerTableLink = + infix fun , Target:Entity> EntityClass.via(table: Table): InnerTableLink = InnerTableLink(table, this@via) - fun > s(c: EntityClass): EntityClass = c + fun > s(c: EntityClass<*, T>): EntityClass<*, T> = c open fun delete(){ klass.removeFromCache(this) @@ -253,7 +255,7 @@ open class Entity(val id: EntityID) { EntityHook.registerChange(EntityChange(klass, id, EntityChangeType.Removed)) } - open fun flush(batch: EntityBatchUpdate? = null): Boolean { + open fun flush(batch: EntityBatchUpdate? = null): Boolean { if (!writeValues.isEmpty()) { if (batch == null) { val table = klass.table @@ -293,37 +295,37 @@ open class Entity(val id: EntityID) { @Suppress("UNCHECKED_CAST") class EntityCache { - val data = HashMap, MutableMap>>() - val inserts = HashMap, MutableList>>() + val data = HashMap, MutableMap>>() + val inserts = HashMap, MutableList>>() val referrers = HashMap, MutableMap, SizedIterable<*>>>() - private fun > getMap(f: EntityClass) : MutableMap = getMap(f.table) + private fun getMap(f: EntityClass<*, *>) : MutableMap> = getMap(f.table) - private fun > getMap(table: IdTable) : MutableMap = data.getOrPut(table, { + private fun getMap(table: IdTable<*>) : MutableMap> = data.getOrPut(table, { HashMap() - }) as MutableMap + }) fun > getOrPutReferrers(sourceId: EntityID<*>, key: Column<*>, refs: ()-> SizedIterable): SizedIterable = referrers.getOrPut(sourceId, {HashMap()}).getOrPut(key, {LazySizedCollection(refs())}) as SizedIterable - fun > find(f: EntityClass, id: EntityID): T? = getMap(f)[id.value] ?: inserts[f.table]?.firstOrNull { it.id == id } as? T + fun , T: Entity> find(f: EntityClass, id: EntityID): T? = getMap(f)[id.value] as T? ?: inserts[f.table]?.firstOrNull { it.id == id } as? T - fun > findAll(f: EntityClass): Collection = getMap(f).values + fun , T: Entity> findAll(f: EntityClass): Collection = getMap(f).values as Collection - fun > store(f: EntityClass, o: T) { + fun , T: Entity> store(f: EntityClass, o: T) { getMap(f).put(o.id.value, o) } - fun > store(o: T) { + fun store(o: Entity<*>) { getMap(o.klass.table).put(o.id.value, o) } - fun > remove(table: IdTable, o: T) { - getMap(table).remove(o.id.value) + fun , T: Entity> remove(table: IdTable, o: T) { + getMap(table).remove(o.id.value) } - fun > scheduleInsert(f: EntityClass, o: T) { - inserts.getOrPut(f.table) { arrayListOf() }.add(o as Entity) + fun , T: Entity> scheduleInsert(f: EntityClass, o: T) { + inserts.getOrPut(f.table) { arrayListOf() }.add(o as Entity<*>) } fun flush() { @@ -343,7 +345,7 @@ class EntityCache { if (map.isNotEmpty()) { val updatedEntities = HashSet>() val batch = EntityBatchUpdate(map.values.first().klass) - for ((i, entity) in map) { + for ((_, entity) in map) { if (entity.flush(batch)) { if (entity.klass is ImmutableEntityClass<*,*>) { throw IllegalStateException("Update on immutable entity ${entity.javaClass.simpleName} ${entity.id}") @@ -353,7 +355,7 @@ class EntityCache { } batch.execute(TransactionManager.current()) updatedEntities.forEach { - EntityHook.registerChange(EntityChange(it.klass as EntityClass>, it.id as EntityID, EntityChangeType.Updated)) + EntityHook.registerChange(EntityChange(it.klass, it.id, EntityChangeType.Updated)) } } } @@ -372,7 +374,7 @@ class EntityCache { internal fun flushInserts(table: IdTable<*>) { inserts.remove(table)?.let { - var toFlush: List> = it + var toFlush: List> = it do { val partition = toFlush.partition { it.writeValues.none { @@ -455,7 +457,7 @@ class EntityCache { } @Suppress("UNCHECKED_CAST") -abstract class EntityClass>(val table: IdTable, entityType: Class? = null) { +abstract class EntityClass, out T: Entity>(val table: IdTable, entityType: Class? = null) { internal val klass: Class<*> = entityType ?: javaClass.enclosingClass as Class private val ctor = klass.constructors[0] @@ -503,7 +505,7 @@ abstract class EntityClass>(val table: IdTable, } val toLoad = distinctIds - cached.map { it.id } - val loaded = wrapRows(searchQuery(Op.build { table.id inList (toLoad) })) + val loaded = wrapRows(searchQuery(Op.build { table.id inList toLoad })) if (cached.isEmpty()) { return loaded } else { @@ -596,25 +598,25 @@ abstract class EntityClass>(val table: IdTable, infix fun optionalReferencedOn(column: Column?>) = registerRefRule(column) { OptionalReference(column, this) } - infix fun > EntityClass.backReferencedOn(column: Column>) + infix fun , Target:Entity> EntityClass.backReferencedOn(column: Column>) = registerRefRule(column) { BackReference(column, this) } @JvmName("backReferencedOnOpt") - infix fun > EntityClass.backReferencedOn(column: Column?>) + infix fun , Target:Entity> EntityClass.backReferencedOn(column: Column?>) = registerRefRule(column) { BackReference(column as Column>, this) } - infix fun > EntityClass.optionalBackReferencedOn(column: Column>) + infix fun , Target:Entity> EntityClass.optionalBackReferencedOn(column: Column>) = registerRefRule(column) { OptionalBackReference(column as Column?>, this) } @JvmName("optionalBackReferencedOnOpt") - infix fun > EntityClass.optionalBackReferencedOn(column: Column?>) + infix fun , Target:Entity> EntityClass.optionalBackReferencedOn(column: Column?>) = registerRefRule(column) { OptionalBackReference(column, this) } - infix fun referrersOn(column: Column>) = referrersOn(column, false) + infix fun > referrersOn(column: Column>) = referrersOn(column, false) - fun referrersOn(column: Column>, cache: Boolean) = registerRefRule(column) { Referrers(column, this, cache) } + fun > referrersOn(column: Column>, cache: Boolean) = registerRefRule(column) { Referrers(column, this, cache) } - fun optionalReferrersOn(column: Column?>, cache: Boolean = false) = registerRefRule(column) { OptionalReferrers(column, this, cache) } + fun > optionalReferrersOn(column: Column?>, cache: Boolean = false) = registerRefRule(column) { OptionalReferrers(column, this, cache) } fun Column.transform(toColumn: (TReal) -> TColumn, toReal: (TColumn) -> TReal): ColumnWithTransform = ColumnWithTransform(this, toColumn, toReal) @@ -663,10 +665,10 @@ abstract class EntityClass>(val table: IdTable, return inCache.values.flatMap { it.toList() as List } + loaded.orEmpty() } - fun > isAssignableTo(entityClass: EntityClass) = entityClass.klass.isAssignableFrom(klass) + fun , T: Entity> isAssignableTo(entityClass: EntityClass) = entityClass.klass.isAssignableFrom(klass) } -abstract class ImmutableEntityClass>(table: IdTable, entityType: Class? = null) : EntityClass(table, entityType) { +abstract class ImmutableEntityClass, out T: Entity>(table: IdTable, entityType: Class? = null) : EntityClass(table, entityType) { open fun forceUpdateEntity(entity: Entity, column: Column, value: T) { table.update({ table.id eq entity.id }) { it[column] = value @@ -674,10 +676,10 @@ abstract class ImmutableEntityClass>(table: IdTable>(table: IdTable, entityType: Class? = null) : ImmutableEntityClass(table, entityType) { +abstract class ImmutableCachedEntityClass, out T: Entity>(table: IdTable, entityType: Class? = null) : ImmutableEntityClass(table, entityType) { private val cacheLoadingState = Key() - private var _cachedValues: MutableMap>? = null + private var _cachedValues: MutableMap>? = null override fun invalidateEntityInCache(o: Entity) { warmCache() diff --git a/src/main/kotlin/org/jetbrains/exposed/dao/EntityHook.kt b/src/main/kotlin/org/jetbrains/exposed/dao/EntityHook.kt index b8f67855f9..5b18e4b749 100644 --- a/src/main/kotlin/org/jetbrains/exposed/dao/EntityHook.kt +++ b/src/main/kotlin/org/jetbrains/exposed/dao/EntityHook.kt @@ -11,33 +11,33 @@ enum class EntityChangeType { } -data class EntityChange(val entityClass: EntityClass>, val id: EntityID, var changeType: EntityChangeType) +data class EntityChange(val entityClass: EntityClass<*, Entity<*>>, val id: EntityID<*>, var changeType: EntityChangeType) -fun EntityChange.toEntity() : Entity? = entityClass.findById(id) +fun, T: Entity> EntityChange.toEntity() : T? = (entityClass as EntityClass).findById(id as EntityID) -fun> EntityChange<*>.toEntity(klass: EntityClass) : T? { +fun,T: Entity> EntityChange.toEntity(klass: EntityClass) : T? { if (!entityClass.isAssignableTo(klass)) return null @Suppress("UNCHECKED_CAST") - return toEntity() as? T + return toEntity() } object EntityHook { - private val entitySubscribers = CopyOnWriteArrayList<(EntityChange<*>) -> Unit>() + private val entitySubscribers = CopyOnWriteArrayList<(EntityChange) -> Unit>() - private val events by transactionScope { CopyOnWriteArrayList>() } + private val events by transactionScope { CopyOnWriteArrayList() } - val registeredEvents: List> get() = events.toList() + val registeredEvents: List get() = events.toList() - fun subscribe (action: (EntityChange<*>) -> Unit): (EntityChange<*>) -> Unit { + fun subscribe (action: (EntityChange) -> Unit): (EntityChange) -> Unit { entitySubscribers.add(action) return action } - fun unsubscribe (action: (EntityChange<*>) -> Unit) { + fun unsubscribe (action: (EntityChange) -> Unit) { entitySubscribers.remove(action) } - fun registerChange(change: EntityChange<*>) { + fun registerChange(change: EntityChange) { if (events.lastOrNull() != change) { events.add(change) } @@ -53,7 +53,7 @@ object EntityHook { } } -fun withHook(action: (EntityChange<*>) -> Unit, body: ()->T): T { +fun withHook(action: (EntityChange) -> Unit, body: ()->T): T { EntityHook.subscribe(action) try { return body().apply { diff --git a/src/main/kotlin/org/jetbrains/exposed/dao/IdTable.kt b/src/main/kotlin/org/jetbrains/exposed/dao/IdTable.kt index f0f83cfbe6..32bf4c93b7 100644 --- a/src/main/kotlin/org/jetbrains/exposed/dao/IdTable.kt +++ b/src/main/kotlin/org/jetbrains/exposed/dao/IdTable.kt @@ -3,7 +3,7 @@ package org.jetbrains.exposed.dao import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Table -abstract class IdTable(name: String): Table(name) { +abstract class IdTable>(name: String): Table(name) { abstract val id : Column> } diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/Alias.kt b/src/main/kotlin/org/jetbrains/exposed/sql/Alias.kt index 91c94f7a25..565f095a8f 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/Alias.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/Alias.kt @@ -33,7 +33,7 @@ class Alias(val delegate: T, val alias: String) : Table() { } -class ExpressionAlias(val delegate: Expression, val alias: String) : Expression() { +class ExpressionAlias(val delegate: Expression, val alias: String) : Expression() { override fun toSQL(queryBuilder: QueryBuilder): String = "${delegate.toSQL(queryBuilder)} $alias" fun aliasOnlyExpression() = object: Expression() { diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/ColumnType.kt b/src/main/kotlin/org/jetbrains/exposed/sql/ColumnType.kt index cc5a7b7537..de4c8103a6 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/ColumnType.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/ColumnType.kt @@ -80,7 +80,7 @@ val Column<*>.autoIncSeqName : String? get() { ?: (columnType as? EntityIDColumnType<*>)?.idColumn?.autoIncSeqName } -class EntityIDColumnType(val idColumn: Column) : ColumnType(false) { +class EntityIDColumnType>(val idColumn: Column) : ColumnType(false) { init { assert(idColumn.table is IdTable<*>){"EntityId supported only for IdTables"} diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/Expression.kt b/src/main/kotlin/org/jetbrains/exposed/sql/Expression.kt index e30a449d1b..5aa3a446f2 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/Expression.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/Expression.kt @@ -27,7 +27,7 @@ class QueryBuilder(val prepared: Boolean) { } } -abstract class Expression { +abstract class Expression { private val _hashCode by lazy { toString().hashCode() } @@ -46,7 +46,7 @@ abstract class Expression { } } -abstract class ExpressionWithColumnType : Expression() { +abstract class ExpressionWithColumnType : Expression() { // used for operations with literals abstract val columnType: IColumnType } diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/Function.kt b/src/main/kotlin/org/jetbrains/exposed/sql/Function.kt index 6a193d86c6..7671b39e00 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/Function.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/Function.kt @@ -4,7 +4,7 @@ import org.joda.time.DateTime import java.math.BigDecimal import java.util.* -abstract class Function : ExpressionWithColumnType() +abstract class Function : ExpressionWithColumnType() class Count(val expr: Expression<*>, val distinct: Boolean = false): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = @@ -13,7 +13,7 @@ class Count(val expr: Expression<*>, val distinct: Boolean = false): Function): Function() { +class Date(val expr: Expression): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "DATE(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = DateColumnType(false) @@ -24,80 +24,80 @@ class CurrentDateTime : Function() { override val columnType: IColumnType = DateColumnType(false) } -class Month(val expr: Expression): Function() { +class Month(val expr: Expression): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "MONTH(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = DateColumnType(false) } -class LowerCase(val expr: Expression) : Function() { +class LowerCase(val expr: Expression) : Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "LOWER(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = StringColumnType() } -class UpperCase(val expr: Expression) : Function() { +class UpperCase(val expr: Expression) : Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "UPPER(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = StringColumnType() } -class Min(val expr: Expression, _columnType: IColumnType): Function() { +class Min, S:T?>(val expr: Expression, _columnType: IColumnType): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "MIN(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = _columnType } -class Max(val expr: Expression, _columnType: IColumnType): Function() { +class Max, S:T?>(val expr: Expression, _columnType: IColumnType): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "MAX(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = _columnType } -class Avg(val expr: Expression, scale: Int): Function() { +class Avg, in S:T?>(val expr: Expression, scale: Int): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "AVG(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale) } -class StdDevPop(val expr: Expression, scale: Int): Function() { +class StdDevPop(val expr: Expression, scale: Int): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "STDDEV_POP(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale) } -class StdDevSamp(val expr: Expression, scale: Int): Function() { +class StdDevSamp(val expr: Expression, scale: Int): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "STDDEV_SAMP(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale) } -class VarPop(val expr: Expression, scale: Int): Function() { +class VarPop(val expr: Expression, scale: Int): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "VAR_POP(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale) } -class VarSamp(val expr: Expression, scale: Int): Function() { +class VarSamp(val expr: Expression, scale: Int): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "VAR_SAMP(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale) } -class Sum(val expr: Expression, _columnType: IColumnType): Function() { +class Sum(val expr: Expression, _columnType: IColumnType): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "SUM(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = _columnType } -class Coalesce(val expr: ExpressionWithColumnType, val alternate: ExpressionWithColumnType): Function() { +class Coalesce(private val expr: ExpressionWithColumnType, private val alternate: ExpressionWithColumnType): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "COALESCE(${expr.toSQL(queryBuilder)}, ${alternate.toSQL(queryBuilder)})" override val columnType: IColumnType = alternate.columnType } -class Substring(val expr: Expression, val start: ExpressionWithColumnType, val length: ExpressionWithColumnType): Function() { +class Substring(private val expr: Expression, private val start: ExpressionWithColumnType, val length: ExpressionWithColumnType): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = currentDialect.functionProvider.substring(expr, start, length, queryBuilder) @@ -112,12 +112,12 @@ class Random(val seed: Int? = null) : Function() { override val columnType: IColumnType = DecimalColumnType(38, 20) } -class Cast(val expr: Expression<*>, override val columnType: IColumnType) : Function() { +class Cast(val expr: Expression<*>, override val columnType: IColumnType) : Function() { override fun toSQL(queryBuilder: QueryBuilder): String = currentDialect.functionProvider.cast(expr, columnType, queryBuilder) } -class Trim(val expr: Expression<*>): Function() { +class Trim(val expr: Expression): Function() { override fun toSQL(queryBuilder: QueryBuilder): String = "TRIM(${expr.toSQL(queryBuilder)})" override val columnType: IColumnType = StringColumnType() @@ -129,17 +129,18 @@ class Case(val value: Expression<*>? = null) { } class CaseWhen (val value: Expression<*>?) { - val cases: ArrayList, Expression>> = ArrayList() + val cases: ArrayList, Expression>> = ArrayList() - fun When (cond: Expression, result: Expression) : CaseWhen { + @Suppress("UNCHECKED_CAST") + fun When (cond: Expression, result: Expression) : CaseWhen { cases.add( cond to result ) - return this + return this as CaseWhen } - fun Else(e: Expression) : Expression = CaseWhenElse(this, e) + fun Else(e: Expression) : Expression = CaseWhenElse(this, e) } -class CaseWhenElse (val caseWhen: CaseWhen, val elseResult: Expression) : Expression() { +class CaseWhenElse (val caseWhen: CaseWhen, val elseResult: Expression) : Expression() { override fun toSQL(queryBuilder: QueryBuilder): String = buildString { append("CASE") if (caseWhen.value != null) diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/Op.kt b/src/main/kotlin/org/jetbrains/exposed/sql/Op.kt index 8e8e49b9e1..2dda9e39c1 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/Op.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/Op.kt @@ -3,7 +3,7 @@ package org.jetbrains.exposed.sql import org.jetbrains.exposed.dao.EntityID import org.joda.time.DateTime -abstract class Op : Expression() { +abstract class Op : Expression() { companion object { inline fun build(op: SqlExpressionBuilder.()-> Op): Op = SqlExpressionBuilder.op() } @@ -21,7 +21,7 @@ class IsNotNullOp(val expr: Expression<*>): Op() { override fun toSQL(queryBuilder: QueryBuilder):String = "${expr.toSQL(queryBuilder)} IS NOT NULL" } -class LiteralOp(override val columnType: IColumnType, val value: T): ExpressionWithColumnType() { +class LiteralOp(override val columnType: IColumnType, val value: T): ExpressionWithColumnType() { override fun toSQL(queryBuilder: QueryBuilder):String = columnType.valueToString(value) } @@ -30,11 +30,11 @@ class Between(val expr: Expression<*>, val from: LiteralOp<*>, val to: LiteralOp "${expr.toSQL(queryBuilder)} BETWEEN ${from.toSQL(queryBuilder)} AND ${to.toSQL(queryBuilder)}" } -class NoOpConversion(val expr: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { +class NoOpConversion(val expr: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { override fun toSQL(queryBuilder: QueryBuilder): String = expr.toSQL(queryBuilder) } -class InListOrNotInListOp(val expr: ExpressionWithColumnType, val list: Iterable, val isInList: Boolean = true): Op() { +class InListOrNotInListOp(val expr: ExpressionWithColumnType, val list: Iterable, val isInList: Boolean = true): Op() { override fun toSQL(queryBuilder: QueryBuilder): String = buildString{ list.iterator().let { i -> @@ -66,11 +66,11 @@ class InListOrNotInListOp(val expr: ExpressionWithColumnType, val list } } -class QueryParameter(val value: T, val sqlType: IColumnType) : Expression() { +class QueryParameter(val value: T, val sqlType: IColumnType) : Expression() { override fun toSQL(queryBuilder: QueryBuilder): String = queryBuilder.registerArgument(sqlType, value) } -fun idParam(value: EntityID, column: Column>): Expression> = QueryParameter(value, EntityIDColumnType(column)) +fun > idParam(value: EntityID, column: Column>): Expression> = QueryParameter(value, EntityIDColumnType(column)) fun booleanParam(value: Boolean): Expression = QueryParameter(value, BooleanColumnType()) fun intParam(value: Int): Expression = QueryParameter(value, IntegerColumnType()) fun longParam(value: Long): Expression = QueryParameter(value, LongColumnType()) @@ -128,7 +128,7 @@ class AndOp(val expr1: Expression, val expr2: Expression): Op< } } -class OrOp(val expr1: Expression, val expr2: Expression): Op() { +class OrOp(val expr1: Expression, val expr2: Expression): Op() { override fun toSQL(queryBuilder: QueryBuilder) = "(${expr1.toSQL(queryBuilder)}) OR (${expr2.toSQL(queryBuilder)})" } @@ -140,19 +140,19 @@ class notExists(val query: Query) : Op() { override fun toSQL(queryBuilder: QueryBuilder) = "NOT EXISTS (${query.prepareSQL(queryBuilder)})" } -class PlusOp(val expr1: Expression, val expr2: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { +class PlusOp(val expr1: Expression, val expr2: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { override fun toSQL(queryBuilder: QueryBuilder) = "${expr1.toSQL(queryBuilder)}+${expr2.toSQL(queryBuilder)}" } -class MinusOp(val expr1: Expression, val expr2: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { +class MinusOp(val expr1: Expression, val expr2: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { override fun toSQL(queryBuilder: QueryBuilder) = "${expr1.toSQL(queryBuilder)}-${expr2.toSQL(queryBuilder)}" } -class TimesOp(val expr1: Expression, val expr2: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { +class TimesOp(val expr1: Expression, val expr2: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { override fun toSQL(queryBuilder: QueryBuilder):String = "(${expr1.toSQL(queryBuilder)}) * (${expr2.toSQL(queryBuilder)})" } -class DivideOp(val expr1: Expression, val expr2: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { +class DivideOp(val expr1: Expression, val expr2: Expression, override val columnType: IColumnType): ExpressionWithColumnType() { override fun toSQL(queryBuilder: QueryBuilder):String = "(${expr1.toSQL(queryBuilder)}) / (${expr2.toSQL(queryBuilder)})" } diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/Queries.kt b/src/main/kotlin/org/jetbrains/exposed/sql/Queries.kt index 587012b971..6b384ddcf6 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/Queries.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/Queries.kt @@ -46,7 +46,7 @@ fun T.insert(body: T.(InsertStatement)->Unit): InsertStatement /** * @sample org.jetbrains.exposed.sql.tests.shared.DMLTests.testGeneratedKey03 */ -fun > T.insertAndGetId(ignore: Boolean = false, body: T.(InsertStatement>)->Unit) = +fun , T: IdTable> T.insertAndGetId(ignore: Boolean = false, body: T.(InsertStatement>)->Unit) = InsertStatement>(this, ignore).run { body(this) execute(TransactionManager.current()) @@ -97,7 +97,7 @@ fun T.insertIgnore(body: T.(UpdateBuilder<*>)->Unit): InsertStatement< execute(TransactionManager.current()) } -fun > T.insertIgnoreAndGetId(body: T.(UpdateBuilder<*>)->Unit) = InsertStatement>(this, isIgnore = true).run { +fun , T: IdTable> T.insertIgnoreAndGetId(body: T.(UpdateBuilder<*>)->Unit) = InsertStatement>(this, isIgnore = true).run { body(this) execute(TransactionManager.current()) generatedKey diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt b/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt index dbbe0f995c..c60934239b 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt @@ -63,7 +63,7 @@ class ResultRow(size: Int, private val fieldIndex: Map, Int>) { internal fun create(columns : List>): ResultRow = ResultRow(columns.size, columns.mapIndexed { i, c -> c to i }.toMap()).apply { columns.forEach { - this[it] = it.defaultValueFun?.invoke() ?: if (!it.columnType.nullable) NotInitializedValue else null + this[it as Expression] = it.defaultValueFun?.invoke() ?: if (!it.columnType.nullable) NotInitializedValue else null } } } diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt b/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt index 8ec420baea..74f2a856c5 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt @@ -1,3 +1,4 @@ +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") package org.jetbrains.exposed.sql import org.jetbrains.exposed.sql.vendors.FunctionProvider @@ -5,19 +6,19 @@ import org.jetbrains.exposed.sql.vendors.currentDialect import org.joda.time.DateTime import java.math.BigDecimal -fun Column<*>.count() = Count(this) +fun Column<*>.count() : Function = Count(this) fun Expression.date() = Date(this) fun Expression.month() = Month(this) -fun Column<*>.countDistinct() = Count(this, true) +fun Column<*>.countDistinct() : Function = Count(this, true) -fun Column.min() = Min(this, this.columnType) +fun, S:T?> ExpressionWithColumnType.min() = Min(this, this.columnType) -fun Column.max() = Max(this, this.columnType) +fun, S:T?> ExpressionWithColumnType.max() = Max(this, this.columnType) -fun Column.avg(scale: Int = 2) = Avg(this, scale) +fun, S:T?> ExpressionWithColumnType.avg(scale: Int = 2) = Avg(this, scale) fun Column.stdDevPop(scale: Int = 2) = StdDevPop(this, scale) @@ -29,26 +30,26 @@ fun Column.varSamp(scale: Int = 2) = VarSamp(this, scale) fun Column.sum() = Sum(this, this.columnType) -fun Expression<*>.castTo(columnType: IColumnType) = Cast(this, columnType) +fun Expression<*>.castTo(columnType: IColumnType) = Cast(this, columnType) -fun Expression.substring(start: Int, length: Int): Substring = +fun Expression.substring(start: Int, length: Int) : Function = Substring(this, LiteralOp(IntegerColumnType(), start), LiteralOp(IntegerColumnType(), length)) -fun Expression.trim() =Trim(this) +fun Expression.trim() : Function = Trim(this) -fun Expression.lowerCase() = LowerCase(this) -fun Expression.upperCase() = UpperCase(this) +fun Expression.lowerCase() : Function = LowerCase(this) +fun Expression.upperCase() : Function = UpperCase(this) fun Column.groupConcat(separator: String? = null, distinct: Boolean = false, vararg orderBy: Pair,Boolean>): GroupConcat = GroupConcat(this, separator, distinct, *orderBy) object SqlExpressionBuilder { - fun coalesce(expr: ExpressionWithColumnType, alternate: ExpressionWithColumnType): ExpressionWithColumnType = + fun , R:T> coalesce(expr: E, alternate: ExpressionWithColumnType) : ExpressionWithColumnType = Coalesce(expr, alternate) fun case(value: Expression<*>? = null) = Case(value) - fun ExpressionWithColumnType.wrap(value: T): Expression = QueryParameter(value, columnType) + fun ExpressionWithColumnType.wrap(value: T): Expression = QueryParameter(value, columnType) infix fun ExpressionWithColumnType.eq(t: T) : Op { if (t == null) { @@ -57,9 +58,9 @@ object SqlExpressionBuilder { return EqOp(this, wrap(t)) } - fun Expression.eq(other: Expression) : Op = EqOp (this, other) + infix fun Expression.eq(other: Expression) : Op = EqOp (this, other) - infix fun ExpressionWithColumnType.eq(other: Expression) : Op = EqOp (this, other) +// infix fun ExpressionWithColumnType.eq(other: Expression) : Op = EqOp (this, other) infix fun ExpressionWithColumnType.neq(other: T): Op { if (other == null) { @@ -69,27 +70,27 @@ object SqlExpressionBuilder { return NeqOp(this, wrap(other)) } - fun ExpressionWithColumnType.neq(other: Expression) : Op = NeqOp (this, other) + fun ExpressionWithColumnType.neq(other: Expression) : Op = NeqOp (this, other) fun ExpressionWithColumnType.isNull(): Op = IsNullOp(this) fun ExpressionWithColumnType.isNotNull(): Op = IsNotNullOp(this) - infix fun ExpressionWithColumnType.less(t: T) : Op = LessOp(this, wrap(t)) + infix fun, S: T?> ExpressionWithColumnType.less(t: T) : Op = LessOp(this, wrap(t)) - fun ExpressionWithColumnType.less(other: Expression) : Op = LessOp (this, other) + fun, S: T?> ExpressionWithColumnType.less(other: Expression) = LessOp (this, other) - infix fun ExpressionWithColumnType.lessEq(t: T) : Op =LessEqOp(this, wrap(t)) + infix fun, S: T?> ExpressionWithColumnType.lessEq(t: T) : Op = LessEqOp(this, wrap(t)) - fun ExpressionWithColumnType.lessEq(other: Expression) : Op = LessEqOp(this, other) + fun, S: T?> ExpressionWithColumnType.lessEq(other: Expression) : Op = LessEqOp(this, other) - infix fun ExpressionWithColumnType.greater(t: T) : Op = GreaterOp(this, wrap(t)) + infix fun, S: T?> ExpressionWithColumnType.greater(t: T) : Op = GreaterOp(this, wrap(t)) - fun ExpressionWithColumnType.greater(other: Expression) : Op = GreaterOp (this, other) + fun, S: T?> ExpressionWithColumnType.greater(other: Expression) : Op = GreaterOp (this, other) - infix fun ExpressionWithColumnType.greaterEq(t: T) : Op = GreaterEqOp (this, wrap(t)) + infix fun, S: T?> ExpressionWithColumnType.greaterEq(t: T) : Op = GreaterEqOp (this, wrap(t)) - fun ExpressionWithColumnType.greaterEq(other: Expression) : Op = GreaterEqOp (this, other) + fun, S: T?> ExpressionWithColumnType.greaterEq(other: Expression) : Op = GreaterEqOp (this, other) operator fun ExpressionWithColumnType.plus(other: Expression) : ExpressionWithColumnType = PlusOp (this, other, columnType) @@ -120,16 +121,16 @@ object SqlExpressionBuilder { infix fun ExpressionWithColumnType.notInList(list: Iterable): Op = InListOrNotInListOp(this, list, isInList = false) @Suppress("UNCHECKED_CAST") - fun ExpressionWithColumnType.asLiteral(value: S) = when (value) { + fun ExpressionWithColumnType.asLiteral(value: T) = when (value) { is Boolean -> booleanLiteral(value) is Int -> intLiteral(value) is Long -> longLiteral(value) is String -> stringLiteral(value) is DateTime -> if ((columnType as DateColumnType).time) dateTimeLiteral(value) else dateLiteral(value) - else -> LiteralOp(columnType, value) + else -> LiteralOp(columnType, value) } as LiteralOp - fun ExpressionWithColumnType.between(from: S, to: S): Op = Between(this, asLiteral(from), asLiteral(to)) + fun ExpressionWithColumnType.between(from: T, to: T): Op = Between(this, asLiteral(from), asLiteral(to)) fun ExpressionWithColumnType.intToDecimal(): ExpressionWithColumnType = NoOpConversion(this, DecimalColumnType(15, 0)) @@ -139,5 +140,5 @@ object SqlExpressionBuilder { } } - infix fun ExpressionWithColumnType.match(pattern: String): Op = match(pattern, null) + infix fun ExpressionWithColumnType.match(pattern: String): Op = match(pattern, null) } diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt b/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt index 1b542752e8..9557625298 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt @@ -187,15 +187,15 @@ open class Table(name: String = ""): ColumnSet(), DdlAware { } @Suppress("UNCHECKED_CAST") - fun Column.entityId(): Column> = replaceColumn(this, Column>(table, name, EntityIDColumnType(this)).apply { + fun > Column.entityId(): Column> = replaceColumn(this, Column>(table, name, EntityIDColumnType(this)).apply { this.indexInPK = this@entityId.indexInPK this.defaultValueFun = this@entityId.defaultValueFun?.let { { EntityID(it(), table as IdTable) } } }) - fun entityId(name: String, table: IdTable) : Column> { + fun > entityId(name: String, table: IdTable) : Column> { val originalColumn = (table.id.columnType as EntityIDColumnType<*>).idColumn val columnTypeCopy = originalColumn.columnType.let { (it as? AutoIncColumnType)?.delegate ?: it }.clone() - val answer = Column>(this, name, EntityIDColumnType(Column(table, name, columnTypeCopy))) + val answer = Column>(this, name, EntityIDColumnType(Column(table, name, columnTypeCopy))) _columns.add(answer) return answer } @@ -251,7 +251,7 @@ open class Table(name: String = ""): ColumnSet(), DdlAware { } - fun Column>.autoinc(idSeqName: String? = null): Column> = cloneWithAutoInc(idSeqName).apply { + fun > Column>.autoinc(idSeqName: String? = null): Column> = cloneWithAutoInc(idSeqName).apply { replaceColumn(this@autoinc, this) } @@ -262,7 +262,7 @@ open class Table(name: String = ""): ColumnSet(), DdlAware { infix fun > C.references(ref: Column): C = references(ref, null) - fun reference(name: String, foreign: IdTable, onDelete: ReferenceOption? = null): Column> = + fun > reference(name: String, foreign: IdTable, onDelete: ReferenceOption? = null): Column> = entityId(name, foreign).references(foreign.id, onDelete) fun Table.reference(name: String, pkColumn: Column): Column { @@ -271,7 +271,7 @@ open class Table(name: String = ""): ColumnSet(), DdlAware { return column } - fun optReference(name: String, foreign: IdTable, onDelete: ReferenceOption? = null): Column?> = + fun > optReference(name: String, foreign: IdTable, onDelete: ReferenceOption? = null): Column?> = entityId(name, foreign).references(foreign.id, onDelete).nullable() fun Column.nullable(): Column { @@ -279,7 +279,8 @@ open class Table(name: String = ""): ColumnSet(), DdlAware { newColumn.referee = referee newColumn.onDelete = onDelete newColumn.defaultValueFun = defaultValueFun - newColumn.dbDefaultValue = dbDefaultValue + @Suppress("UNCHECKED_CAST") + newColumn.dbDefaultValue = dbDefaultValue as Expression? newColumn.columnType.nullable = true return replaceColumn (this, newColumn) } diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/statements/BatchUpdateStatement.kt b/src/main/kotlin/org/jetbrains/exposed/sql/statements/BatchUpdateStatement.kt index 034550170f..985dfe9a42 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/statements/BatchUpdateStatement.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/statements/BatchUpdateStatement.kt @@ -25,7 +25,7 @@ class BatchUpdateStatement(val table: IdTable<*>): UpdateStatement(table, null) } } - override fun update(column: Column, value: Expression) = error("Expressions unsupported in batch update") + override fun update(column: Column, value: Expression) = error("Expressions unsupported in batch update") override fun prepareSQL(transaction: Transaction): String = "${super.prepareSQL(transaction)} WHERE ${transaction.identity(table.id)} = ?" @@ -36,11 +36,12 @@ class BatchUpdateStatement(val table: IdTable<*>): UpdateStatement(table, null) = data.map { it.second.map { it.key.columnType to it.value } + (table.id.columnType to it.first) } } -class EntityBatchUpdate(val klass: EntityClass>) { +class EntityBatchUpdate(val klass: EntityClass<*, Entity<*>>) { - private val data = ArrayList, SortedMap, Any?>>>() + private val data = ArrayList, SortedMap, Any?>>>() - fun addBatch(id: EntityID) { + fun addBatch(id: EntityID<*>) { + if (id.table != klass.table) error("Table from Entity ID ${id.table.tableName} differs from entity class ${klass.table.tableName}") data.add(id to TreeMap()) } diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/statements/UpdateBuilder.kt b/src/main/kotlin/org/jetbrains/exposed/sql/statements/UpdateBuilder.kt index ad22b6c034..628ef2b1f0 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/statements/UpdateBuilder.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/statements/UpdateBuilder.kt @@ -22,7 +22,7 @@ abstract class UpdateBuilder(type: StatementType, targets: List): values[column] = value } - open fun update(column: Column, value: Expression) { + open fun update(column: Column, value: Expression) { if (values.containsKey(column)) { error("$column is already initialized") } diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt b/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt index 4818d56813..4a34f499d4 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt @@ -43,7 +43,7 @@ open class DataTypeProvider { open class FunctionProvider { - open fun substring(expr: Expression, start: ExpressionWithColumnType, length: ExpressionWithColumnType, builder: QueryBuilder) : String = + open fun substring(expr: Expression, start: ExpressionWithColumnType, length: ExpressionWithColumnType, builder: QueryBuilder) : String = "SUBSTRING(${expr.toSQL(builder)}, ${start.toSQL(builder)}, ${length.toSQL(builder)})" open fun random(seed: Int?): String = "RANDOM(${seed?.toString().orEmpty()})" diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/vendors/OracleDialect.kt b/src/main/kotlin/org/jetbrains/exposed/sql/vendors/OracleDialect.kt index fd0e024a66..8cfad0284f 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/vendors/OracleDialect.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/vendors/OracleDialect.kt @@ -44,7 +44,7 @@ internal object OracleDataTypeProvider : DataTypeProvider() { internal object OracleFunctionProvider : FunctionProvider() { - override fun substring(expr: Expression, start: ExpressionWithColumnType, length: ExpressionWithColumnType, builder: QueryBuilder): String = + override fun substring(expr: Expression, start: ExpressionWithColumnType, length: ExpressionWithColumnType, builder: QueryBuilder): String = super.substring(expr, start, length, builder).replace("SUBSTRING", "SUBSTR") /* seed is ignored. You have to use dbms_random.seed function manually */ diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt b/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt index 67fd2d0751..d1711abc11 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt @@ -13,7 +13,7 @@ internal object SQLiteDataTypeProvider : DataTypeProvider() { } internal object SQLiteFunctionProvider : FunctionProvider() { - override fun substring(expr: Expression, start: ExpressionWithColumnType, length: ExpressionWithColumnType, builder: QueryBuilder): String = + override fun substring(expr: Expression, start: ExpressionWithColumnType, length: ExpressionWithColumnType, builder: QueryBuilder): String = super.substring(expr, start, length, builder).replace("SUBSTRING", "substr") } diff --git a/src/test/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt b/src/test/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt index f76013476f..e1a0a351c4 100644 --- a/src/test/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt +++ b/src/test/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt @@ -53,8 +53,8 @@ enum class TestDB(val connection: String, val driver: String, val user: String = } Unit }), - SQLSERVER("jdbc:sqlserver://${System.getProperty("exposed.test.sqlserver.host", "localhost")}" + - ":${System.getProperty("exposed.test.sqlserver.port", "1433")}", + SQLSERVER("jdbc:sqlserver://${System.getProperty("exposed.test.sqlserver.host", "192.168.99.100")}" + + ":${System.getProperty("exposed.test.sqlserver.port", "32781")}", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "SA", "yourStrong(!)Password"); companion object { diff --git a/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt b/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt index 6efad02a60..a17a468d2c 100644 --- a/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt +++ b/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt @@ -145,9 +145,9 @@ class DDLTests : DatabaseTestsBase() { @Test fun unnamedTableWithQuotesSQL() { withTables(UnnamedTable) { val q = db.identityQuoteString - val tableName = if (db.dialect.needsQuotesWhenSymbolsInNames) { "$q${"UnnamedTable$1".inProperCase()}$q" } else { "UnnamedTable$1".inProperCase() } - assertEquals("CREATE TABLE " + if (db.dialect.supportsIfNotExists) { "IF NOT EXISTS " } else { "" } + "$tableName " + - "(${"id".inProperCase()} ${db.dialect.dataTypeProvider.shortType()} PRIMARY KEY, ${"name".inProperCase()} VARCHAR(42) NOT NULL)", UnnamedTable.ddl) + val tableName = if (currentDialect.needsQuotesWhenSymbolsInNames) { "$q${"UnnamedTable$1".inProperCase()}$q" } else { "UnnamedTable$1".inProperCase() } + assertEquals("CREATE TABLE " + if (currentDialect.supportsIfNotExists) { "IF NOT EXISTS " } else { "" } + "$tableName " + + "(${"id".inProperCase()} ${currentDialect.dataTypeProvider.shortType()} PRIMARY KEY, ${"name".inProperCase()} VARCHAR(42) NOT NULL)", UnnamedTable.ddl) } } @@ -157,6 +157,9 @@ class DDLTests : DatabaseTestsBase() { withDb (TestDB.H2 ) { assertEquals("CREATE TABLE IF NOT EXISTS ${"test_named_table".inProperCase()}", TestTable.ddl) + DMLTestsData.Users.select { + exists(DMLTestsData.UserData.select { DMLTestsData.Users.id eq DMLTestsData.UserData.user_id }) + } } } @@ -170,9 +173,9 @@ class DDLTests : DatabaseTestsBase() { } withTables(excludeSettings = listOf(TestDB.MYSQL, TestDB.ORACLE), tables = TestTable) { - assertEquals("CREATE TABLE " + if (db.dialect.supportsIfNotExists) { "IF NOT EXISTS " } else { "" } + "${"different_column_types".inProperCase()} " + - "(${"id".inProperCase()} ${db.dialect.dataTypeProvider.shortAutoincType()} NOT NULL, ${"name".inProperCase()} VARCHAR(42) PRIMARY KEY, " + - "${"age".inProperCase()} ${db.dialect.dataTypeProvider.shortType()} NULL)", TestTable.ddl) + assertEquals("CREATE TABLE " + if (currentDialect.supportsIfNotExists) { "IF NOT EXISTS " } else { "" } + "${"different_column_types".inProperCase()} " + + "(${"id".inProperCase()} ${currentDialect.dataTypeProvider.shortAutoincType()} NOT NULL, ${"name".inProperCase()} VARCHAR(42) PRIMARY KEY, " + + "${"age".inProperCase()} ${currentDialect.dataTypeProvider.shortType()} NULL)", TestTable.ddl) } } @@ -184,8 +187,8 @@ class DDLTests : DatabaseTestsBase() { } withTables(excludeSettings = listOf(TestDB.MYSQL), tables = TestTable) { - assertEquals("CREATE TABLE " + if (db.dialect.supportsIfNotExists) { "IF NOT EXISTS " } else { "" } + "${"with_different_column_types".inProperCase()} " + - "(${"id".inProperCase()} ${db.dialect.dataTypeProvider.shortType()}, ${"name".inProperCase()} VARCHAR(42), ${"age".inProperCase()} ${db.dialect.dataTypeProvider.shortType()} NULL, " + + assertEquals("CREATE TABLE " + if (currentDialect.supportsIfNotExists) { "IF NOT EXISTS " } else { "" } + "${"with_different_column_types".inProperCase()} " + + "(${"id".inProperCase()} ${currentDialect.dataTypeProvider.shortType()}, ${"name".inProperCase()} VARCHAR(42), ${"age".inProperCase()} ${db.dialect.dataTypeProvider.shortType()} NULL, " + "CONSTRAINT pk_with_different_column_types PRIMARY KEY (${"id".inProperCase()}, ${"name".inProperCase()}))", TestTable.ddl) } } diff --git a/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DMLTests.kt b/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DMLTests.kt index e1a0e0b128..b27c1ac78a 100644 --- a/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DMLTests.kt +++ b/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DMLTests.kt @@ -2,8 +2,10 @@ package org.jetbrains.exposed.sql.tests.shared import org.hamcrest.Matchers.containsInAnyOrder import org.hamcrest.Matchers.not +import org.jetbrains.exposed.dao.IdTable import org.jetbrains.exposed.dao.IntIdTable import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.Random import org.jetbrains.exposed.sql.statements.BatchDataInconsistentException import org.jetbrains.exposed.sql.statements.BatchInsertStatement import org.jetbrains.exposed.sql.tests.DatabaseTestsBase @@ -12,6 +14,7 @@ import org.joda.time.DateTime import org.junit.Assert.assertThat import org.junit.Test import java.math.BigDecimal +import java.util.* import kotlin.test.* object DMLTestsData { @@ -413,7 +416,7 @@ class DMLTests : DatabaseTestsBase() { @Test fun testGroupBy06() { withCitiesAndUsers { cities, users, userData -> - val maxNullableId: Max = cities.id.max() + val maxNullableId = cities.id.max() cities.slice(maxNullableId).selectAll() .map { it[maxNullableId] }.let { result -> @@ -720,9 +723,23 @@ class DMLTests : DatabaseTestsBase() { } val row = tbl.selectAll().single() + tbl.select { + coalesce(tbl.dcn, intLiteral(0).intToDecimal()).isNull() +// tbl.tn.greaterEq(tbl.dcn) +// tbl.tn greater DateTime.now()!! + } tbl.checkRow(row, 42, null, date, null, time, null, DMLTestsData.E.ONE, null, DMLTestsData.E.ONE, null, "test", null, BigDecimal("239.42"), null) } } + + fun usdConversion(currencyAmount: ExpressionWithColumnType): ExpressionWithColumnType { + return NoOpConversion(Expression.build { + val coalesce = coalesce(currencyAmount, intLiteral(0).intToDecimal()) + coalesce / coalesce(DMLTestsData.Misc.t, intLiteral(1).intToDecimal()) + }, DecimalColumnType(15, 2)) + } + + @Test fun testInsert03() { val tbl = DMLTestsData.Misc val date = today @@ -858,6 +875,21 @@ class DMLTests : DatabaseTestsBase() { } } + @Test fun testGeneratedKey04() { + val CharIdTable = object : IdTable("charId") { + override val id = varchar("id", 50).primaryKey() + .clientDefault { UUID.randomUUID().toString() } + .entityId() + val foo = integer("foo") + } + withTables(CharIdTable){ + val id = IntIdTestTable.insertAndGetId { + it[CharIdTable.foo] = 5 + } + assertNotNull(id?.value) + } + } + /* Test fun testInsert05() { val stringThatNeedsEscaping = "multi\r\nline" @@ -954,6 +986,10 @@ class DMLTests : DatabaseTestsBase() { } + tbl.select { + tbl.nn.neq(tbl.nn) + } + tbl.checkRow(tbl.select{tbl.nn.eq(42)}.single(), 42, 42, date, date, time, time, eOne, eOne, eOne, eOne, sTest, sTest, dec, dec) tbl.checkRow(tbl.select{tbl.nn neq null }.single(), 42, 42, date, date, time, time, eOne, eOne, eOne, eOne, sTest, sTest, dec, dec)