Skip to content

Commit

Permalink
Function class reworked
Browse files Browse the repository at this point in the history
  • Loading branch information
Tapac committed Mar 8, 2018
1 parent 6e0ae85 commit 40af152
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/main/kotlin/org/jetbrains/exposed/sql/Alias.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class QueryAlias(val query: Query, val alias: String): ColumnSet() {

fun <T:Table> T.alias(alias: String) = Alias(this, alias)
fun <T:Query> T.alias(alias: String) = QueryAlias(this, alias)
fun <T:Expression<*>> T.alias(alias: String) = ExpressionAlias(this, alias)
fun <T> Expression<T>.alias(alias: String) = ExpressionAlias(this, alias)

fun Join.joinQuery(on: (SqlExpressionBuilder.(QueryAlias)->Op<Boolean>), joinType: JoinType = JoinType.INNER, joinPart: () -> Query): Join {
val qAlias = joinPart().alias("q${joinParts.count { it.joinPart is QueryAlias }}")
Expand Down
87 changes: 27 additions & 60 deletions src/main/kotlin/org/jetbrains/exposed/sql/Function.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,123 +4,89 @@ import org.joda.time.DateTime
import java.math.BigDecimal
import java.util.*

abstract class Function<T> : ExpressionWithColumnType<T>()
abstract class Function<T>(override val columnType: IColumnType) : ExpressionWithColumnType<T>()

class Count(val expr: Expression<*>, val distinct: Boolean = false): Function<Int>() {
class Count(val expr: Expression<*>, val distinct: Boolean = false): Function<Int>(IntegerColumnType()) {
override fun toSQL(queryBuilder: QueryBuilder): String =
"COUNT(${if (distinct) "DISTINCT " else ""}${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = IntegerColumnType()
}

class Date<T:DateTime?>(val expr: Expression<T>): Function<DateTime>() {
class Date<T:DateTime?>(val expr: Expression<T>): Function<DateTime>(DateColumnType(false)) {
override fun toSQL(queryBuilder: QueryBuilder): String = "DATE(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = DateColumnType(false)
}

class CurrentDateTime : Function<DateTime>() {
class CurrentDateTime : Function<DateTime>(DateColumnType(false)) {
override fun toSQL(queryBuilder: QueryBuilder) = "CURRENT_TIMESTAMP"
override val columnType: IColumnType = DateColumnType(false)
}

class Month<T:DateTime?>(val expr: Expression<T>): Function<DateTime>() {
class Month<T:DateTime?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
override fun toSQL(queryBuilder: QueryBuilder): String = "MONTH(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = DateColumnType(false)
}

class LowerCase<T: String?>(val expr: Expression<T>) : Function<T>() {
class LowerCase<T: String?>(val expr: Expression<T>) : Function<T>(VarCharColumnType()) {
override fun toSQL(queryBuilder: QueryBuilder): String = "LOWER(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = VarCharColumnType()
}

class UpperCase<T: String?>(val expr: Expression<T>) : Function<T>() {
class UpperCase<T: String?>(val expr: Expression<T>) : Function<T>(VarCharColumnType()) {
override fun toSQL(queryBuilder: QueryBuilder): String = "UPPER(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = VarCharColumnType()
}

class Min<T:Comparable<T>, in S:T?>(val expr: Expression<in S>, _columnType: IColumnType): Function<T?>() {
class Min<T:Comparable<T>, in S:T?>(val expr: Expression<in S>, _columnType: IColumnType): Function<T?>(_columnType) {
override fun toSQL(queryBuilder: QueryBuilder): String = "MIN(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = _columnType
}

class Max<T:Comparable<T>, in S:T?>(val expr: Expression<in S>, _columnType: IColumnType): Function<T?>() {
class Max<T:Comparable<T>, in S:T?>(val expr: Expression<in S>, _columnType: IColumnType): Function<T?>(_columnType) {
override fun toSQL(queryBuilder: QueryBuilder): String = "MAX(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = _columnType
}

class Avg<T:Comparable<T>, in S:T?>(val expr: Expression<in S>, scale: Int): Function<BigDecimal?>() {
class Avg<T:Comparable<T>, in S:T?>(val expr: Expression<in S>, scale: Int): Function<BigDecimal?>(DecimalColumnType(Int.MAX_VALUE, scale)) {
override fun toSQL(queryBuilder: QueryBuilder): String = "AVG(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale)
}

class StdDevPop<T>(val expr: Expression<T>, scale: Int): Function<BigDecimal?>() {
class StdDevPop<T>(val expr: Expression<T>, scale: Int): Function<BigDecimal?>(DecimalColumnType(Int.MAX_VALUE, scale)) {
override fun toSQL(queryBuilder: QueryBuilder): String = "STDDEV_POP(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale)
}

class StdDevSamp<T>(val expr: Expression<T>, scale: Int): Function<BigDecimal?>() {
class StdDevSamp<T>(val expr: Expression<T>, scale: Int): Function<BigDecimal?>(DecimalColumnType(Int.MAX_VALUE, scale)) {
override fun toSQL(queryBuilder: QueryBuilder): String = "STDDEV_SAMP(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale)
}

class VarPop<T>(val expr: Expression<T>, scale: Int): Function<BigDecimal?>() {
class VarPop<T>(val expr: Expression<T>, scale: Int): Function<BigDecimal?>(DecimalColumnType(Int.MAX_VALUE, scale)) {
override fun toSQL(queryBuilder: QueryBuilder): String = "VAR_POP(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale)
}

class VarSamp<T>(val expr: Expression<T>, scale: Int): Function<BigDecimal?>() {
class VarSamp<T>(val expr: Expression<T>, scale: Int): Function<BigDecimal?>(DecimalColumnType(Int.MAX_VALUE, scale)) {
override fun toSQL(queryBuilder: QueryBuilder): String = "VAR_SAMP(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = DecimalColumnType(Int.MAX_VALUE, scale)
}

class Sum<T>(val expr: Expression<T>, _columnType: IColumnType): Function<T?>() {
class Sum<T>(val expr: Expression<T>, _columnType: IColumnType): Function<T?>(_columnType) {
override fun toSQL(queryBuilder: QueryBuilder): String = "SUM(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = _columnType
}

class Coalesce<out T, S:T?, R:T>(private val expr: ExpressionWithColumnType<S>, private val alternate: ExpressionWithColumnType<out T>): Function<R>() {
class Coalesce<out T, S:T?, R:T>(private val expr: ExpressionWithColumnType<S>,
private val alternate: ExpressionWithColumnType<out T>): Function<R>(alternate.columnType) {
override fun toSQL(queryBuilder: QueryBuilder): String =
"COALESCE(${expr.toSQL(queryBuilder)}, ${alternate.toSQL(queryBuilder)})"

override val columnType: IColumnType = alternate.columnType
}

class Substring<T:String?>(private val expr: Expression<T>, private val start: ExpressionWithColumnType<Int>, val length: ExpressionWithColumnType<Int>): Function<T>() {
class Substring<T:String?>(private val expr: Expression<T>, private val start: Expression<Int>,
val length: Expression<Int>): Function<T>(VarCharColumnType()) {
override fun toSQL(queryBuilder: QueryBuilder): String
= currentDialect.functionProvider.substring(expr, start, length, queryBuilder)

override val columnType: IColumnType = VarCharColumnType()
}


class Random(val seed: Int? = null) : Function<BigDecimal>() {
override fun toSQL(queryBuilder: QueryBuilder): String
= currentDialect.functionProvider.random(seed)

override val columnType: IColumnType = DecimalColumnType(38, 20)
class Random(val seed: Int? = null) : Function<BigDecimal>(DecimalColumnType(38, 20)) {
override fun toSQL(queryBuilder: QueryBuilder) = currentDialect.functionProvider.random(seed)
}

class Cast<T>(val expr: Expression<*>, override val columnType: IColumnType) : Function<T?>() {
class Cast<T>(val expr: Expression<*>, columnType: IColumnType) : Function<T?>(columnType) {
override fun toSQL(queryBuilder: QueryBuilder): String
= currentDialect.functionProvider.cast(expr, columnType, queryBuilder)
}

class Trim<T:String?>(val expr: Expression<T>): Function<T>() {
class Trim<T:String?>(val expr: Expression<T>): Function<T>(VarCharColumnType()) {
override fun toSQL(queryBuilder: QueryBuilder): String = "TRIM(${expr.toSQL(queryBuilder)})"

override val columnType: IColumnType = VarCharColumnType()
}

class Case(val value: Expression<*>? = null) {
Expand Down Expand Up @@ -154,7 +120,10 @@ class CaseWhenElse<T, R:T> (val caseWhen: CaseWhen<T>, val elseResult: Expressio
}
}

class GroupConcat(val expr: Column<*>, val separator: String?, val distinct: Boolean, vararg val orderBy: Pair<Expression<*>,Boolean>): Function<String?>() {
class GroupConcat(val expr: Column<*>,
val separator: String?,
val distinct: Boolean,
vararg val orderBy: Pair<Expression<*>, Boolean>): Function<String?>(VarCharColumnType()) {
override fun toSQL(queryBuilder: QueryBuilder): String = buildString {
append("GROUP_CONCAT(")
if (distinct)
Expand All @@ -174,6 +143,4 @@ class GroupConcat(val expr: Column<*>, val separator: String?, val distinct: Boo
}
append(")")
}

override val columnType: IColumnType = VarCharColumnType()
}
4 changes: 2 additions & 2 deletions src/main/kotlin/org/jetbrains/exposed/sql/Query.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ResultRow(size: Int, private val fieldIndex: Map<Expression<*>, Int>) {
} as T
}

operator fun <T> set(c: Expression<T>, value: T) {
operator fun <T> set(c: Expression<out T>, value: T) {
val index = fieldIndex[c] ?: error("${c.toSQL(QueryBuilder(false))} is not in record set")
data[index] = value
}
Expand Down Expand Up @@ -64,7 +64,7 @@ class ResultRow(size: Int, private val fieldIndex: Map<Expression<*>, Int>) {
internal fun create(columns : List<Column<*>>): ResultRow =
ResultRow(columns.size, columns.mapIndexed { i, c -> c to i }.toMap()).apply {
columns.forEach {
this[it as Expression<Any?>] = it.defaultValueFun?.invoke() ?: if (!it.columnType.nullable) NotInitializedValue else null
this[it] = it.defaultValueFun?.invoke() ?: if (!it.columnType.nullable) NotInitializedValue else null
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,17 @@ object SqlExpressionBuilder {
return EqOp(this, wrap(t))
}

infix fun<T, S1: T?, S2: T?> Expression<S1>.eq(other: Expression<S2>) : Op<Boolean> = EqOp (this, other)
infix fun<T, S1: T?, S2: T?> Expression<in S1>.eq(other: Expression<in S2>) : Op<Boolean> = EqOp (this, other)

infix fun<T> ExpressionWithColumnType<T>.neq(other: T): Op<Boolean> {
infix fun <T> ExpressionWithColumnType<T>.neq(other: T): Op<Boolean> {
if (other == null) {
return isNotNull()
}

return NeqOp(this, wrap(other))
}

fun<T, S: T?> ExpressionWithColumnType<S>.neq(other: Expression<S>) : Op<Boolean> = NeqOp (this, other)
infix fun <T, S1: T?, S2: T?> ExpressionWithColumnType<in S1>.neq(other: Expression<in S2>) : Op<Boolean> = NeqOp (this, other)

fun<T> ExpressionWithColumnType<T>.isNull(): Op<Boolean> = IsNullOp(this)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ open class DataTypeProvider {

open class FunctionProvider {

open fun<T:String?> substring(expr: Expression<T>, start: ExpressionWithColumnType<Int>, length: ExpressionWithColumnType<Int>, builder: QueryBuilder) : String =
open fun<T:String?> substring(expr: Expression<T>, start: Expression<Int>, length: Expression<Int>, builder: QueryBuilder) : String =
"SUBSTRING(${expr.toSQL(builder)}, ${start.toSQL(builder)}, ${length.toSQL(builder)})"

open fun random(seed: Int?): String = "RANDOM(${seed?.toString().orEmpty()})"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ internal object OracleDataTypeProvider : DataTypeProvider() {

internal object OracleFunctionProvider : FunctionProvider() {

override fun<T:String?> substring(expr: Expression<T>, start: ExpressionWithColumnType<Int>, length: ExpressionWithColumnType<Int>, builder: QueryBuilder): String =
override fun<T:String?> substring(expr: Expression<T>, start: Expression<Int>, length: Expression<Int>, builder: QueryBuilder): String =
super.substring(expr, start, length, builder).replace("SUBSTRING", "SUBSTR")

/* seed is ignored. You have to use dbms_random.seed function manually */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.jetbrains.exposed.sql.vendors

import org.jetbrains.exposed.sql.Expression
import org.jetbrains.exposed.sql.ExpressionWithColumnType
import org.jetbrains.exposed.sql.Index
import org.jetbrains.exposed.sql.QueryBuilder

Expand All @@ -14,7 +13,7 @@ internal object SQLiteDataTypeProvider : DataTypeProvider() {
}

internal object SQLiteFunctionProvider : FunctionProvider() {
override fun<T:String?> substring(expr: Expression<T>, start: ExpressionWithColumnType<Int>, length: ExpressionWithColumnType<Int>, builder: QueryBuilder): String =
override fun<T:String?> substring(expr: Expression<T>, start: Expression<Int>, length: Expression<Int>, builder: QueryBuilder): String =
super.substring(expr, start, length, builder).replace("SUBSTRING", "substr")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1170,7 +1170,7 @@ class DMLTests : DatabaseTestsBase() {
}

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)
tbl.checkRow(tbl.select { tbl.nn.neq<Int?>(null) }.single(), 42, 42, date, date, time, time, eOne, eOne, eOne, eOne, sTest, sTest, dec, dec)

tbl.checkRow(tbl.select { tbl.dn.eq(date) }.single(), 42, 42, date, date, time, time, eOne, eOne, eOne, eOne, sTest, sTest, dec, dec)
tbl.checkRow(tbl.select { tbl.dn.isNotNull() }.single(), 42, 42, date, date, time, time, eOne, eOne, eOne, eOne, sTest, sTest, dec, dec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ class EntityTests: DatabaseTestsBase() {

@Test
fun testInsertChildWithoutFlush() {
withTables(Posts) {
withTables(Boards, Posts) {
val parent = Post.new { }
Post.new { this.parent = parent } // first flush before referencing
assertEquals(2, Post.all().count())
Expand Down Expand Up @@ -335,7 +335,7 @@ class EntityTests: DatabaseTestsBase() {

@Test
fun testInsertChildWithFlush() {
withTables(Posts) {
withTables(Boards, Posts) {
val parent = Post.new { }
flushCache()
assertNotNull(parent.id._value)
Expand All @@ -346,7 +346,7 @@ class EntityTests: DatabaseTestsBase() {

@Test
fun testInsertChildWithChild() {
withTables(Posts) {
withTables(Boards, Posts) {
val parent = Post.new { }
val child1 = Post.new { this.parent = parent }
Post.new { this.parent = child1 }
Expand Down

0 comments on commit 40af152

Please sign in to comment.