diff --git a/libraries/kotlin-jdbc/src/main/kotlin/kotlin/jdbc/Connections.kt b/libraries/kotlin-jdbc/src/main/kotlin/kotlin/jdbc/Connections.kt index f950379106e..516e127ac17 100644 --- a/libraries/kotlin-jdbc/src/main/kotlin/kotlin/jdbc/Connections.kt +++ b/libraries/kotlin-jdbc/src/main/kotlin/kotlin/jdbc/Connections.kt @@ -3,9 +3,47 @@ package kotlin.jdbc import java.sql.* import kotlin.template.StringTemplate import java.math.BigDecimal +import java.util.Map +import java.util.Properties + +private fun Map.toProperties() : Properties { + val p = Properties() + + this.keySet().forEach { + p.put(it, this[it]) + } + + return p +} /** - * Helper method to process a statement on this collection + * create connection for the specified jdbc url with no credentials + */ +fun getConnection(url : String) : Connection = DriverManager.getConnection(url).sure() + +/** + * create connection for the specified jdbc url and properties + */ +fun getConnection(url : String, info : Map) : Connection = DriverManager.getConnection(url, info.toProperties())!! + +/** + * create connection for the specified jdbc url and credentials + */ +fun getConnection(url : String, user : String, password : String) : Connection = DriverManager.getConnection(url, user, password)!! + +/** + * Executes specified block with connection and close connection after this + */ +fun Connection.use(block : (Connection) -> Any?) : Unit { + try { + block(this) + } finally { + this.close() + } +} + +/** + * Helper method to process a statement on this connection */ fun Connection.statement(block: (Statement) -> T): T { val statement = createStatement() diff --git a/libraries/kotlin-jdbc/src/main/kotlin/kotlin/jdbc/ResultSets.kt b/libraries/kotlin-jdbc/src/main/kotlin/kotlin/jdbc/ResultSets.kt index 568fb80ace7..3f0a620f2cf 100644 --- a/libraries/kotlin-jdbc/src/main/kotlin/kotlin/jdbc/ResultSets.kt +++ b/libraries/kotlin-jdbc/src/main/kotlin/kotlin/jdbc/ResultSets.kt @@ -4,6 +4,18 @@ import java.sql.* import java.util.ArrayList import java.util.Collection import java.util.List +import java.util.Map + +/** + * Executes the specfied block with result set and then closes it + */ +public fun ResultSet.use(block : (ResultSet) -> R) : R { + try { + return block(this) + } finally { + this.close() + } +} /** * Creates an iterator through a [[ResultSet]] @@ -18,6 +30,56 @@ fun ResultSet.iterator() : Iterator { } } +/** + * Returns iterable that calls to the specified mapper function for each row + */ +fun ResultSet.getMapped(fn : (ResultSet) -> T) : jet.Iterable { + val rs = this + + val iterator = object : Iterator{ + public override val hasNext : Boolean + get() = rs.next() + + public override fun next() : T = fn(rs) + } + + return object : jet.Iterable { + public override fun iterator(): Iterator = iterator + } +} + +/** + * Returns array with column names + */ +fun ResultSet.getColumnNames() : jet.Array { + val meta = getMetaData().sure() + return jet.Array(meta.getColumnCount(), {meta.getColumnName(it + 1) ?: it.toString()}) +} + +/** + * Return array filled with values from current row in the cursor. Values will have the same order as column's order + * @columnNames you can specify column names to extract otherwise all columns will be extracted + */ +fun ResultSet.getValues(columnNames : jet.Array = getColumnNames()) : jet.Array { + return jet.Array(columnNames.size, { + this[columnNames[it]] + }) +} + +/** + * Return map filled with values from current row in the cursor. Uses column names as keys for result map. + * @param columnNames you can specify column names to extract otherwise all columns will be extracted + */ +fun ResultSet.getValuesAsMap(columnNames : jet.Array = getColumnNames()) : java.util.Map { + val result = java.util.HashMap(columnNames.size) + + columnNames.forEach { + result[it] = this[it] + } + + return result +} + /** * Returns the value at the given column index (starting at 1) */ @@ -47,3 +109,26 @@ fun ResultSet.mapTo(result: Collection, fn: (ResultSet) -> T) : Collectio } return result } + +private fun ResultSet.ensureHasRow() : ResultSet { + if (!next()) { + throw IllegalStateException("There are no rows left in cursor") + } + return this +} + +/** + * Returns int value from the cursor at first column. May be useful to get result of count(*) + */ +fun ResultSet.singleInt() : Int = ensureHasRow().getInt(1) + +/** + * Returns long value from the cursor at first column. + */ +fun ResultSet.singleLong() : Long = ensureHasRow().getLong(1) + +/** + * Returns double value from the cursor at first column. + */ +fun ResultSet.singleDouble() : Double = ensureHasRow().getDouble(1) + diff --git a/libraries/kotlin-jdbc/src/test/kotlin/test/kotlin/jdbc/JdbcTest.kt b/libraries/kotlin-jdbc/src/test/kotlin/test/kotlin/jdbc/JdbcTest.kt index bb3cf4f1826..1563f04a25d 100644 --- a/libraries/kotlin-jdbc/src/test/kotlin/test/kotlin/jdbc/JdbcTest.kt +++ b/libraries/kotlin-jdbc/src/test/kotlin/test/kotlin/jdbc/JdbcTest.kt @@ -7,6 +7,8 @@ import junit.framework.TestCase import org.h2.jdbcx.JdbcDataSource import javax.sql.DataSource import org.h2.jdbcx.JdbcConnectionPool +import kotlin.nullable.forEach +import java.sql.ResultSet public val dataSource : DataSource = createDataSource() @@ -40,4 +42,48 @@ class JdbcTest : TestCase() { } } } -} \ No newline at end of file + + fun testGetValuesAsMap() { + dataSource.query("select * from foo") { + for (row in it) { + println(row.getValuesAsMap()) + } + } + } + + fun testStringFormat() { + dataSource.query(kotlin.template.StringTemplate(array("select * from foo where id = ", 1))) { + for (row in it) { + println(row.getValuesAsMap()) + } + } + } + + fun testMapIterator() { + val mapper = { (rs : ResultSet) -> + "id: ${rs["id"]}" + } + + dataSource.query("select * from foo") { + for (row in it.getMapped(mapper)) { + println(row) + } + } + } + + fun testCount() { + dataSource.query("select count(*) from foo") { + println("count: ${it.singleInt()}") + } + } + + fun testFormatCursor() { + dataSource.query("select * from foo") { + println(it.getColumnNames().toList().makeString("\t")) + + for (row in it) { + println(it.getValues().makeString("\t")) + } + } + } +}