Skip to content

Commit

Permalink
support type overrides with SQL templates, #32
Browse files Browse the repository at this point in the history
  • Loading branch information
Miha-x64 committed May 30, 2020
1 parent 80d3112 commit 4b1b4ca
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 79 deletions.
Expand Up @@ -14,7 +14,7 @@ import net.aquadc.persistence.type.Ilk
private val orElse: () -> R
) : Fetch<Blocking<CUR>, R> {
override fun fetch(
from: Blocking<CUR>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<CUR>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): R =
from.cell(query, argumentTypes, arguments, rt, orElse)
}
Expand All @@ -23,7 +23,7 @@ import net.aquadc.persistence.type.Ilk
private val rt: Ilk<R, *>
) : Fetch<Blocking<CUR>, List<R>> {
override fun fetch(
from: Blocking<CUR>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<CUR>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): List<R> {
val cur = from.select(query, argumentTypes, arguments, 1)
try {
Expand All @@ -48,7 +48,7 @@ import net.aquadc.persistence.type.Ilk
private val orElse: () -> StructSnapshot<SCH>
) : Fetch<Blocking<CUR>, StructSnapshot<SCH>> {
override fun fetch(
from: Blocking<CUR>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<CUR>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): StructSnapshot<SCH> {
val managedColNames = table.managedColNames
val managedColTypes = table.managedColTypes
Expand All @@ -69,7 +69,7 @@ import net.aquadc.persistence.type.Ilk
private val bindBy: BindBy
) : Fetch<Blocking<CUR>, List<StructSnapshot<SCH>>> {
override fun fetch(
from: Blocking<CUR>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<CUR>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): List<StructSnapshot<SCH>> {
val colNames = table.managedColNames
val colTypes = table.managedColTypes
Expand Down
Expand Up @@ -424,7 +424,7 @@ class JdbcSession(

override fun <R> rawQuery(
@Language("SQL") query: String,
argumentTypes: Array<out DataType.NotNull.Simple<*>>,
argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>,
fetch: Fetch<Blocking<ResultSet>, R>
): VarFunc<Any, R> =
BlockingQuery(lowLevel, query, argumentTypes, fetch)
Expand Down
12 changes: 6 additions & 6 deletions sql/src/main/kotlin/net/aquadc/persistence/sql/blocking/Lazy.kt
Expand Up @@ -23,7 +23,7 @@ import java.sql.SQLFeatureNotSupportedException
private val orElse: () -> R
) : Fetch<Blocking<CUR>, Lazy<R>> {
override fun fetch(
from: Blocking<CUR>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<CUR>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): Lazy<R> {
val rt = rt; val orElse = orElse // don't capture `this`
return lazy { from.cell(query, argumentTypes, arguments, rt, orElse) }
Expand All @@ -34,7 +34,7 @@ import java.sql.SQLFeatureNotSupportedException
private val rt: Ilk<R, *>
) : Fetch<Blocking<CUR>, CloseableIterator<R>> {
override fun fetch(
from: Blocking<CUR>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<CUR>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): CloseableIterator<R> {
val rt = rt // don't capture `this`
return object : CurIterator<CUR, NullSchema, R>(from, query, argumentTypes, arguments, null, BindBy.Name/*whatever*/, NullSchema) {
Expand All @@ -51,7 +51,7 @@ import java.sql.SQLFeatureNotSupportedException

private var fallback: Struct<SCH>? = null
override fun fetch(
from: Blocking<CUR>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<CUR>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): CloseableStruct<SCH> {
val lazy = CurIterator<CUR, SCH, CloseableStruct<SCH>>(from, query, argumentTypes, arguments, table, bindBy, table.schema)

Expand All @@ -69,7 +69,7 @@ import java.sql.SQLFeatureNotSupportedException
private val transient: Boolean
) : Fetch<Blocking<CUR>, CloseableIterator<Struct<SCH>>> {
override fun fetch(
from: Blocking<CUR>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<CUR>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): CloseableIterator<Struct<SCH>> {
val transient = transient // don't capture this
return object : CurIterator<CUR, SCH, Struct<SCH>>(
Expand All @@ -84,7 +84,7 @@ import java.sql.SQLFeatureNotSupportedException
@PublishedApi internal object InputStreamFromResultSet : Fetch<Blocking<ResultSet>, InputStream> {

override fun fetch(
from: Blocking<ResultSet>, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>
from: Blocking<ResultSet>, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>
): InputStream =
from.select(query, argumentTypes, arguments, 1).let { rs ->
check(rs.next()) { rs.close(); "ResultSet is empty." }
Expand All @@ -107,7 +107,7 @@ import java.sql.SQLFeatureNotSupportedException
private open class CurIterator<CUR, SCH : Schema<SCH>, R>(
protected val from: Blocking<CUR>,
private val query: String,
private val argumentTypes: Array<out DataType.NotNull.Simple<*>>,
private val argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>,
private val arguments: Array<out Any>,

private val table: Table<SCH, *>?,
Expand Down
Expand Up @@ -26,7 +26,7 @@ import kotlin.concurrent.getOrSet
internal class BlockingQuery<CUR, R>(
private val session: Blocking<CUR>,
private val query: String,
private val argumentTypes: Array<out DataType.NotNull.Simple<*>>,
private val argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>,
private val fetch: Fetch<Blocking<CUR>, R>
) : VarFuncImpl<Any, R>(), VarFunc<Any, R> {

Expand Down
Expand Up @@ -373,7 +373,7 @@ class SqliteSession(
}
}

override fun <R> rawQuery(@Language("SQL") query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, fetch: Fetch<Blocking<Cursor>, R>): VarFunc<Any, R> =
override fun <R> rawQuery(@Language("SQL") query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, fetch: Fetch<Blocking<Cursor>, R>): VarFunc<Any, R> =
BlockingQuery(lowLevel, query, argumentTypes, fetch)

}
Expand Down
9 changes: 7 additions & 2 deletions sql/src/main/kotlin/net/aquadc/persistence/sql/sql.kt
Expand Up @@ -13,6 +13,7 @@ import net.aquadc.persistence.struct.Struct
import net.aquadc.persistence.struct.forEach
import net.aquadc.persistence.struct.intersect
import net.aquadc.persistence.type.DataType
import net.aquadc.persistence.type.Ilk
import net.aquadc.properties.Property
import net.aquadc.properties.TransactionalProperty
import net.aquadc.properties.internal.ManagedProperty
Expand Down Expand Up @@ -54,8 +55,12 @@ interface Session<SRC> {
*/
fun beginTransaction(): Transaction

fun <R> rawQuery(@Language("SQL") query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, fetch: Fetch<SRC, R>): VarFunc<Any, R>
// ^^^^^^^^^^^^^^^^ add Database Navigator to IntelliJ for SQL highlighting in String literals
fun <R> rawQuery(
@Language("SQL") query: String,
// ^^^^^^^^^^^^^^^^ add Database Navigator to IntelliJ for SQL highlighting in String literals
argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>,
fetch: Fetch<SRC, R>
): VarFunc<Any, R>

}

Expand Down
89 changes: 45 additions & 44 deletions sql/src/main/kotlin/net/aquadc/persistence/sql/template.kt
Expand Up @@ -7,6 +7,7 @@ package net.aquadc.persistence.sql

import net.aquadc.persistence.VarFuncImpl
import net.aquadc.persistence.type.DataType
import net.aquadc.persistence.type.Ilk
import org.intellij.lang.annotations.Language

/**
Expand All @@ -19,7 +20,7 @@ interface VarFunc<T, R> {
}

interface Fetch<SRC, R> {
fun fetch(from: SRC, query: String, argumentTypes: Array<out DataType.NotNull.Simple<*>>, arguments: Array<out Any>): R
fun fetch(from: SRC, query: String, argumentTypes: Array<out Ilk<*, DataType.NotNull<*>>>, arguments: Array<out Any>): R
}

enum class BindBy {
Expand All @@ -36,84 +37,84 @@ inline fun <SRC, R> Session<SRC>.query(

inline fun <SRC, T : Any, R> Session<SRC>.query(
@Language("SQL") query: String,
type: DataType.NotNull.Simple<T>,
type: Ilk<T, DataType.NotNull<T>>,
fetch: Fetch<SRC, R>
): (T) -> R =
rawQuery(query, arrayOf(type), fetch) as VarFuncImpl<Any, R>

inline fun <SRC, T1 : Any, T2 : Any, R> Session<SRC>.query(
@Language("SQL") query: String,
type1: DataType.NotNull.Simple<T1>,
type2: DataType.NotNull.Simple<T2>,
type1: Ilk<T1, DataType.NotNull<T1>>,
type2: Ilk<T2, DataType.NotNull<T2>>,
fetch: Fetch<SRC, R>
): (T1, T2) -> R =
rawQuery(query, arrayOf(type1, type2), fetch) as VarFuncImpl<Any, R>
rawQuery(query, arrayOf<Ilk<*, DataType.NotNull<*>>>(type1, type2), fetch) as VarFuncImpl<Any, R>

inline fun <SRC, T1 : Any, T2 : Any, T3 : Any, R> Session<SRC>.query(
@Language("SQL") query: String,
type1: DataType.NotNull.Simple<T1>,
type2: DataType.NotNull.Simple<T2>,
type3: DataType.NotNull.Simple<T3>,
type1: Ilk<T1, DataType.NotNull<T1>>,
type2: Ilk<T2, DataType.NotNull<T2>>,
type3: Ilk<T3, DataType.NotNull<T3>>,
fetch: Fetch<SRC, R>
): (T1, T2, T3) -> R =
rawQuery(query, arrayOf(type1, type2, type3), fetch) as VarFuncImpl<Any, R>
rawQuery(query, arrayOf<Ilk<*, DataType.NotNull<*>>>(type1, type2, type3), fetch) as VarFuncImpl<Any, R>

inline fun <SRC, T1 : Any, T2 : Any, T3 : Any, T4 : Any, R> Session<SRC>.query(
@Language("SQL") query: String,
type1: DataType.NotNull.Simple<T1>,
type2: DataType.NotNull.Simple<T2>,
type3: DataType.NotNull.Simple<T3>,
type4: DataType.NotNull.Simple<T4>,
type1: Ilk<T1, DataType.NotNull<T1>>,
type2: Ilk<T2, DataType.NotNull<T2>>,
type3: Ilk<T3, DataType.NotNull<T3>>,
type4: Ilk<T4, DataType.NotNull<T4>>,
fetch: Fetch<SRC, R>
): (T1, T2, T3, T4) -> R =
rawQuery(query, arrayOf(type1, type2, type3, type4), fetch) as VarFuncImpl<Any, R>
rawQuery(query, arrayOf<Ilk<*, DataType.NotNull<*>>>(type1, type2, type3, type4), fetch) as VarFuncImpl<Any, R>

inline fun <SRC, T1 : Any, T2 : Any, T3 : Any, T4 : Any, T5 : Any, R> Session<SRC>.query(
@Language("SQL") query: String,
type1: DataType.NotNull.Simple<T1>,
type2: DataType.NotNull.Simple<T2>,
type3: DataType.NotNull.Simple<T3>,
type4: DataType.NotNull.Simple<T4>,
type5: DataType.NotNull.Simple<T5>,
type1: Ilk<T1, DataType.NotNull<T1>>,
type2: Ilk<T2, DataType.NotNull<T2>>,
type3: Ilk<T3, DataType.NotNull<T3>>,
type4: Ilk<T4, DataType.NotNull<T4>>,
type5: Ilk<T5, DataType.NotNull<T5>>,
fetch: Fetch<SRC, R>
): (T1, T2, T3, T4, T5) -> R =
rawQuery(query, arrayOf(type1, type2, type3, type4, type5), fetch) as VarFuncImpl<Any, R>
rawQuery(query, arrayOf<Ilk<*, DataType.NotNull<*>>>(type1, type2, type3, type4, type5), fetch) as VarFuncImpl<Any, R>

inline fun <SRC, T1 : Any, T2 : Any, T3 : Any, T4 : Any, T5 : Any, T6 : Any, R> Session<SRC>.query(
@Language("SQL") query: String,
type1: DataType.NotNull.Simple<T1>,
type2: DataType.NotNull.Simple<T2>,
type3: DataType.NotNull.Simple<T3>,
type4: DataType.NotNull.Simple<T4>,
type5: DataType.NotNull.Simple<T5>,
type6: DataType.NotNull.Simple<T6>,
type1: Ilk<T1, DataType.NotNull<T1>>,
type2: Ilk<T2, DataType.NotNull<T2>>,
type3: Ilk<T3, DataType.NotNull<T3>>,
type4: Ilk<T4, DataType.NotNull<T4>>,
type5: Ilk<T5, DataType.NotNull<T5>>,
type6: Ilk<T6, DataType.NotNull<T6>>,
fetch: Fetch<SRC, R>
): (T1, T2, T3, T4, T5) -> R =
rawQuery(query, arrayOf(type1, type2, type3, type4, type5, type6), fetch) as VarFuncImpl<Any, R>
rawQuery(query, arrayOf<Ilk<*, DataType.NotNull<*>>>(type1, type2, type3, type4, type5, type6), fetch) as VarFuncImpl<Any, R>

inline fun <SRC, T1 : Any, T2 : Any, T3 : Any, T4 : Any, T5 : Any, T6 : Any, T7 : Any, R> Session<SRC>.query(
@Language("SQL") query: String,
type1: DataType.NotNull.Simple<T1>,
type2: DataType.NotNull.Simple<T2>,
type3: DataType.NotNull.Simple<T3>,
type4: DataType.NotNull.Simple<T4>,
type5: DataType.NotNull.Simple<T5>,
type6: DataType.NotNull.Simple<T6>,
type7: DataType.NotNull.Simple<T7>,
type1: Ilk<T1, DataType.NotNull<T1>>,
type2: Ilk<T2, DataType.NotNull<T2>>,
type3: Ilk<T3, DataType.NotNull<T3>>,
type4: Ilk<T4, DataType.NotNull<T4>>,
type5: Ilk<T5, DataType.NotNull<T5>>,
type6: Ilk<T6, DataType.NotNull<T6>>,
type7: Ilk<T7, DataType.NotNull<T7>>,
fetch: Fetch<SRC, R>
): (T1, T2, T3, T4, T5, T7) -> R =
rawQuery(query, arrayOf(type1, type2, type3, type4, type5, type6, type7), fetch) as VarFuncImpl<Any, R>
rawQuery(query, arrayOf<Ilk<*, DataType.NotNull<*>>>(type1, type2, type3, type4, type5, type6, type7), fetch) as VarFuncImpl<Any, R>

inline fun <SRC, T1 : Any, T2 : Any, T3 : Any, T4 : Any, T5 : Any, T6 : Any, T7 : Any, T8 : Any, R> Session<SRC>.query(
@Language("SQL") query: String,
type1: DataType.NotNull.Simple<T1>,
type2: DataType.NotNull.Simple<T2>,
type3: DataType.NotNull.Simple<T3>,
type4: DataType.NotNull.Simple<T4>,
type5: DataType.NotNull.Simple<T5>,
type6: DataType.NotNull.Simple<T6>,
type7: DataType.NotNull.Simple<T7>,
type8: DataType.NotNull.Simple<T8>,
type1: Ilk<T1, DataType.NotNull<T1>>,
type2: Ilk<T2, DataType.NotNull<T2>>,
type3: Ilk<T3, DataType.NotNull<T3>>,
type4: Ilk<T4, DataType.NotNull<T4>>,
type5: Ilk<T5, DataType.NotNull<T5>>,
type6: Ilk<T6, DataType.NotNull<T6>>,
type7: Ilk<T7, DataType.NotNull<T7>>,
type8: Ilk<T8, DataType.NotNull<T8>>,
fetch: Fetch<SRC, R>
): (T1, T2, T3, T4, T5, T7, T8) -> R =
rawQuery(query, arrayOf(type1, type2, type3, type4, type5, type6, type7, type8), fetch) as VarFuncImpl<Any, R>
rawQuery(query, arrayOf<Ilk<*, DataType.NotNull<*>>>(type1, type2, type3, type4, type5, type6, type7, type8), fetch) as VarFuncImpl<Any, R>
3 changes: 3 additions & 0 deletions sql/src/main/kotlin/net/aquadc/persistence/sql/util.kt
Expand Up @@ -354,3 +354,6 @@ internal fun <CUR, SCH : Schema<SCH>> Blocking<CUR>.mapRow(

override val custom: CustomType<T>? get() = this
}

inline fun <T, DT : DataType<T>> nativeType(name: CharSequence, type: DT): Ilk<T, DT> =
NativeType(name, type)
53 changes: 33 additions & 20 deletions sql/src/test/kotlin/net/aquadc/persistence/sql/postgres.kt
Expand Up @@ -9,6 +9,8 @@ import net.aquadc.persistence.sql.ColMeta.Companion.embed
import net.aquadc.persistence.sql.ColMeta.Companion.nativeType
import net.aquadc.persistence.sql.ColMeta.Companion.type
import net.aquadc.persistence.sql.blocking.Blocking
import net.aquadc.persistence.sql.blocking.Eagerly
import net.aquadc.persistence.sql.blocking.JdbcSession
import net.aquadc.persistence.sql.dialect.postgres.PostgresDialect
import net.aquadc.persistence.struct.Schema
import net.aquadc.persistence.struct.Struct
Expand Down Expand Up @@ -76,7 +78,7 @@ class TemplatesPostgres : TemplatesTest() {
it[MoreNumbers] = listOf(intArrayOf(1, 2, 3), intArrayOf(4, 5, 6))
}

@Test fun `just a table`() {
@Test fun <CUR> `just a table`() {
val Yuzerz = tableOf(Yoozer, "yoozerz1", "_id", i64) { arrayOf(embed(SnakeCase, Extras)) }
val schema = PostgresDialect.createTable(Yuzerz, true)
assertEquals(
Expand All @@ -92,9 +94,15 @@ class TemplatesPostgres : TemplatesTest() {
schema
)
assertInserts(schema, Yuzerz)
assertEquals(
"Some name",
(session as Session<Blocking<CUR>>)
.query("SELECT \"name\" FROM \"${Yuzerz.name}\" WHERE \"numbers\" = ?", intCollection, Eagerly.cell<CUR, String>(string))
.invoke(intArrayOf(0, 1, 2))
)
}

@Test fun `custom table`() {
@Test fun <CUR> `custom table`() {
val Yuzerz = object : Table<Yoozer, Long>(Yoozer, "yoozerz2", "_id", i64) {
override fun Yoozer.meta(): Array<out ColMeta<Yoozer>> = arrayOf(
type(pkColumn, "serial NOT NULL"), // this.pkColumn would be inaccessible within lambda
Expand All @@ -113,10 +121,16 @@ class TemplatesPostgres : TemplatesTest() {
schema
)
assertInserts(schema, Yuzerz)
assertEquals(
"Some name",
(session as Session<Blocking<CUR>>)
.query("SELECT \"name\" FROM \"${Yuzerz.name}\" WHERE \"extras\" = ?", serialized(SomeSchema), Eagerly.cell<CUR, String>(string))
.invoke(sampleYoozer[Yoozer.Extras])
)
}

@Test fun `very custom table`() {
val stmt = db.connection.createStatement()
@Test fun <CUR> `very custom table`() {
val stmt = (session as JdbcSession).connection.createStatement()
stmt.execute("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";")
stmt.close()

Expand All @@ -142,7 +156,7 @@ class TemplatesPostgres : TemplatesTest() {
"int[][] NOT NULL", collection(intCollection)
) {
override fun invoke(p1: List<IntArray>): Any? =
db.connection.unwrap(PgConnection::class.java).createArrayOf("int", p1.toTypedArray())
(session as JdbcSession).connection.unwrap(PgConnection::class.java).createArrayOf("int", p1.toTypedArray())
override fun back(p: Any?): List<IntArray> =
((p as java.sql.Array).array as Array<*>).map { (it as Array<Int>).toIntArray() }
// never cast to Array<Array<Int>>: ^^^^^^^^^^^ empty array will be returned as Array<Int>
Expand All @@ -165,26 +179,25 @@ class TemplatesPostgres : TemplatesTest() {
schema
)
assertInserts(schema, Yoozerz)
assertEquals(
"Some name",
(session as Session<Blocking<CUR>>)
.query("SELECT \"name\" FROM \"${Yoozerz.name}\" WHERE \"id\" = ? AND \"extras\" = ?",
nativeType("uuid", uuid),
someJsonb,
Eagerly.cell<CUR, String>(string))
.invoke(sampleYoozer[Yoozer.Id], sampleYoozer[Yoozer.Extras])
)
}
private fun assertInserts(create: String, table: Table<Yoozer, *>) {
var e: Throwable? = null
db.withTransaction {
db.connection.createStatement().run {
session.withTransaction {
(session as JdbcSession).connection.createStatement().run {
execute(create)
close()
}
try {
val rec = insert(table, sampleYoozer)
assertNotSame(sampleYoozer, rec)
assertEquals(sampleYoozer, rec)
} catch (t: Throwable) {
e = t
}
db.connection.createStatement().run {
execute("DROP TABLE " + table.name)
close()
}
val rec = insert(table, sampleYoozer)
assertNotSame(sampleYoozer, rec)
assertEquals(sampleYoozer, rec)
}
e?.let { throw it }
}
}

0 comments on commit 4b1b4ca

Please sign in to comment.