Skip to content

Commit

Permalink
Add adjustHaving, andHaving, orHaving extension methods for `Qu…
Browse files Browse the repository at this point in the history
…ery` (JetBrains#1412)

Add `adjustHaving`, `andHaving`, `orHaving` extension methods
  • Loading branch information
naftalmm authored Dec 27, 2021
1 parent 21bd328 commit b5e57c8
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 16 deletions.
39 changes: 33 additions & 6 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ open class Query(override var set: FieldSet, where: Op<Boolean>?) : AbstractQuer

/**
* Changes [set.fields] field of a Query, [set.source] will be preserved
* @param body builder for new column set, current [set.source] used as a receiver and current [set] as an , you are expected to slice it
* @sample org.jetbrains.exposed.sql.tests.shared.DMLTests.testAdjustQuerySlice
* @param body builder for new column set, current [set.source] used as a receiver and current [set] as an argument, you are expected to slice it
* @sample org.jetbrains.exposed.sql.tests.shared.dml.AdjustQueryTests.testAdjustQuerySlice
*/
fun adjustSlice(body: ColumnSet.(FieldSet) -> FieldSet): Query = apply { set = set.source.body(set) }

/**
* Changes [set.source] field of a Query, [set.fields] will be preserved
* @param body builder for new column set, previous value used as a receiver
* @sample org.jetbrains.exposed.sql.tests.shared.DMLTests.testAdjustQueryColumnSet
* @sample org.jetbrains.exposed.sql.tests.shared.dml.AdjustQueryTests.testAdjustQueryColumnSet
*/
fun adjustColumnSet(body: ColumnSet.() -> ColumnSet): Query {
return adjustSlice { oldSlice -> body().slice(oldSlice.fields) }
Expand All @@ -80,10 +80,17 @@ open class Query(override var set: FieldSet, where: Op<Boolean>?) : AbstractQuer
/**
* Changes [where] field of a Query.
* @param body new WHERE condition builder, previous value used as a receiver
* @sample org.jetbrains.exposed.sql.tests.shared.DMLTests.testAdjustQueryWhere
* @sample org.jetbrains.exposed.sql.tests.shared.dml.AdjustQueryTests.testAdjustQueryWhere
*/
fun adjustWhere(body: Op<Boolean>?.() -> Op<Boolean>): Query = apply { where = where.body() }

/**
* Changes [having] field of a Query.
* @param body new HAVING condition builder, previous value used as a receiver
* @sample org.jetbrains.exposed.sql.tests.shared.dml.AdjustQueryTests.testAdjustQueryHaving
*/
fun adjustHaving(body: Op<Boolean>?.() -> Op<Boolean>): Query = apply { having = having.body() }

fun hasCustomForUpdateState() = forUpdate != null
fun isForUpdate() = (forUpdate ?: false) && currentDialect.supportsSelectForUpdate()

Expand Down Expand Up @@ -223,11 +230,31 @@ fun Query.andWhere(andPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustWher
}

/**
* Mutate Query instance and add `andPart` to where condition with `or` operator.
* Mutate Query instance and add `orPart` to where condition with `or` operator.
* @return same Query instance which was provided as a receiver.
*/
fun Query.orWhere(andPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustWhere {
fun Query.orWhere(orPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustWhere {
val expr = Op.build { orPart() }
if (this == null) expr
else this or expr
}

/**
* Mutate Query instance and add `andPart` to having condition with `and` operator.
* @return same Query instance which was provided as a receiver.
*/
fun Query.andHaving(andPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustHaving {
val expr = Op.build { andPart() }
if (this == null) expr
else this and expr
}

/**
* Mutate Query instance and add `orPart` to having condition with `or` operator.
* @return same Query instance which was provided as a receiver.
*/
fun Query.orHaving(orPart: SqlExpressionBuilder.() -> Op<Boolean>) = adjustHaving {
val expr = Op.build { orPart() }
if (this == null) expr
else this or expr
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ class AdjustQueryTests : DatabaseTestsBase() {
}
}

private fun Op<Boolean>.repr(): String {
val builder = QueryBuilder(false)
builder.append(this)
return builder.toString()
}

@Test
fun testAdjustQueryWhere() {
withCitiesAndUsers { cities, users, _ ->
Expand All @@ -62,11 +68,6 @@ class AdjustQueryTests : DatabaseTestsBase() {
predicate
}
val actualWhere = queryAdjusted.where
fun Op<Boolean>.repr(): String {
val builder = QueryBuilder(false)
builder.append(this)
return builder.toString()
}

assertEquals(predicate.repr(), actualWhere!!.repr())
assertQueryResultValid(queryAdjusted)
Expand All @@ -84,17 +85,108 @@ class AdjustQueryTests : DatabaseTestsBase() {
predicate
}
val actualWhere = queryAdjusted.where
fun Op<Boolean>.repr(): String {
val builder = QueryBuilder(false)
builder.append(this)
return builder.toString()
}

assertEquals((predicate.and(predicate)).repr(), actualWhere!!.repr())
assertQueryResultValid(queryAdjusted)
}
}

@Test
fun testQueryOrWhere() {
withCitiesAndUsers { cities, users, _ ->
val queryAdjusted = (users innerJoin cities)
.slice(users.name, cities.name)
.select { predicate }

queryAdjusted.orWhere {
predicate
}
val actualWhere = queryAdjusted.where

assertEquals((predicate.or(predicate)).repr(), actualWhere!!.repr())
assertQueryResultValid(queryAdjusted)
}
}

@Test
fun testAdjustQueryHaving() {
withCitiesAndUsers { cities, users, _ ->
val predicateHaving = Op.build {
DMLTestsData.Users.id.count() eq DMLTestsData.Cities.id.max()
}

val queryAdjusted = (cities innerJoin users)
.slice(cities.name)
.selectAll()
.groupBy(cities.name)
queryAdjusted.adjustHaving {
assertNull(this)
predicateHaving
}
val actualHaving = queryAdjusted.having

assertEquals(predicateHaving.repr(), actualHaving!!.repr())
val r = queryAdjusted.orderBy(cities.name).toList()
assertEquals(2, r.size)
assertEquals("Munich", r[0][cities.name])
assertEquals("St. Petersburg", r[1][cities.name])
}
}

@Test
fun testQueryAndHaving() {
withCitiesAndUsers { cities, users, _ ->
val predicateHaving = Op.build {
DMLTestsData.Users.id.count() eq DMLTestsData.Cities.id.max()
}

val queryAdjusted = (cities innerJoin users)
.slice(cities.name)
.selectAll()
.groupBy(cities.name)
.having { predicateHaving }

queryAdjusted.andHaving {
predicateHaving
}

val actualHaving = queryAdjusted.having
assertEquals((predicateHaving.and(predicateHaving)).repr(), actualHaving!!.repr())

val r = queryAdjusted.orderBy(cities.name).toList()
assertEquals(2, r.size)
assertEquals("Munich", r[0][cities.name])
assertEquals("St. Petersburg", r[1][cities.name])
}
}

@Test
fun testQueryOrHaving() {
withCitiesAndUsers { cities, users, _ ->
val predicateHaving = Op.build {
DMLTestsData.Users.id.count() eq DMLTestsData.Cities.id.max()
}

val queryAdjusted = (cities innerJoin users)
.slice(cities.name)
.selectAll()
.groupBy(cities.name)
.having { predicateHaving }

queryAdjusted.orHaving {
predicateHaving
}

val actualHaving = queryAdjusted.having
assertEquals((predicateHaving.or(predicateHaving)).repr(), actualHaving!!.repr())

val r = queryAdjusted.orderBy(cities.name).toList()
assertEquals(2, r.size)
assertEquals("Munich", r[0][cities.name])
assertEquals("St. Petersburg", r[1][cities.name])
}
}

private val predicate = Op.build {
val nameCheck = (DMLTestsData.Users.id eq "andrey") or (DMLTestsData.Users.name eq "Sergey")
val cityCheck = DMLTestsData.Users.cityId eq DMLTestsData.Cities.id
Expand Down

0 comments on commit b5e57c8

Please sign in to comment.