New Query features: slice/columnSet/where adjustments, fields visibility; Minor fixes

This commit is contained in:
alexander.burkov
2017-07-04 17:22:58 +03:00
parent ce6af60162
commit e1af10917d
8 changed files with 138 additions and 28 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
.gradle
build
classes
out/test/resources

View File

@@ -125,8 +125,8 @@ allprojects {
testCompile 'org.slf4j:slf4j-log4j12:1.7.25'
testCompile 'log4j:log4j:1.2.17'
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'com.h2database:h2:1.4.194'
}
}

View File

@@ -1,3 +1,3 @@
group=org.jetbrains.exposed
version=0.9-SNAPSHOT
kotlin_version=1.1.2-3
version=0.8.1-SNAPSHOT
kotlin_version=1.1.3

View File

@@ -75,15 +75,48 @@ class ResultRow(size: Int, private val fieldIndex: Map<Expression<*>, Int>) {
}
}
open class Query(val transaction: Transaction, val set: FieldSet, val where: Op<Boolean>?): SizedIterable<ResultRow>, Statement<ResultSet>(StatementType.SELECT, set.source.targetTables()) {
private val groupedByColumns = ArrayList<Expression<*>>()
private val orderByColumns = ArrayList<Pair<Expression<*>, Boolean>>()
private var having: Op<Boolean>? = null
private var limit: Int? = null
private var offset: Int = 0
private var distinct: Boolean = false
private var count: Boolean = false
open class Query(val transaction: Transaction, set: FieldSet, where: Op<Boolean>?): SizedIterable<ResultRow>, Statement<ResultSet>(StatementType.SELECT, set.source.targetTables()) {
var groupedByColumns: List<Expression<*>> = mutableListOf()
private set
var orderByColumns: List<Pair<Expression<*>, Boolean>> = mutableListOf()
private set
var having: Op<Boolean>? = null
private set
var distinct: Boolean = false
private set
private var forUpdate: Boolean? = null
var set: FieldSet = set
private set
var where: Op<Boolean>? = where
private set
var limit: Int? = null
private set
var offset: Int = 0
private set
/**
* 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, you are expected to slice it
* @sample org.jetbrains.exposed.sql.tests.shared.DMLTests.testAdjustQuerySlice
*/
fun adjustSlice(body: ColumnSet.() -> FieldSet): Query = apply { set = set.source.body() }
/**
* 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
*/
fun adjustColumnSet(body: ColumnSet.() -> ColumnSet): Query {
val oldSlice = set.fields
return adjustSlice { body().slice(oldSlice) }
}
/**
* 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
*/
fun adjustWhere(body: Op<Boolean>?.() -> Op<Boolean>): Query = apply { where = where.body() }
fun hasCustomForUpdateState() = forUpdate != null
fun isForUpdate() = (forUpdate ?: transaction.selectsForUpdate) && transaction.db.dialect.supportsSelectForUpdate()
@@ -112,9 +145,9 @@ open class Query(val transaction: Transaction, val set: FieldSet, val where: Op<
append(" FROM ")
append(set.source.describe(transaction))
if (where != null) {
where?.let {
append(" WHERE ")
append(where.toSQL(builder))
append(it.toSQL(builder))
}
if (!count) {
@@ -156,14 +189,14 @@ open class Query(val transaction: Transaction, val set: FieldSet, val where: Op<
return this
}
fun withDistinct() : Query {
distinct = true
fun withDistinct(value: Boolean = true) : Query {
distinct = value
return this
}
fun groupBy(vararg columns: Expression<*>): Query {
for (column in columns) {
groupedByColumns.add(column)
(groupedByColumns as MutableList).add(column)
}
return this
}
@@ -179,13 +212,13 @@ open class Query(val transaction: Transaction, val set: FieldSet, val where: Op<
}
fun orderBy (column: Expression<*>, isAsc: Boolean = true) : Query {
orderByColumns.add(column to isAsc)
(orderByColumns as MutableList).add(column to isAsc)
return this
}
fun orderBy (vararg columns: Pair<Column<*>,Boolean>) : Query {
for (pair in columns) {
orderByColumns.add(pair)
(orderByColumns as MutableList).add(pair)
}
return this
}
@@ -231,6 +264,7 @@ open class Query(val transaction: Transaction, val set: FieldSet, val where: Op<
return ResultIterator(transaction.exec(this)!!)
}
private var count: Boolean = false
override fun count(): Int {
flushEntities()

View File

@@ -158,9 +158,12 @@ object SchemaUtils {
}
fun drop(vararg tables: Table) {
EntityCache.sortTablesByReferences(tables.toList()).reversed().filter { it in tables}.flatMap { it.dropStatement() }.forEach {
TransactionManager.current().exec(it)
}
EntityCache.sortTablesByReferences(tables.toList()).reversed()
.filter { it in tables && it.exists() }
.flatMap { it.dropStatement() }
.forEach {
TransactionManager.current().exec(it)
}
currentDialect.resetCaches()
}
}

View File

@@ -24,7 +24,8 @@ interface FieldSet {
abstract class ColumnSet : FieldSet {
abstract val columns: List<Column<*>>
override val fields: List<Expression<*>> get() = columns
override val source get() = this
override val source
get() = this
abstract fun describe(s: Transaction): String
@@ -220,7 +221,7 @@ open class Table(name: String = ""): ColumnSet(), DdlAware {
fun blob(name: String): Column<Blob> = registerColumn(name, BlobColumnType())
fun text(name: String): Column<String> = registerColumn(name, StringColumnType())
fun text(name: String): Column<String> = registerColumn(name, StringColumnType(length = 65535))
fun binary(name: String, length: Int): Column<ByteArray> = registerColumn(name, BinaryColumnType(length))

View File

@@ -8,7 +8,7 @@ import java.sql.ResultSet
import java.util.*
class BatchInsertStatement(table: Table, ignore: Boolean = false): InsertStatement<List<Map<Column<*>, Any>>>(table, ignore) {
open class BatchInsertStatement(table: Table, ignore: Boolean = false): InsertStatement<List<Map<Column<*>, Any>>>(table, ignore) {
override val flushCache: Boolean = false
// REVIEW

View File

@@ -1,16 +1,15 @@
package org.jetbrains.exposed.sql.tests.shared
import org.hamcrest.Matchers.*
import org.jetbrains.exposed.dao.IntIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.tests.DatabaseTestsBase
import org.jetbrains.exposed.sql.tests.TestDB
import org.joda.time.DateTime
import org.junit.Assert.assertThat
import org.junit.Test
import java.math.BigDecimal
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.*
object DMLTestsData {
object Cities : Table() {
@@ -1026,6 +1025,78 @@ class DMLTests : DatabaseTestsBase() {
}
}
}
private fun assertQueryResultValid(query: Query): Unit {
val users = DMLTestsData.Users
val cities = DMLTestsData.Cities
query.forEach { row ->
val userName = row[users.name]
val cityName = row[cities.name]
when (userName) {
"Andrey" -> assertEquals("St. Petersburg", cityName)
"Sergey" -> assertEquals("Munich", cityName)
else -> error("Unexpected user $userName")
}
}
}
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
nameCheck and cityCheck
}
@Test fun testAdjustQuerySlice() {
withCitiesAndUsers { cities, users, _ ->
val queryAdjusted = (users innerJoin cities)
.slice(users.name)
.select(predicate)
fun Query.sliceIt(): FieldSet = this.set.source.slice(users.name, cities.name)
val oldSlice = queryAdjusted.set.fields
val expectedSlice = queryAdjusted.sliceIt().fields
queryAdjusted.adjustSlice { slice(users.name, cities.name) }
val actualSlice = queryAdjusted.set.fields
fun containsInAnyOrder(list: List<*>) = containsInAnyOrder(*list.toTypedArray())
assertThat(oldSlice, not(containsInAnyOrder(actualSlice)))
assertThat(actualSlice, containsInAnyOrder(expectedSlice))
assertQueryResultValid(queryAdjusted)
}
}
@Test fun testAdjustQueryColumnSet() {
withCitiesAndUsers { cities, users, _ ->
val queryAdjusted = users
.slice(users.name, cities.name)
.select(predicate)
val oldColumnSet = queryAdjusted.set.source
val expectedColumnSet = users innerJoin cities
queryAdjusted.adjustColumnSet { innerJoin(cities) }
val actualColumnSet = queryAdjusted.set.source
fun ColumnSet.repr(): String = this.describe(queryAdjusted.transaction)
assertNotEquals(oldColumnSet.repr(), actualColumnSet.repr())
assertEquals(expectedColumnSet.repr(), actualColumnSet.repr())
assertQueryResultValid(queryAdjusted)
}
}
@Test fun testAdjustQueryWhere() {
withCitiesAndUsers { cities, users, _ ->
val queryAdjusted = (users innerJoin cities)
.slice(users.name, cities.name)
.selectAll()
queryAdjusted.adjustWhere {
assertNull(this)
predicate
}
val actualWhere = queryAdjusted.where
fun Op<Boolean>.repr(): String = this.toSQL(QueryBuilder(false))
assertEquals(predicate.repr(), actualWhere!!.repr())
assertQueryResultValid(queryAdjusted)
}
}
}
private val today: DateTime = DateTime.now().withTimeAtStartOfDay()