mirror of
https://github.com/jlengrand/Exposed.git
synced 2026-03-10 08:11:20 +00:00
Adding sql datetime functions year, day, hour, minute, second (#707)
* Adding sql datetime functions * Adding sql datetime functions (missing space) * Removing jodatime dependency * Adding functions tests to jodatime and javatime modules * Including sqlite in datetime tests and enhacement of code * Value from DB type check
This commit is contained in:
committed by
Andrey.Tarashevskiy
parent
0756360687
commit
677b21783c
@@ -139,6 +139,7 @@ class IntegerColumnType : ColumnType() {
|
||||
override fun valueFromDB(value: Any): Any = when(value) {
|
||||
is Int -> value
|
||||
is Number -> value.toInt()
|
||||
is String -> value.toInt()
|
||||
else -> error("Unexpected value of type Int: $value of ${value::class.qualifiedName}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +175,42 @@ abstract class FunctionProvider {
|
||||
expr.toList().appendTo { +it }
|
||||
append(")")
|
||||
}
|
||||
|
||||
open fun <T> year(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("YEAR(")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
open fun <T> month(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("MONTH(")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
open fun <T> day(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("DAY(")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
open fun <T> hour(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("HOUR(")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
open fun <T> minute(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("MINUTE(")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
open fun <T> second(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("SECOND(")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -98,6 +98,42 @@ internal object OracleFunctionProvider : FunctionProvider() {
|
||||
else
|
||||
expr.toList().appendTo(separator = " || '$separator' || ") { +it }
|
||||
}
|
||||
|
||||
override fun <T> year(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(YEAR FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> month(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(MONTH FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> day(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(DAY FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> hour(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(HOUR FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> minute(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(MINUTE FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> second(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(SECOND FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
}
|
||||
|
||||
open class OracleDialect : VendorDialect(dialectName, OracleDataTypeProvider, OracleFunctionProvider) {
|
||||
|
||||
@@ -78,6 +78,42 @@ internal object PostgreSQLFunctionProvider : FunctionProvider() {
|
||||
append(" ~* ")
|
||||
append(pattern)
|
||||
}
|
||||
|
||||
override fun <T> year(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(YEAR FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> month(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(MONTH FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> day(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(DAY FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> hour(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(HOUR FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> minute(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(MINUTE FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun <T> second(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("Extract(SECOND FROM ")
|
||||
append(expr)
|
||||
append(")")
|
||||
}
|
||||
}
|
||||
|
||||
open class PostgreSQLDialect : VendorDialect(dialectName, PostgreSQLDataTypeProvider, PostgreSQLFunctionProvider) {
|
||||
|
||||
@@ -53,6 +53,42 @@ internal object SQLiteFunctionProvider : FunctionProvider() {
|
||||
else
|
||||
expr.toList().appendTo(this, separator = " || '$separator' || ") { +it }
|
||||
}
|
||||
|
||||
override fun <T> year(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("STRFTIME('%Y',")
|
||||
append(expr)
|
||||
append(" / 1000, 'unixepoch')")
|
||||
}
|
||||
|
||||
override fun <T> month(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("STRFTIME('%m',")
|
||||
append(expr)
|
||||
append(" / 1000, 'unixepoch')")
|
||||
}
|
||||
|
||||
override fun <T> day(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("STRFTIME('%d',")
|
||||
append(expr)
|
||||
append(" / 1000, 'unixepoch')")
|
||||
}
|
||||
|
||||
override fun <T> hour(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("STRFTIME('%H',")
|
||||
append(expr)
|
||||
append(" / 1000, 'unixepoch')")
|
||||
}
|
||||
|
||||
override fun <T> minute(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("STRFTIME('%M',")
|
||||
append(expr)
|
||||
append(" / 1000, 'unixepoch')")
|
||||
}
|
||||
|
||||
override fun <T> second(expr: Expression<T>, queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("STRFTIME('%S',")
|
||||
append(expr)
|
||||
append(" / 1000, 'unixepoch')")
|
||||
}
|
||||
}
|
||||
|
||||
open class SQLiteDialect : VendorDialect(dialectName, SQLiteDataTypeProvider, SQLiteFunctionProvider) {
|
||||
|
||||
@@ -20,24 +20,50 @@ class CurrentDateTime : Function<LocalDateTime>(JavaLocalDateTimeColumnType.INST
|
||||
}
|
||||
}
|
||||
|
||||
class Year<T:Temporal?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.year(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Month<T:Temporal?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
when (currentDialect) {
|
||||
is PostgreSQLDialect -> append("EXTRACT(MONTH FROM ", expr, ")")
|
||||
is OracleDialect -> append("EXTRACT(MONTH FROM ", expr, ")")
|
||||
is OracleDialect -> append("EXTRACT(MONTH FROM ", expr, ")")
|
||||
is SQLServerDialect -> append("MONTH(", expr, ")")
|
||||
is MariaDBDialect -> append("MONTH(", expr, ")")
|
||||
is SQLiteDialect -> append("STRFTIME('%m',", expr, ")")
|
||||
is H2Dialect -> append("MONTH(", expr, ")")
|
||||
else -> append("MONTH(", expr, ")")
|
||||
}
|
||||
currentDialect.functionProvider.month(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Day<T:Temporal?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.day(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Hour<T:Temporal?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.hour(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Minute<T:Temporal?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.minute(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Second<T:Temporal?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.second(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Temporal?> Expression<T>.date() = Date(this)
|
||||
|
||||
fun <T: Temporal?> Expression<T>.year() = Year(this)
|
||||
fun <T: Temporal?> Expression<T>.month() = Month(this)
|
||||
fun <T: Temporal?> Expression<T>.day() = Day(this)
|
||||
fun <T: Temporal?> Expression<T>.hour() = Hour(this)
|
||||
fun <T: Temporal?> Expression<T>.minute() = Minute(this)
|
||||
fun <T: Temporal?> Expression<T>.second() = Second(this)
|
||||
|
||||
|
||||
fun dateParam(value: LocalDate): Expression<LocalDate> = QueryParameter(value, JavaLocalDateColumnType.INSTANCE)
|
||||
|
||||
@@ -1,13 +1,47 @@
|
||||
package org.jetbrains.exposed
|
||||
|
||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.`java-time`.*
|
||||
import org.jetbrains.exposed.sql.tests.DatabaseTestsBase
|
||||
import org.jetbrains.exposed.sql.tests.currentDialectTest
|
||||
import org.jetbrains.exposed.sql.tests.shared.assertEquals
|
||||
import org.jetbrains.exposed.sql.vendors.MysqlDialect
|
||||
import org.junit.Test
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.temporal.Temporal
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
open class JavaTimeBaseTest : DatabaseTestsBase() {
|
||||
|
||||
@Test
|
||||
fun javaTimeFunctions() {
|
||||
withTables(CitiesTime) {
|
||||
val now = LocalDateTime.now()
|
||||
|
||||
val cityID = CitiesTime.insertAndGetId {
|
||||
it[name] = "Tunisia"
|
||||
it[local_time] = now
|
||||
}
|
||||
|
||||
val insertedYear = CitiesTime.slice(CitiesTime.local_time.year()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.year()]
|
||||
val insertedMonth = CitiesTime.slice(CitiesTime.local_time.month()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.month()]
|
||||
val insertedDay = CitiesTime.slice(CitiesTime.local_time.day()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.day()]
|
||||
val insertedHour = CitiesTime.slice(CitiesTime.local_time.hour()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.hour()]
|
||||
val insertedMinute = CitiesTime.slice(CitiesTime.local_time.minute()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.minute()]
|
||||
val insertedSecond = CitiesTime.slice(CitiesTime.local_time.second()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.second()]
|
||||
|
||||
assertEquals(now.year, insertedYear)
|
||||
assertEquals(now.month.value, insertedMonth)
|
||||
assertEquals(now.dayOfMonth, insertedDay)
|
||||
assertEquals(now.hour, insertedHour)
|
||||
assertEquals(now.minute, insertedMinute)
|
||||
assertEquals(now.second, insertedSecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T:Temporal> assertEqualDateTime(d1: T?, d2: T?) {
|
||||
when{
|
||||
@@ -28,4 +62,9 @@ fun equalDateTime(d1: Temporal?, d2: Temporal?) = try {
|
||||
false
|
||||
}
|
||||
|
||||
val today: LocalDate = LocalDate.now()
|
||||
val today: LocalDate = LocalDate.now()
|
||||
|
||||
object CitiesTime : IntIdTable("CitiesTime") {
|
||||
val name = varchar("name", 50) // Column<String>
|
||||
val local_time = datetime("local_time").nullable() // Column<datetime>
|
||||
}
|
||||
@@ -18,23 +18,50 @@ class CurrentDateTime : Function<DateTime>(DateColumnType(false)) {
|
||||
}
|
||||
}
|
||||
|
||||
class Year<T:DateTime?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.year(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Month<T:DateTime?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
when (currentDialect) {
|
||||
is PostgreSQLDialect -> append("EXTRACT(MONTH FROM ", expr, ")")
|
||||
is OracleDialect -> append("EXTRACT(MONTH FROM ", expr, ")")
|
||||
is SQLServerDialect -> append("MONTH(", expr, ")")
|
||||
is MariaDBDialect -> append("MONTH(", expr, ")")
|
||||
is SQLiteDialect -> append("STRFTIME('%m',", expr, ")")
|
||||
is H2Dialect -> append("MONTH(", expr, ")")
|
||||
else -> append("MONTH(", expr, ")")
|
||||
}
|
||||
currentDialect.functionProvider.month(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Day<T:DateTime?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.day(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Hour<T:DateTime?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.hour(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Minute<T:DateTime?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.minute(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
class Second<T:DateTime?>(val expr: Expression<T>): Function<Int>(IntegerColumnType()) {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
currentDialect.functionProvider.second(expr, queryBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: DateTime?> Expression<T>.date() = Date(this)
|
||||
|
||||
fun <T: DateTime?> Expression<T>.year() = Year(this)
|
||||
fun <T: DateTime?> Expression<T>.month() = Month(this)
|
||||
fun <T: DateTime?> Expression<T>.day() = Day(this)
|
||||
fun <T: DateTime?> Expression<T>.hour() = Hour(this)
|
||||
fun <T: DateTime?> Expression<T>.minute() = Minute(this)
|
||||
fun <T: DateTime?> Expression<T>.second() = Second(this)
|
||||
|
||||
|
||||
fun dateParam(value: DateTime): Expression<DateTime> = QueryParameter(value, DateColumnType(false))
|
||||
|
||||
@@ -1,16 +1,47 @@
|
||||
package org.jetbrains.exposed
|
||||
|
||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.jodatime.*
|
||||
import org.jetbrains.exposed.sql.tests.DatabaseTestsBase
|
||||
import org.jetbrains.exposed.sql.tests.currentDialectTest
|
||||
import org.jetbrains.exposed.sql.tests.shared.assertEquals
|
||||
import org.jetbrains.exposed.sql.vendors.MysqlDialect
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.DateTimeZone
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
open class JodaTimeBaseTest : DatabaseTestsBase() {
|
||||
init {
|
||||
DateTimeZone.setDefault(DateTimeZone.UTC)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jodaTimeFunctions() {
|
||||
withTables(CitiesTime) {
|
||||
val now = DateTime.now()
|
||||
|
||||
val cityID = CitiesTime.insertAndGetId {
|
||||
it[name] = "St. Petersburg"
|
||||
it[local_time] = now.toDateTime()
|
||||
}
|
||||
|
||||
val insertedYear = CitiesTime.slice(CitiesTime.local_time.year()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.year()]
|
||||
val insertedMonth = CitiesTime.slice(CitiesTime.local_time.month()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.month()]
|
||||
val insertedDay = CitiesTime.slice(CitiesTime.local_time.day()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.day()]
|
||||
val insertedHour = CitiesTime.slice(CitiesTime.local_time.hour()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.hour()]
|
||||
val insertedMinute = CitiesTime.slice(CitiesTime.local_time.minute()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.minute()]
|
||||
val insertedSecond = CitiesTime.slice(CitiesTime.local_time.second()).select { CitiesTime.id.eq(cityID) }.single()[CitiesTime.local_time.second()]
|
||||
|
||||
assertEquals(now.year, insertedYear)
|
||||
assertEquals(now.monthOfYear, insertedMonth)
|
||||
assertEquals(now.dayOfMonth, insertedDay)
|
||||
assertEquals(now.hourOfDay, insertedHour)
|
||||
assertEquals(now.minuteOfHour, insertedMinute)
|
||||
assertEquals(now.secondOfMinute, insertedSecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun assertEqualDateTime(d1: DateTime?, d2: DateTime?) {
|
||||
@@ -33,4 +64,9 @@ fun equalDateTime(d1: DateTime?, d2: DateTime?) = try {
|
||||
false
|
||||
}
|
||||
|
||||
val today: DateTime = DateTime.now().withTimeAtStartOfDay()
|
||||
val today: DateTime = DateTime.now().withTimeAtStartOfDay()
|
||||
|
||||
object CitiesTime : IntIdTable("CitiesTime") {
|
||||
val name = varchar("name", 50) // Column<String>
|
||||
val local_time = datetime("local_time").nullable() // Column<datetime>
|
||||
}
|
||||
Reference in New Issue
Block a user