diff --git a/examples/kotlin/README.md b/examples/kotlin/README.md index ed910aa966..717425cb51 100644 --- a/examples/kotlin/README.md +++ b/examples/kotlin/README.md @@ -5,6 +5,7 @@ This is a Kotlin gradle project configured to compile and test all examples. Cur To run tests: ```shell script +docker run --name dinosql-postgres -d -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=mysecretpassword -e POSTGRES_DB=postgres -p 5432:5432 postgres:11 ./gradlew clean test ``` diff --git a/examples/kotlin/build.gradle b/examples/kotlin/build.gradle index fd331077ae..c29f0389c8 100644 --- a/examples/kotlin/build.gradle +++ b/examples/kotlin/build.gradle @@ -12,8 +12,8 @@ repositories { dependencies { implementation 'org.postgresql:postgresql:42.2.9' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0' } test { diff --git a/examples/kotlin/gradle/wrapper/gradle-wrapper.properties b/examples/kotlin/gradle/wrapper/gradle-wrapper.properties index b5354905d6..fd68b0da3e 100644 --- a/examples/kotlin/gradle/wrapper/gradle-wrapper.properties +++ b/examples/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Sat Jan 25 10:45:34 EST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/examples/kotlin/src/main/kotlin/com/example/authors/Queries.kt b/examples/kotlin/src/main/kotlin/com/example/authors/Queries.kt index 4dde3eec1f..0f40b51622 100644 --- a/examples/kotlin/src/main/kotlin/com/example/authors/Queries.kt +++ b/examples/kotlin/src/main/kotlin/com/example/authors/Queries.kt @@ -5,18 +5,22 @@ package com.example.authors import java.sql.Connection import java.sql.SQLException +import sqlc.runtime.ExecuteQuery +import sqlc.runtime.ListQuery +import sqlc.runtime.RowQuery + interface Queries { @Throws(SQLException::class) - fun createAuthor(name: String, bio: String?): Author + fun createAuthor(name: String, bio: String?): RowQuery @Throws(SQLException::class) - fun deleteAuthor(id: Long) + fun deleteAuthor(id: Long): ExecuteQuery @Throws(SQLException::class) - fun getAuthor(id: Long): Author + fun getAuthor(id: Long): RowQuery @Throws(SQLException::class) - fun listAuthors(): List + fun listAuthors(): ListQuery } diff --git a/examples/kotlin/src/main/kotlin/com/example/authors/QueriesImpl.kt b/examples/kotlin/src/main/kotlin/com/example/authors/QueriesImpl.kt index 1cf7434630..97900fc754 100644 --- a/examples/kotlin/src/main/kotlin/com/example/authors/QueriesImpl.kt +++ b/examples/kotlin/src/main/kotlin/com/example/authors/QueriesImpl.kt @@ -5,6 +5,10 @@ package com.example.authors import java.sql.Connection import java.sql.SQLException +import sqlc.runtime.ExecuteQuery +import sqlc.runtime.ListQuery +import sqlc.runtime.RowQuery + const val createAuthor = """-- name: createAuthor :one INSERT INTO authors ( name, bio @@ -32,71 +36,91 @@ ORDER BY name class QueriesImpl(private val conn: Connection) : Queries { @Throws(SQLException::class) - override fun createAuthor(name: String, bio: String?): Author { - return conn.prepareStatement(createAuthor).use { stmt -> - stmt.setString(1, name) - stmt.setString(2, bio) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = Author( - results.getLong(1), - results.getString(2), - results.getString(3) - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + override fun createAuthor(name: String, bio: String?): RowQuery { + return object : RowQuery() { + override fun execute(): Author { + return conn.prepareStatement(createAuthor).use { stmt -> + this.statement = stmt + stmt.setString(1, name) + stmt.setString(2, bio) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = Author( + results.getLong(1), + results.getString(2), + results.getString(3) + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @Throws(SQLException::class) - override fun deleteAuthor(id: Long) { - conn.prepareStatement(deleteAuthor).use { stmt -> - stmt.setLong(1, id) - - stmt.execute() + override fun deleteAuthor(id: Long): ExecuteQuery { + return object : ExecuteQuery() { + override fun execute() { + conn.prepareStatement(deleteAuthor).use { stmt -> + this.statement = stmt + stmt.setLong(1, id) + + stmt.execute() + } + } } } @Throws(SQLException::class) - override fun getAuthor(id: Long): Author { - return conn.prepareStatement(getAuthor).use { stmt -> - stmt.setLong(1, id) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = Author( - results.getLong(1), - results.getString(2), - results.getString(3) - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + override fun getAuthor(id: Long): RowQuery { + return object : RowQuery() { + override fun execute(): Author { + return conn.prepareStatement(getAuthor).use { stmt -> + this.statement = stmt + stmt.setLong(1, id) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = Author( + results.getLong(1), + results.getString(2), + results.getString(3) + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @Throws(SQLException::class) - override fun listAuthors(): List { - return conn.prepareStatement(listAuthors).use { stmt -> - - val results = stmt.executeQuery() - val ret = mutableListOf() - while (results.next()) { - ret.add(Author( - results.getLong(1), - results.getString(2), - results.getString(3) - )) + override fun listAuthors(): ListQuery { + return object : ListQuery() { + override fun execute(): List { + return conn.prepareStatement(listAuthors).use { stmt -> + this.statement = stmt + + val results = stmt.executeQuery() + val ret = mutableListOf() + while (results.next()) { + ret.add(Author( + results.getLong(1), + results.getString(2), + results.getString(3) + )) + } + ret + } } - ret } } diff --git a/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/Queries.kt b/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/Queries.kt index addf28a721..19a9c71b73 100644 --- a/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/Queries.kt +++ b/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/Queries.kt @@ -7,15 +7,19 @@ import java.sql.SQLException import java.sql.Types import java.time.OffsetDateTime +import sqlc.runtime.ExecuteQuery +import sqlc.runtime.ListQuery +import sqlc.runtime.RowQuery + interface Queries { @Throws(SQLException::class) - fun booksByTags(dollar1: List): List + fun booksByTags(dollar1: List): ListQuery @Throws(SQLException::class) - fun booksByTitleYear(title: String, year: Int): List + fun booksByTitleYear(title: String, year: Int): ListQuery @Throws(SQLException::class) - fun createAuthor(name: String): Author + fun createAuthor(name: String): RowQuery @Throws(SQLException::class) fun createBook( @@ -25,29 +29,29 @@ interface Queries { title: String, year: Int, available: OffsetDateTime, - tags: List): Book + tags: List): RowQuery @Throws(SQLException::class) - fun deleteBook(bookId: Int) + fun deleteBook(bookId: Int): ExecuteQuery @Throws(SQLException::class) - fun getAuthor(authorId: Int): Author + fun getAuthor(authorId: Int): RowQuery @Throws(SQLException::class) - fun getBook(bookId: Int): Book + fun getBook(bookId: Int): RowQuery @Throws(SQLException::class) fun updateBook( title: String, tags: List, - bookId: Int) + bookId: Int): ExecuteQuery @Throws(SQLException::class) fun updateBookISBN( title: String, tags: List, isbn: String, - bookId: Int) + bookId: Int): ExecuteQuery } diff --git a/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/QueriesImpl.kt b/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/QueriesImpl.kt index e4696f2cbc..5f9f7d70c0 100644 --- a/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/QueriesImpl.kt +++ b/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/QueriesImpl.kt @@ -7,6 +7,10 @@ import java.sql.SQLException import java.sql.Types import java.time.OffsetDateTime +import sqlc.runtime.ExecuteQuery +import sqlc.runtime.ListQuery +import sqlc.runtime.RowQuery + const val booksByTags = """-- name: booksByTags :many SELECT book_id, @@ -88,66 +92,81 @@ WHERE book_id = ? class QueriesImpl(private val conn: Connection) : Queries { @Throws(SQLException::class) - override fun booksByTags(dollar1: List): List { - return conn.prepareStatement(booksByTags).use { stmt -> - stmt.setArray(1, conn.createArrayOf("pg_catalog.varchar", dollar1.toTypedArray())) - - val results = stmt.executeQuery() - val ret = mutableListOf() - while (results.next()) { - ret.add(BooksByTagsRow( - results.getInt(1), - results.getString(2), - results.getString(3), - results.getString(4), - (results.getArray(5).array as Array).toList() - )) + override fun booksByTags(dollar1: List): ListQuery { + return object : ListQuery() { + override fun execute(): List { + return conn.prepareStatement(booksByTags).use { stmt -> + this.statement = stmt + stmt.setArray(1, conn.createArrayOf("pg_catalog.varchar", dollar1.toTypedArray())) + + val results = stmt.executeQuery() + val ret = mutableListOf() + while (results.next()) { + ret.add(BooksByTagsRow( + results.getInt(1), + results.getString(2), + results.getString(3), + results.getString(4), + (results.getArray(5).array as Array).toList() + )) + } + ret + } } - ret } } @Throws(SQLException::class) - override fun booksByTitleYear(title: String, year: Int): List { - return conn.prepareStatement(booksByTitleYear).use { stmt -> - stmt.setString(1, title) - stmt.setInt(2, year) - - val results = stmt.executeQuery() - val ret = mutableListOf() - while (results.next()) { - ret.add(Book( - results.getInt(1), - results.getInt(2), - results.getString(3), - BookType.lookup(results.getString(4))!!, - results.getString(5), - results.getInt(6), - results.getObject(7, OffsetDateTime::class.java), - (results.getArray(8).array as Array).toList() - )) + override fun booksByTitleYear(title: String, year: Int): ListQuery { + return object : ListQuery() { + override fun execute(): List { + return conn.prepareStatement(booksByTitleYear).use { stmt -> + this.statement = stmt + stmt.setString(1, title) + stmt.setInt(2, year) + + val results = stmt.executeQuery() + val ret = mutableListOf() + while (results.next()) { + ret.add(Book( + results.getInt(1), + results.getInt(2), + results.getString(3), + BookType.lookup(results.getString(4))!!, + results.getString(5), + results.getInt(6), + results.getObject(7, OffsetDateTime::class.java), + (results.getArray(8).array as Array).toList() + )) + } + ret + } } - ret } } @Throws(SQLException::class) - override fun createAuthor(name: String): Author { - return conn.prepareStatement(createAuthor).use { stmt -> - stmt.setString(1, name) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") + override fun createAuthor(name: String): RowQuery { + return object : RowQuery() { + override fun execute(): Author { + return conn.prepareStatement(createAuthor).use { stmt -> + this.statement = stmt + stmt.setString(1, name) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = Author( + results.getInt(1), + results.getString(2) + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - val ret = Author( - results.getInt(1), - results.getString(2) - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") - } - ret } } @@ -159,89 +178,109 @@ class QueriesImpl(private val conn: Connection) : Queries { title: String, year: Int, available: OffsetDateTime, - tags: List): Book { - return conn.prepareStatement(createBook).use { stmt -> - stmt.setInt(1, authorId) - stmt.setString(2, isbn) - stmt.setObject(3, booktype.value, Types.OTHER) - stmt.setString(4, title) - stmt.setInt(5, year) - stmt.setObject(6, available) - stmt.setArray(7, conn.createArrayOf("pg_catalog.varchar", tags.toTypedArray())) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = Book( - results.getInt(1), - results.getInt(2), - results.getString(3), - BookType.lookup(results.getString(4))!!, - results.getString(5), - results.getInt(6), - results.getObject(7, OffsetDateTime::class.java), - (results.getArray(8).array as Array).toList() - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + tags: List): RowQuery { + return object : RowQuery() { + override fun execute(): Book { + return conn.prepareStatement(createBook).use { stmt -> + this.statement = stmt + stmt.setInt(1, authorId) + stmt.setString(2, isbn) + stmt.setObject(3, booktype.value, Types.OTHER) + stmt.setString(4, title) + stmt.setInt(5, year) + stmt.setObject(6, available) + stmt.setArray(7, conn.createArrayOf("pg_catalog.varchar", tags.toTypedArray())) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = Book( + results.getInt(1), + results.getInt(2), + results.getString(3), + BookType.lookup(results.getString(4))!!, + results.getString(5), + results.getInt(6), + results.getObject(7, OffsetDateTime::class.java), + (results.getArray(8).array as Array).toList() + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @Throws(SQLException::class) - override fun deleteBook(bookId: Int) { - conn.prepareStatement(deleteBook).use { stmt -> - stmt.setInt(1, bookId) - - stmt.execute() + override fun deleteBook(bookId: Int): ExecuteQuery { + return object : ExecuteQuery() { + override fun execute() { + conn.prepareStatement(deleteBook).use { stmt -> + this.statement = stmt + stmt.setInt(1, bookId) + + stmt.execute() + } + } } } @Throws(SQLException::class) - override fun getAuthor(authorId: Int): Author { - return conn.prepareStatement(getAuthor).use { stmt -> - stmt.setInt(1, authorId) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") + override fun getAuthor(authorId: Int): RowQuery { + return object : RowQuery() { + override fun execute(): Author { + return conn.prepareStatement(getAuthor).use { stmt -> + this.statement = stmt + stmt.setInt(1, authorId) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = Author( + results.getInt(1), + results.getString(2) + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - val ret = Author( - results.getInt(1), - results.getString(2) - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") - } - ret } } @Throws(SQLException::class) - override fun getBook(bookId: Int): Book { - return conn.prepareStatement(getBook).use { stmt -> - stmt.setInt(1, bookId) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = Book( - results.getInt(1), - results.getInt(2), - results.getString(3), - BookType.lookup(results.getString(4))!!, - results.getString(5), - results.getInt(6), - results.getObject(7, OffsetDateTime::class.java), - (results.getArray(8).array as Array).toList() - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + override fun getBook(bookId: Int): RowQuery { + return object : RowQuery() { + override fun execute(): Book { + return conn.prepareStatement(getBook).use { stmt -> + this.statement = stmt + stmt.setInt(1, bookId) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = Book( + results.getInt(1), + results.getInt(2), + results.getString(3), + BookType.lookup(results.getString(4))!!, + results.getString(5), + results.getInt(6), + results.getObject(7, OffsetDateTime::class.java), + (results.getArray(8).array as Array).toList() + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @@ -249,13 +288,18 @@ class QueriesImpl(private val conn: Connection) : Queries { override fun updateBook( title: String, tags: List, - bookId: Int) { - conn.prepareStatement(updateBook).use { stmt -> - stmt.setString(1, title) - stmt.setArray(2, conn.createArrayOf("pg_catalog.varchar", tags.toTypedArray())) - stmt.setInt(3, bookId) - - stmt.execute() + bookId: Int): ExecuteQuery { + return object : ExecuteQuery() { + override fun execute() { + conn.prepareStatement(updateBook).use { stmt -> + this.statement = stmt + stmt.setString(1, title) + stmt.setArray(2, conn.createArrayOf("pg_catalog.varchar", tags.toTypedArray())) + stmt.setInt(3, bookId) + + stmt.execute() + } + } } } @@ -264,14 +308,19 @@ class QueriesImpl(private val conn: Connection) : Queries { title: String, tags: List, isbn: String, - bookId: Int) { - conn.prepareStatement(updateBookISBN).use { stmt -> - stmt.setString(1, title) - stmt.setArray(2, conn.createArrayOf("pg_catalog.varchar", tags.toTypedArray())) - stmt.setString(3, isbn) - stmt.setInt(4, bookId) - - stmt.execute() + bookId: Int): ExecuteQuery { + return object : ExecuteQuery() { + override fun execute() { + conn.prepareStatement(updateBookISBN).use { stmt -> + this.statement = stmt + stmt.setString(1, title) + stmt.setArray(2, conn.createArrayOf("pg_catalog.varchar", tags.toTypedArray())) + stmt.setString(3, isbn) + stmt.setInt(4, bookId) + + stmt.execute() + } + } } } diff --git a/examples/kotlin/src/main/kotlin/com/example/jets/Queries.kt b/examples/kotlin/src/main/kotlin/com/example/jets/Queries.kt index 7437a6f2b0..939d471db9 100644 --- a/examples/kotlin/src/main/kotlin/com/example/jets/Queries.kt +++ b/examples/kotlin/src/main/kotlin/com/example/jets/Queries.kt @@ -5,15 +5,19 @@ package com.example.jets import java.sql.Connection import java.sql.SQLException +import sqlc.runtime.ExecuteQuery +import sqlc.runtime.ListQuery +import sqlc.runtime.RowQuery + interface Queries { @Throws(SQLException::class) - fun countPilots(): Long + fun countPilots(): RowQuery @Throws(SQLException::class) - fun deletePilot(id: Int) + fun deletePilot(id: Int): ExecuteQuery @Throws(SQLException::class) - fun listPilots(): List + fun listPilots(): ListQuery } diff --git a/examples/kotlin/src/main/kotlin/com/example/jets/QueriesImpl.kt b/examples/kotlin/src/main/kotlin/com/example/jets/QueriesImpl.kt index 3214a8acdd..1bd9d55132 100644 --- a/examples/kotlin/src/main/kotlin/com/example/jets/QueriesImpl.kt +++ b/examples/kotlin/src/main/kotlin/com/example/jets/QueriesImpl.kt @@ -5,6 +5,10 @@ package com.example.jets import java.sql.Connection import java.sql.SQLException +import sqlc.runtime.ExecuteQuery +import sqlc.runtime.ListQuery +import sqlc.runtime.RowQuery + const val countPilots = """-- name: countPilots :one SELECT COUNT(*) FROM pilots """ @@ -20,43 +24,58 @@ SELECT id, name FROM pilots LIMIT 5 class QueriesImpl(private val conn: Connection) : Queries { @Throws(SQLException::class) - override fun countPilots(): Long { - return conn.prepareStatement(countPilots).use { stmt -> - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = results.getLong(1) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + override fun countPilots(): RowQuery { + return object : RowQuery() { + override fun execute(): Long { + return conn.prepareStatement(countPilots).use { stmt -> + this.statement = stmt + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = results.getLong(1) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @Throws(SQLException::class) - override fun deletePilot(id: Int) { - conn.prepareStatement(deletePilot).use { stmt -> - stmt.setInt(1, id) + override fun deletePilot(id: Int): ExecuteQuery { + return object : ExecuteQuery() { + override fun execute() { + conn.prepareStatement(deletePilot).use { stmt -> + this.statement = stmt + stmt.setInt(1, id) - stmt.execute() + stmt.execute() + } + } } } @Throws(SQLException::class) - override fun listPilots(): List { - return conn.prepareStatement(listPilots).use { stmt -> - - val results = stmt.executeQuery() - val ret = mutableListOf() - while (results.next()) { - ret.add(Pilot( - results.getInt(1), - results.getString(2) - )) + override fun listPilots(): ListQuery { + return object : ListQuery() { + override fun execute(): List { + return conn.prepareStatement(listPilots).use { stmt -> + this.statement = stmt + + val results = stmt.executeQuery() + val ret = mutableListOf() + while (results.next()) { + ret.add(Pilot( + results.getInt(1), + results.getString(2) + )) + } + ret + } } - ret } } diff --git a/examples/kotlin/src/main/kotlin/com/example/ondeck/Queries.kt b/examples/kotlin/src/main/kotlin/com/example/ondeck/Queries.kt index 69debb00a7..57cbca9697 100644 --- a/examples/kotlin/src/main/kotlin/com/example/ondeck/Queries.kt +++ b/examples/kotlin/src/main/kotlin/com/example/ondeck/Queries.kt @@ -7,9 +7,13 @@ import java.sql.SQLException import java.sql.Types import java.time.LocalDateTime +import sqlc.runtime.ExecuteQuery +import sqlc.runtime.ListQuery +import sqlc.runtime.RowQuery + interface Queries { @Throws(SQLException::class) - fun createCity(name: String, slug: String): City + fun createCity(name: String, slug: String): RowQuery @Throws(SQLException::class) fun createVenue( @@ -19,31 +23,31 @@ interface Queries { spotifyPlaylist: String, status: Status, statuses: List, - tags: List): Int + tags: List): RowQuery @Throws(SQLException::class) - fun deleteVenue(slug: String) + fun deleteVenue(slug: String): ExecuteQuery @Throws(SQLException::class) - fun getCity(slug: String): City + fun getCity(slug: String): RowQuery @Throws(SQLException::class) - fun getVenue(slug: String, city: String): Venue + fun getVenue(slug: String, city: String): RowQuery @Throws(SQLException::class) - fun listCities(): List + fun listCities(): ListQuery @Throws(SQLException::class) - fun listVenues(city: String): List + fun listVenues(city: String): ListQuery @Throws(SQLException::class) - fun updateCityName(name: String, slug: String) + fun updateCityName(name: String, slug: String): ExecuteQuery @Throws(SQLException::class) - fun updateVenueName(name: String, slug: String): Int + fun updateVenueName(name: String, slug: String): RowQuery @Throws(SQLException::class) - fun venueCountByCity(): List + fun venueCountByCity(): ListQuery } diff --git a/examples/kotlin/src/main/kotlin/com/example/ondeck/QueriesImpl.kt b/examples/kotlin/src/main/kotlin/com/example/ondeck/QueriesImpl.kt index 774b67cb4d..c3e049ce2d 100644 --- a/examples/kotlin/src/main/kotlin/com/example/ondeck/QueriesImpl.kt +++ b/examples/kotlin/src/main/kotlin/com/example/ondeck/QueriesImpl.kt @@ -7,6 +7,10 @@ import java.sql.SQLException import java.sql.Types import java.time.LocalDateTime +import sqlc.runtime.ExecuteQuery +import sqlc.runtime.ListQuery +import sqlc.runtime.RowQuery + const val createCity = """-- name: createCity :one INSERT INTO city ( name, @@ -103,23 +107,28 @@ class QueriesImpl(private val conn: Connection) : Queries { // This is the third line @Throws(SQLException::class) - override fun createCity(name: String, slug: String): City { - return conn.prepareStatement(createCity).use { stmt -> - stmt.setString(1, name) - stmt.setString(2, slug) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = City( - results.getString(1), - results.getString(2) - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + override fun createCity(name: String, slug: String): RowQuery { + return object : RowQuery() { + override fun execute(): City { + return conn.prepareStatement(createCity).use { stmt -> + this.statement = stmt + stmt.setString(1, name) + stmt.setString(2, slug) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = City( + results.getString(1), + results.getString(2) + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @@ -131,169 +140,214 @@ class QueriesImpl(private val conn: Connection) : Queries { spotifyPlaylist: String, status: Status, statuses: List, - tags: List): Int { - return conn.prepareStatement(createVenue).use { stmt -> - stmt.setString(1, slug) - stmt.setString(2, name) - stmt.setString(3, city) - stmt.setString(4, spotifyPlaylist) - stmt.setObject(5, status.value, Types.OTHER) - stmt.setArray(6, conn.createArrayOf("status", statuses.map { v -> v.value }.toTypedArray())) - stmt.setArray(7, conn.createArrayOf("text", tags.toTypedArray())) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = results.getInt(1) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + tags: List): RowQuery { + return object : RowQuery() { + override fun execute(): Int { + return conn.prepareStatement(createVenue).use { stmt -> + this.statement = stmt + stmt.setString(1, slug) + stmt.setString(2, name) + stmt.setString(3, city) + stmt.setString(4, spotifyPlaylist) + stmt.setObject(5, status.value, Types.OTHER) + stmt.setArray(6, conn.createArrayOf("status", statuses.map { v -> v.value }.toTypedArray())) + stmt.setArray(7, conn.createArrayOf("text", tags.toTypedArray())) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = results.getInt(1) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @Throws(SQLException::class) - override fun deleteVenue(slug: String) { - conn.prepareStatement(deleteVenue).use { stmt -> - stmt.setString(1, slug) - stmt.setString(2, slug) - - stmt.execute() + override fun deleteVenue(slug: String): ExecuteQuery { + return object : ExecuteQuery() { + override fun execute() { + conn.prepareStatement(deleteVenue).use { stmt -> + this.statement = stmt + stmt.setString(1, slug) + stmt.setString(2, slug) + + stmt.execute() + } + } } } @Throws(SQLException::class) - override fun getCity(slug: String): City { - return conn.prepareStatement(getCity).use { stmt -> - stmt.setString(1, slug) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") + override fun getCity(slug: String): RowQuery { + return object : RowQuery() { + override fun execute(): City { + return conn.prepareStatement(getCity).use { stmt -> + this.statement = stmt + stmt.setString(1, slug) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = City( + results.getString(1), + results.getString(2) + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - val ret = City( - results.getString(1), - results.getString(2) - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") - } - ret } } @Throws(SQLException::class) - override fun getVenue(slug: String, city: String): Venue { - return conn.prepareStatement(getVenue).use { stmt -> - stmt.setString(1, slug) - stmt.setString(2, city) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = Venue( - results.getInt(1), - Status.lookup(results.getString(2))!!, - (results.getArray(3).array as Array).map { v -> Status.lookup(v)!! }.toList(), - results.getString(4), - results.getString(5), - results.getString(6), - results.getString(7), - results.getString(8), - (results.getArray(9).array as Array).toList(), - results.getObject(10, LocalDateTime::class.java) - ) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + override fun getVenue(slug: String, city: String): RowQuery { + return object : RowQuery() { + override fun execute(): Venue { + return conn.prepareStatement(getVenue).use { stmt -> + this.statement = stmt + stmt.setString(1, slug) + stmt.setString(2, city) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = Venue( + results.getInt(1), + Status.lookup(results.getString(2))!!, + (results.getArray(3).array as Array).map { v -> Status.lookup(v)!! }.toList(), + results.getString(4), + results.getString(5), + results.getString(6), + results.getString(7), + results.getString(8), + (results.getArray(9).array as Array).toList(), + results.getObject(10, LocalDateTime::class.java) + ) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @Throws(SQLException::class) - override fun listCities(): List { - return conn.prepareStatement(listCities).use { stmt -> - - val results = stmt.executeQuery() - val ret = mutableListOf() - while (results.next()) { - ret.add(City( - results.getString(1), - results.getString(2) - )) + override fun listCities(): ListQuery { + return object : ListQuery() { + override fun execute(): List { + return conn.prepareStatement(listCities).use { stmt -> + this.statement = stmt + + val results = stmt.executeQuery() + val ret = mutableListOf() + while (results.next()) { + ret.add(City( + results.getString(1), + results.getString(2) + )) + } + ret + } } - ret } } @Throws(SQLException::class) - override fun listVenues(city: String): List { - return conn.prepareStatement(listVenues).use { stmt -> - stmt.setString(1, city) - - val results = stmt.executeQuery() - val ret = mutableListOf() - while (results.next()) { - ret.add(Venue( - results.getInt(1), - Status.lookup(results.getString(2))!!, - (results.getArray(3).array as Array).map { v -> Status.lookup(v)!! }.toList(), - results.getString(4), - results.getString(5), - results.getString(6), - results.getString(7), - results.getString(8), - (results.getArray(9).array as Array).toList(), - results.getObject(10, LocalDateTime::class.java) - )) + override fun listVenues(city: String): ListQuery { + return object : ListQuery() { + override fun execute(): List { + return conn.prepareStatement(listVenues).use { stmt -> + this.statement = stmt + stmt.setString(1, city) + + val results = stmt.executeQuery() + val ret = mutableListOf() + while (results.next()) { + ret.add(Venue( + results.getInt(1), + Status.lookup(results.getString(2))!!, + (results.getArray(3).array as Array).map { v -> Status.lookup(v)!! }.toList(), + results.getString(4), + results.getString(5), + results.getString(6), + results.getString(7), + results.getString(8), + (results.getArray(9).array as Array).toList(), + results.getObject(10, LocalDateTime::class.java) + )) + } + ret + } } - ret } } @Throws(SQLException::class) - override fun updateCityName(name: String, slug: String) { - conn.prepareStatement(updateCityName).use { stmt -> - stmt.setString(1, name) - stmt.setString(2, slug) - - stmt.execute() + override fun updateCityName(name: String, slug: String): ExecuteQuery { + return object : ExecuteQuery() { + override fun execute() { + conn.prepareStatement(updateCityName).use { stmt -> + this.statement = stmt + stmt.setString(1, name) + stmt.setString(2, slug) + + stmt.execute() + } + } } } @Throws(SQLException::class) - override fun updateVenueName(name: String, slug: String): Int { - return conn.prepareStatement(updateVenueName).use { stmt -> - stmt.setString(1, name) - stmt.setString(2, slug) - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") - } - val ret = results.getInt(1) - if (results.next()) { - throw SQLException("expected one row in result set, but got many") + override fun updateVenueName(name: String, slug: String): RowQuery { + return object : RowQuery() { + override fun execute(): Int { + return conn.prepareStatement(updateVenueName).use { stmt -> + this.statement = stmt + stmt.setString(1, name) + stmt.setString(2, slug) + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = results.getInt(1) + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - ret } } @Throws(SQLException::class) - override fun venueCountByCity(): List { - return conn.prepareStatement(venueCountByCity).use { stmt -> - - val results = stmt.executeQuery() - val ret = mutableListOf() - while (results.next()) { - ret.add(VenueCountByCityRow( - results.getString(1), - results.getLong(2) - )) + override fun venueCountByCity(): ListQuery { + return object : ListQuery() { + override fun execute(): List { + return conn.prepareStatement(venueCountByCity).use { stmt -> + this.statement = stmt + + val results = stmt.executeQuery() + val ret = mutableListOf() + while (results.next()) { + ret.add(VenueCountByCityRow( + results.getString(1), + results.getLong(2) + )) + } + ret + } } - ret } } diff --git a/examples/kotlin/src/main/kotlin/sqlc/runtime/Query.kt b/examples/kotlin/src/main/kotlin/sqlc/runtime/Query.kt new file mode 100644 index 0000000000..67a4a18834 --- /dev/null +++ b/examples/kotlin/src/main/kotlin/sqlc/runtime/Query.kt @@ -0,0 +1,42 @@ +package sqlc.runtime + +import java.sql.Statement +import java.time.Duration + +abstract class Query { + @Volatile + var timeout: Duration? = null + + private var _statement: Statement? = null + protected var statement: Statement + @Synchronized get() { + return this._statement ?: throw Exception("Cannot get Statement before Query is executed") + } + @Synchronized set(value) { + val timeout = this.timeout + if (timeout != null) { + value.queryTimeout = timeout.seconds.toInt() + } + this._statement = value + } + + fun cancel() { + this.statement.cancel() + } +} + +abstract class RowQuery : Query() { + abstract fun execute(): T +} + +abstract class ListQuery : Query() { + abstract fun execute(): List +} + +abstract class ExecuteQuery : Query() { + abstract fun execute() +} + +abstract class ExecuteUpdateQuery : Query() { + abstract fun execute(): Int +} \ No newline at end of file diff --git a/examples/kotlin/src/test/kotlin/com/example/authors/QueriesImplTest.kt b/examples/kotlin/src/test/kotlin/com/example/authors/QueriesImplTest.kt index 93608a1dfc..e2add5e72c 100644 --- a/examples/kotlin/src/test/kotlin/com/example/authors/QueriesImplTest.kt +++ b/examples/kotlin/src/test/kotlin/com/example/authors/QueriesImplTest.kt @@ -17,7 +17,7 @@ class QueriesImplTest() { fun testCreateAuthor() { val db = QueriesImpl(dbtest.getConnection()) - val initialAuthors = db.listAuthors() + val initialAuthors = db.listAuthors().execute() assert(initialAuthors.isEmpty()) val name = "Brian Kernighan" @@ -25,14 +25,14 @@ class QueriesImplTest() { val insertedAuthor = db.createAuthor( name = name, bio = bio - ) + ).execute() val expectedAuthor = Author(insertedAuthor.id, name, bio) assertEquals(expectedAuthor, insertedAuthor) - val fetchedAuthor = db.getAuthor(insertedAuthor.id) + val fetchedAuthor = db.getAuthor(insertedAuthor.id).execute() assertEquals(expectedAuthor, fetchedAuthor) - val listedAuthors = db.listAuthors() + val listedAuthors = db.listAuthors().execute() assertEquals(1, listedAuthors.size) assertEquals(expectedAuthor, listedAuthors[0]) } @@ -41,19 +41,19 @@ class QueriesImplTest() { fun testNull() { val db = QueriesImpl(dbtest.getConnection()) - val initialAuthors = db.listAuthors() + val initialAuthors = db.listAuthors().execute() assert(initialAuthors.isEmpty()) val name = "Brian Kernighan" val bio = null - val insertedAuthor = db.createAuthor(name, bio) + val insertedAuthor = db.createAuthor(name, bio).execute() val expectedAuthor = Author(insertedAuthor.id, name, bio) assertEquals(expectedAuthor, insertedAuthor) - val fetchedAuthor = db.getAuthor(insertedAuthor.id) + val fetchedAuthor = db.getAuthor(insertedAuthor.id).execute() assertEquals(expectedAuthor, fetchedAuthor) - val listedAuthors = db.listAuthors() + val listedAuthors = db.listAuthors().execute() assertEquals(1, listedAuthors.size) assertEquals(expectedAuthor, listedAuthors[0]) } diff --git a/examples/kotlin/src/test/kotlin/com/example/booktest/postgresql/QueriesImplTest.kt b/examples/kotlin/src/test/kotlin/com/example/booktest/postgresql/QueriesImplTest.kt index d85747431e..4d67b1f70e 100644 --- a/examples/kotlin/src/test/kotlin/com/example/booktest/postgresql/QueriesImplTest.kt +++ b/examples/kotlin/src/test/kotlin/com/example/booktest/postgresql/QueriesImplTest.kt @@ -15,7 +15,7 @@ class QueriesImplTest { fun testQueries() { val conn = dbtest.getConnection() val db = QueriesImpl(conn) - val author = db.createAuthor("Unknown Master") + val author = db.createAuthor("Unknown Master").execute() // Start a transaction conn.autoCommit = false @@ -27,7 +27,7 @@ class QueriesImplTest { year = 2016, available = OffsetDateTime.now(), tags = listOf() - ) + ).execute() val b1 = db.createBook( authorId = author.authorId, @@ -37,13 +37,13 @@ class QueriesImplTest { year = 2016, available = OffsetDateTime.now(), tags = listOf("cool", "unique") - ) + ).execute() db.updateBook( bookId = b1.bookId, title = "changed second title", tags = listOf("cool", "disastor") - ) + ).execute() val b3 = db.createBook( authorId = author.authorId, @@ -53,7 +53,7 @@ class QueriesImplTest { year = 2001, available = OffsetDateTime.now(), tags = listOf("cool") - ) + ).execute() db.createBook( authorId = author.authorId, @@ -63,7 +63,7 @@ class QueriesImplTest { year = 2011, available = OffsetDateTime.now(), tags = listOf("other") - ) + ).execute() // Commit transaction conn.commit() @@ -76,20 +76,20 @@ class QueriesImplTest { isbn = "NEW ISBN", title = "never ever gonna finish, a quatrain", tags = listOf("someother") - ) + ).execute() - val books0 = db.booksByTitleYear("my book title", 2016) + val books0 = db.booksByTitleYear("my book title", 2016).execute() val formatter = DateTimeFormatter.ISO_DATE_TIME for (book in books0) { println("Book ${book.bookId} (${book.booktype}): ${book.title} available: ${book.available.format(formatter)}") - val author = db.getAuthor(book.authorId) - println("Book ${book.bookId} author: ${author.name}") + val author2 = db.getAuthor(book.authorId).execute() + println("Book ${book.bookId} author: ${author2.name}") } // find a book with either "cool" or "other" tag println("---------\\nTag search results:\\n") - val res = db.booksByTags(listOf("cool", "other", "someother")) + val res = db.booksByTags(listOf("cool", "other", "someother")).execute() for (ab in res) { println("Book ${ab.bookId}: '${ab.title}', Author: '${ab.name}', ISBN: '${ab.isbn}' Tags: '${ab.tags.toList()}'") } diff --git a/examples/kotlin/src/test/kotlin/com/example/ondeck/QueriesImplTest.kt b/examples/kotlin/src/test/kotlin/com/example/ondeck/QueriesImplTest.kt index 2ab1ba3ac5..98d378f93a 100644 --- a/examples/kotlin/src/test/kotlin/com/example/ondeck/QueriesImplTest.kt +++ b/examples/kotlin/src/test/kotlin/com/example/ondeck/QueriesImplTest.kt @@ -16,7 +16,7 @@ class QueriesImplTest { val city = q.createCity( slug = "san-francisco", name = "San Francisco" - ) + ).execute() val venueId = q.createVenue( slug = "the-fillmore", name = "The Fillmore", @@ -25,22 +25,22 @@ class QueriesImplTest { status = Status.OPEN, statuses = listOf(Status.OPEN, Status.CLOSED), tags = listOf("rock", "punk") - ) + ).execute() val venue = q.getVenue( slug = "the-fillmore", city = city.slug - ) + ).execute() assertEquals(venueId, venue.id) - assertEquals(city, q.getCity(city.slug)) - assertEquals(listOf(VenueCountByCityRow(city.slug, 1)), q.venueCountByCity()) - assertEquals(listOf(city), q.listCities()) - assertEquals(listOf(venue), q.listVenues(city.slug)) + assertEquals(city, q.getCity(city.slug).execute()) + assertEquals(listOf(VenueCountByCityRow(city.slug, 1)), q.venueCountByCity().execute()) + assertEquals(listOf(city), q.listCities().execute()) + assertEquals(listOf(venue), q.listVenues(city.slug).execute()) - q.updateCityName(slug = city.slug, name = "SF") - val id = q.updateVenueName(slug = venue.slug, name = "Fillmore") + q.updateCityName(slug = city.slug, name = "SF").execute() + val id = q.updateVenueName(slug = venue.slug, name = "Fillmore").execute() assertEquals(venue.id, id) - q.deleteVenue(venue.slug) + q.deleteVenue(venue.slug).execute() } } \ No newline at end of file diff --git a/go.sum b/go.sum index b1a5e67a9c..eded4cc5ed 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,7 @@ cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTj cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -18,6 +19,7 @@ github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHS github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -109,6 +111,7 @@ github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTD github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-playground/overalls v0.0.0-20180201144345-22ec1a223b7c/go.mod h1:UqxAgEOt89sCiXlrc/ycnx00LVvUO/eS8tMUkWX4R7w= github.com/go-sql-driver/mysql v0.0.0-20170715192408-3955978caca4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -251,6 +254,7 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/klauspost/crc32 v1.2.0/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.2.0/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -491,6 +495,7 @@ go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -519,6 +524,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -625,6 +631,7 @@ golang.org/x/tools v0.0.0-20191107010934-f79515f33823/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191217033636-bbbf87ae2631/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191219041853-979b82bfef62/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200206050830-dd0d5d485177 h1:E2vxBajJgSA3TcJhDGTh/kP3VnsvXKl9jSijv+h7svQ= golang.org/x/tools v0.0.0-20200206050830-dd0d5d485177/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -691,6 +698,7 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= diff --git a/internal/dinosql/kotlin/gen.go b/internal/dinosql/kotlin/gen.go index 1723a4eb61..e9b53528b9 100644 --- a/internal/dinosql/kotlin/gen.go +++ b/internal/dinosql/kotlin/gen.go @@ -121,7 +121,7 @@ func (v KtParams) Bindings() string { for i, f := range v.Struct.JDBCParamBindings { out = append(out, jdbcSet(f.Type, i+1, f.Name)) } - return indent(strings.Join(out, "\n"), 6, 0) + return indent(strings.Join(out, "\n"), 10, 0) } func jdbcGet(t ktType, idx int) string { @@ -149,7 +149,7 @@ func (v KtQueryValue) ResultSet() string { out = append(out, jdbcGet(f.Type, i+1)) } ret := indent(strings.Join(out, ",\n"), 4, -1) - ret = indent(v.Struct.Name+"(\n"+ret+"\n)", 8, 0) + ret = indent(v.Struct.Name+"(\n"+ret+"\n)", 12, 0) return ret } @@ -218,9 +218,9 @@ func KtImports(r KtGenerateable, settings config.CombinedSettings) func(string) } func InterfaceKtImports(r KtGenerateable, settings config.CombinedSettings) [][]string { - gq := r.KtQueries(settings) + kq := r.KtQueries(settings) uses := func(name string) bool { - for _, q := range gq { + for _, q := range kq { if !q.Ret.isEmpty() { if strings.HasPrefix(q.Ret.Type(), name) { return true @@ -237,30 +237,14 @@ func InterfaceKtImports(r KtGenerateable, settings config.CombinedSettings) [][] return false } - std := map[string]struct{}{ - "java.sql.Connection": {}, - "java.sql.SQLException": {}, - } - if uses("LocalDate") { - std["java.time.LocalDate"] = struct{}{} - } - if uses("LocalTime") { - std["java.time.LocalTime"] = struct{}{} - } - if uses("LocalDateTime") { - std["java.time.LocalDateTime"] = struct{}{} - } - if uses("OffsetDateTime") { - std["java.time.OffsetDateTime"] = struct{}{} - } - + std := stdImports(uses) stds := make([]string, 0, len(std)) for s, _ := range std { stds = append(stds, s) } sort.Strings(stds) - return [][]string{stds} + return [][]string{stds, runtimeImports(kq)} } func ModelKtImports(r KtGenerateable, settings config.CombinedSettings) [][]string { @@ -287,21 +271,53 @@ func ModelKtImports(r KtGenerateable, settings config.CombinedSettings) [][]stri return [][]string{stds} } -func QueryKtImports(r KtGenerateable, settings config.CombinedSettings, filename string) [][]string { - // for _, strct := range r.KtDataClasses() { - // for _, f := range strct.Fields { - // if strings.HasPrefix(f.Type, "[]") { - // return true - // } - // } - // } - var gq []KtQuery - for _, query := range r.KtQueries(settings) { - gq = append(gq, query) +func stdImports(uses func(name string) bool) map[string]struct{} { + std := map[string]struct{}{ + "java.sql.SQLException": {}, + } + if uses("LocalDate") { + std["java.time.LocalDate"] = struct{}{} + } + if uses("LocalTime") { + std["java.time.LocalTime"] = struct{}{} + } + if uses("LocalDateTime") { + std["java.time.LocalDateTime"] = struct{}{} + } + if uses("OffsetDateTime") { + std["java.time.OffsetDateTime"] = struct{}{} } + return std +} + +func runtimeImports(kq []KtQuery) []string { + rt := map[string]struct{}{} + for _, q := range kq { + switch q.Cmd { + case ":one": + rt["sqlc.runtime.RowQuery"] = struct{}{} + case ":many": + rt["sqlc.runtime.ListQuery"] = struct{}{} + case ":exec": + rt["sqlc.runtime.ExecuteQuery"] = struct{}{} + case ":execUpdate": + rt["sqlc.runtime.ExecuteUpdateQuery"] = struct{}{} + default: + panic(fmt.Sprintf("invalid command %q", q.Cmd)) + } + } + rts := make([]string, 0, len(rt)) + for s, _ := range rt { + rts = append(rts, s) + } + sort.Strings(rts) + return rts +} +func QueryKtImports(r KtGenerateable, settings config.CombinedSettings, filename string) [][]string { + kq := r.KtQueries(settings) uses := func(name string) bool { - for _, q := range gq { + for _, q := range kq { if !q.Ret.isEmpty() { if q.Ret.Struct != nil { for _, f := range q.Ret.Struct.Fields { @@ -326,7 +342,7 @@ func QueryKtImports(r KtGenerateable, settings config.CombinedSettings, filename } hasEnum := func() bool { - for _, q := range gq { + for _, q := range kq { if !q.Arg.isEmpty() { for _, f := range q.Arg.Struct.Fields { if f.Type.IsEnum { @@ -338,41 +354,19 @@ func QueryKtImports(r KtGenerateable, settings config.CombinedSettings, filename return false } - std := map[string]struct{}{ - "java.sql.Connection": {}, - "java.sql.SQLException": {}, - } - if uses("LocalDate") { - std["java.time.LocalDate"] = struct{}{} - } - if uses("LocalTime") { - std["java.time.LocalTime"] = struct{}{} - } - if uses("LocalDateTime") { - std["java.time.LocalDateTime"] = struct{}{} - } - if uses("OffsetDateTime") { - std["java.time.OffsetDateTime"] = struct{}{} - } + std := stdImports(uses) + std["java.sql.Connection"] = struct{}{} if hasEnum() { std["java.sql.Types"] = struct{}{} } - pkg := make(map[string]struct{}) - - pkgs := make([]string, 0, len(pkg)) - for p, _ := range pkg { - pkgs = append(pkgs, p) - } - stds := make([]string, 0, len(std)) for s, _ := range std { stds = append(stds, s) } sort.Strings(stds) - sort.Strings(pkgs) - return [][]string{stds, pkgs} + return [][]string{stds, runtimeImports(kq)} } func ktEnumValueName(value string) string { @@ -786,16 +780,16 @@ interface Queries { {{- range .KtQueries}} @Throws(SQLException::class) {{- if eq .Cmd ":one"}} - fun {{.MethodName}}({{.Arg.Args}}): {{.Ret.Type}} + fun {{.MethodName}}({{.Arg.Args}}): RowQuery<{{.Ret.Type}}> {{- end}} {{- if eq .Cmd ":many"}} - fun {{.MethodName}}({{.Arg.Args}}): List<{{.Ret.Type}}> + fun {{.MethodName}}({{.Arg.Args}}): ListQuery<{{.Ret.Type}}> {{- end}} {{- if eq .Cmd ":exec"}} - fun {{.MethodName}}({{.Arg.Args}}) + fun {{.MethodName}}({{.Arg.Args}}): ExecuteQuery {{- end}} {{- if eq .Cmd ":execrows"}} - fun {{.MethodName}}({{.Arg.Args}}): Int + fun {{.MethodName}}({{.Arg.Args}}): ExecuteUpdateQuery {{- end}} {{end}} } @@ -867,19 +861,24 @@ class QueriesImpl(private val conn: Connection) : Queries { {{range .Comments}}//{{.}} {{end}} @Throws(SQLException::class) - override fun {{.MethodName}}({{.Arg.Args}}): {{.Ret.Type}} { - return conn.prepareStatement({{.ConstantName}}).use { stmt -> - {{.Arg.Bindings}} - - val results = stmt.executeQuery() - if (!results.next()) { - throw SQLException("no rows in result set") + override fun {{.MethodName}}({{.Arg.Args}}): RowQuery<{{.Ret.Type}}> { + return object : RowQuery<{{.Ret.Type}}>() { + override fun execute(): {{.Ret.Type}} { + return conn.prepareStatement({{.ConstantName}}).use { stmt -> + this.statement = stmt + {{.Arg.Bindings}} + + val results = stmt.executeQuery() + if (!results.next()) { + throw SQLException("no rows in result set") + } + val ret = {{.Ret.ResultSet}} + if (results.next()) { + throw SQLException("expected one row in result set, but got many") + } + ret + } } - val ret = {{.Ret.ResultSet}} - if (results.next()) { - throw SQLException("expected one row in result set, but got many") - } - ret } } {{end}} @@ -888,16 +887,21 @@ class QueriesImpl(private val conn: Connection) : Queries { {{range .Comments}}//{{.}} {{end}} @Throws(SQLException::class) - override fun {{.MethodName}}({{.Arg.Args}}): List<{{.Ret.Type}}> { - return conn.prepareStatement({{.ConstantName}}).use { stmt -> - {{.Arg.Bindings}} - - val results = stmt.executeQuery() - val ret = mutableListOf<{{.Ret.Type}}>() - while (results.next()) { - ret.add({{.Ret.ResultSet}}) + override fun {{.MethodName}}({{.Arg.Args}}): ListQuery<{{.Ret.Type}}> { + return object : ListQuery<{{.Ret.Type}}>() { + override fun execute(): List<{{.Ret.Type}}> { + return conn.prepareStatement({{.ConstantName}}).use { stmt -> + this.statement = stmt + {{.Arg.Bindings}} + + val results = stmt.executeQuery() + val ret = mutableListOf<{{.Ret.Type}}>() + while (results.next()) { + ret.add({{.Ret.ResultSet}}) + } + ret + } } - ret } } {{end}} @@ -907,11 +911,16 @@ class QueriesImpl(private val conn: Connection) : Queries { {{end}} @Throws(SQLException::class) {{ if $.EmitInterface }}override {{ end -}} - override fun {{.MethodName}}({{.Arg.Args}}) { - conn.prepareStatement({{.ConstantName}}).use { stmt -> - {{ .Arg.Bindings }} - - stmt.execute() + override fun {{.MethodName}}({{.Arg.Args}}): ExecuteQuery { + return object : ExecuteQuery() { + override fun execute() { + conn.prepareStatement({{.ConstantName}}).use { stmt -> + this.statement = stmt + {{ .Arg.Bindings }} + + stmt.execute() + } + } } } {{end}} @@ -921,12 +930,17 @@ class QueriesImpl(private val conn: Connection) : Queries { {{end}} @Throws(SQLException::class) {{ if $.EmitInterface }}override {{ end -}} - override fun {{.MethodName}}({{.Arg.Args}}): Int { - return conn.prepareStatement({{.ConstantName}}).use { stmt -> - {{ .Arg.Bindings }} - - stmt.execute() - stmt.updateCount + override fun {{.MethodName}}({{.Arg.Args}}): ExecuteUpdateQuery { + return object : ExecUpdateQuery() { + override fun execute(): Int { + return conn.prepareStatement({{.ConstantName}}).use { stmt -> + this.statement = stmt + {{ .Arg.Bindings }} + + stmt.execute() + stmt.updateCount + } + } } } {{end}} diff --git a/internal/endtoend/endtoend_test.go b/internal/endtoend/endtoend_test.go index a881867368..8c645a2831 100644 --- a/internal/endtoend/endtoend_test.go +++ b/internal/endtoend/endtoend_test.go @@ -89,6 +89,10 @@ func cmpDirectory(t *testing.T, dir string, actual map[string]string) { if strings.HasSuffix(path, "_test.go") || strings.Contains(path, "src/test/") { return nil } + // TODO(mightyguava): Remove this after sqlc-kotlin-runtime is published to Maven. + if strings.HasSuffix(path, "Query.kt") { + return nil + } blob, err := ioutil.ReadFile(path) if err != nil { return err