Skip to content

Commit

Permalink
#469 Compound operations / TRUE/FALSE operators / OR operator optimiz…
Browse files Browse the repository at this point in the history
…ation
  • Loading branch information
Tapac committed Jan 30, 2019
1 parent 4322a24 commit 19199b8
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 17 deletions.
42 changes: 25 additions & 17 deletions src/main/kotlin/org/jetbrains/exposed/sql/Op.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@ abstract class Op<T> : Expression<T>() {
companion object {
inline fun <T> build(op: SqlExpressionBuilder.() -> Op<T>): Op<T> = SqlExpressionBuilder.op()
}

object TRUE : Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder): String = "TRUE"
}
object FALSE : Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder): String = "FALSE"
}
}

infix fun Op<Boolean>.and(op: Expression<Boolean>): Op<Boolean> = AndOp(this, op)

infix fun Op<Boolean>.or(op: Expression<Boolean>): Op<Boolean> = OrOp(this, op)

fun List<Op<Boolean>>.compoundAnd() = reduce { op, nextOp -> op and nextOp }
fun List<Op<Boolean>>.compoundOr() = reduce { op, nextOp -> op or nextOp }

fun not(op: Expression<Boolean>): Op<Boolean> = NotOp(op)

class IsNullOp(val expr: Expression<*>) : Op<Boolean>() {
Expand Down Expand Up @@ -43,8 +53,8 @@ class InListOrNotInListOp<T>(val expr: ExpressionWithColumnType<T>, val list: It
override fun toSQL(queryBuilder: QueryBuilder): String = buildString {
list.iterator().let { i ->
if (!i.hasNext()) {
val expr = Op.build { booleanLiteral(!isInList) eq booleanLiteral(true) }
append(expr.toSQL(queryBuilder))
val op = if (isInList) Op.FALSE else Op.TRUE
append(op.toSQL(queryBuilder))
} else {
val first = i.next()
if (!i.hasNext()) {
Expand Down Expand Up @@ -91,13 +101,13 @@ fun dateTimeLiteral(value: DateTime): LiteralOp<DateTime> = LiteralOp(DateColumn

abstract class ComparisonOp(val expr1: Expression<*>, val expr2: Expression<*>, val opSign: String) : Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder) = buildString {
if (expr1 is OrOp<*>) {
if (expr1 is OrOp) {
append("(").append(expr1.toSQL(queryBuilder)).append(")")
} else {
append(expr1.toSQL(queryBuilder))
}
append(" $opSign ")
if (expr2 is OrOp<*>) {
if (expr2 is OrOp) {
append("(").append(expr2.toSQL(queryBuilder)).append(")")
} else {
append(expr2.toSQL(queryBuilder))
Expand All @@ -115,27 +125,25 @@ class LikeOp(expr1: Expression<*>, expr2: Expression<*>) : ComparisonOp(expr1, e
class NotLikeOp(expr1: Expression<*>, expr2: Expression<*>) : ComparisonOp(expr1, expr2, "NOT LIKE")
class RegexpOp(expr1: Expression<*>, expr2: Expression<*>) : ComparisonOp(expr1, expr2, "REGEXP")
class NotRegexpOp(expr1: Expression<*>, expr2: Expression<*>) : ComparisonOp(expr1, expr2, "NOT REGEXP")
class AndOp(expr1: Expression<Boolean>, expr2: Expression<Boolean>) : ComparisonOp(expr1, expr2, "AND")

class AndOp(val expr1: Expression<Boolean>, val expr2: Expression<Boolean>) : Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder): String = buildString {
if (expr1 is OrOp<*>) {
append("(").append(expr1.toSQL(queryBuilder)).append(")")
} else {
class OrOp(val expr1: Expression<Boolean>, val expr2: Expression<Boolean>): Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder) : String = buildString {
if (expr1 is OrOp) {
append(expr1.toSQL(queryBuilder))
}
append(" and ")
if (expr2 is OrOp<*>) {
append("(").append(expr2.toSQL(queryBuilder)).append(")")
} else {
append('(').append(expr1.toSQL(queryBuilder)).append(")")
}
append(" OR ")

if (expr2 is OrOp) {
append(expr2.toSQL(queryBuilder))
} else {
append('(').append(expr2.toSQL(queryBuilder)).append(")")
}
}
}

class OrOp<T>(val expr1: Expression<T>, val expr2: Expression<T>): Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder) = "(${expr1.toSQL(queryBuilder)}) OR (${expr2.toSQL(queryBuilder)})"
}

class NotOp<T>(val expr: Expression<T>) : Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder) = "NOT (${expr.toSQL(queryBuilder)})"
}
Expand Down
20 changes: 20 additions & 0 deletions src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DMLTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,26 @@ class DMLTests : DatabaseTestsBase() {
assertEquals(1, OrgMemberships.selectAll().count())
}
}

@Test
fun testCompoundOp() {
withCitiesAndUsers { cities, users, _ ->
val allUsers = setOf(
"Andrey",
"Sergey",
"Eugene",
"Alex",
"Something"
)
val orOp = allUsers.map { Op.build { users.name eq it } }.compoundOr()
val userNamesOr = users.select(orOp).map { it[users.name] }.toSet()
assertEquals(allUsers, userNamesOr)

val andOp = allUsers.map { Op.build { users.name eq it } }.compoundAnd()
assertEquals(0, users.select(andOp).count())
}
}

}

private val today: DateTime = DateTime.now().withTimeAtStartOfDay()
Expand Down

0 comments on commit 19199b8

Please sign in to comment.