-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implement named parameters * Refactor Driver Interface. * Add NamedParameters to README.md * Stop usage of generic exception * Expose SqlParameterSource in execute() * Bump version
- Loading branch information
1 parent
0d4f22f
commit 69010cd
Showing
15 changed files
with
856 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,16 +5,14 @@ | |
[![Maven Central](https://img.shields.io/maven-central/v/io.github.moreirasantos/pgkn)](https://central.sonatype.com/artifact/io.github.moreirasantos/pgkn/) | ||
[![Kotlin](https://img.shields.io/badge/kotlin-1.9.0-blue.svg?logo=kotlin)](http://kotlinlang.org) | ||
|
||
|
||
# pgkn | ||
PostgreSQL Kotlin/Native Driver | ||
|
||
## Usage | ||
``` | ||
// Show full structure of a kotlin native project | ||
implementation("io.github.moreirasantos:pgkn:1.0.0") | ||
``` | ||
``` | ||
```kotlin | ||
fun main() { | ||
val driver = PostgresDriver( | ||
host = "host.docker.internal", | ||
|
@@ -44,3 +42,19 @@ fun main() { | |
} | ||
} | ||
``` | ||
## Features | ||
## Named Parameters | ||
```kotlin | ||
driver.execute( | ||
"select name from my_table where name = :one OR email = :other", | ||
mapOf("one" to "your_name", "other" to "[email protected]") | ||
) { it.getString(0) } | ||
``` | ||
Named Parameters provides an alternative to the traditional syntax using `?` to specify parameters. | ||
Under the hood, it substitutes the named parameters to a query placeholder. | ||
|
||
In JDBC, the placeholder would be `?` but with libpq, we will pass `$1`, `$2`, etc as stated here: | ||
[31.3.1. Main Functions - PQexecParams](https://www.postgresql.org/docs/9.5/libpq-exec.html) | ||
|
||
This feature implementation tries to follow Spring's `NamedParameterJdbcTemplate` as close as possible. | ||
[NamedParameterJdbcTemplate](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.html) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 0 additions & 4 deletions
4
src/commonMain/kotlin/io/github/moreirasantos/pgkn/PgKnException.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
src/commonMain/kotlin/io/github/moreirasantos/pgkn/exception/PgKnException.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package io.github.moreirasantos.pgkn.exception | ||
|
||
sealed class SQLException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) | ||
|
||
class InvalidDataAccessApiUsageException(message: String, cause: Throwable? = null) : SQLException(message, cause) | ||
|
||
class AnonymousClassException : SQLException("Class must not be anonymous", null) | ||
|
||
class GetColumnValueException(columnIndex: Int) : SQLException("Error getting column $columnIndex value", null) |
128 changes: 128 additions & 0 deletions
128
src/commonMain/kotlin/io/github/moreirasantos/pgkn/paramsource/AbstractSqlParameterSource.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package io.github.moreirasantos.pgkn.paramsource | ||
|
||
import io.github.moreirasantos.pgkn.exception.AnonymousClassException | ||
import io.github.moreirasantos.pgkn.paramsource.SqlParameterSource.Companion.TYPE_UNKNOWN | ||
import kotlinx.datetime.Instant | ||
import kotlinx.datetime.LocalDate | ||
import kotlinx.datetime.LocalDateTime | ||
import kotlinx.datetime.LocalTime | ||
import kotlin.reflect.KClass | ||
|
||
|
||
/** | ||
* Abstract base class for [SqlParameterSource] implementations. | ||
* Provides registration of SQL types per parameter and a friendly | ||
* [toString] representation. | ||
* Concrete subclasses must implement [hasValue] and [getValue]. | ||
*/ | ||
abstract class AbstractSqlParameterSource : SqlParameterSource { | ||
private val sqlTypes: MutableMap<String, UInt> = HashMap() | ||
private val typeNames: MutableMap<String, String> = HashMap() | ||
|
||
/** | ||
* Register an SQL type for the given parameter. | ||
* @param paramName the name of the parameter | ||
* @param sqlType the SQL type of the parameter | ||
*/ | ||
fun registerSqlType(paramName: String, sqlType: UInt) { | ||
sqlTypes[paramName] = sqlType | ||
} | ||
|
||
fun registerSqlType(paramName: String, value: Any?) { | ||
registerSqlType( | ||
paramName = paramName, | ||
sqlType = value?.let { oidMap[it::class.simpleName ?: throw AnonymousClassException()] } | ||
?: TYPE_UNKNOWN | ||
) | ||
} | ||
|
||
/** | ||
* Register an SQL type for the given parameter. | ||
* @param paramName the name of the parameter | ||
* @param typeName the type name of the parameter | ||
*/ | ||
fun registerTypeName(paramName: String, typeName: String) { | ||
typeNames[paramName] = typeName | ||
} | ||
|
||
/** | ||
* Return the SQL type for the given parameter, if registered. | ||
* @param paramName the name of the parameter | ||
* @return the SQL type of the parameter, | ||
* or `TYPE_UNKNOWN` if not registered | ||
*/ | ||
override fun getSqlType(paramName: String) = sqlTypes[paramName] ?: TYPE_UNKNOWN | ||
|
||
/** | ||
* Return the type name for the given parameter, if registered. | ||
* @param paramName the name of the parameter | ||
* @return the type name of the parameter, | ||
* or `null` if not registered | ||
*/ | ||
fun getTypeName(paramName: String) = typeNames[paramName] | ||
|
||
/** | ||
* Enumerate the parameter names and values with their corresponding SQL type if available, | ||
* or just return the simple `SqlParameterSource` implementation class name otherwise. | ||
* @since 5.2 | ||
* @see .getParameterNames | ||
*/ | ||
@Suppress("NestedBlockDepth") | ||
override fun toString(): String { | ||
val parameterNames: Array<String>? = parameterNames | ||
return if (parameterNames != null) { | ||
val array = ArrayList<String>(parameterNames.size) | ||
for (parameterName in parameterNames) { | ||
val value = getValue(parameterName) | ||
/* | ||
if (value is SqlParameterValue) { | ||
value = (value as SqlParameterValue?).getValue() | ||
} | ||
*/ | ||
var typeName = getTypeName(parameterName) | ||
if (typeName == null) { | ||
val sqlType = getSqlType(parameterName) | ||
if (sqlType != TYPE_UNKNOWN) { | ||
typeName = sqlTypeNames[sqlType] | ||
if (typeName == null) { | ||
typeName = sqlType.toString() | ||
} | ||
} | ||
} | ||
val entry = StringBuilder() | ||
entry.append(parameterName).append('=').append(value) | ||
if (typeName != null) { | ||
entry.append(" (type:").append(typeName).append(')') | ||
} | ||
array.add(entry.toString()) | ||
} | ||
array.joinToString( | ||
separator = ", ", | ||
prefix = this::class.simpleName + " {", | ||
postfix = "}" | ||
) | ||
} else { | ||
this::class.simpleName!! | ||
} | ||
} | ||
} | ||
|
||
// Full list here: https://jdbc.postgresql.org/documentation/publicapi/constant-values.html | ||
private val oidMap: Map<String, UInt> = hashMapOf( | ||
Boolean::class.namedClassName to 16u, | ||
ByteArray::class.namedClassName to 17u, | ||
Long::class.namedClassName to 20u, | ||
Int::class.namedClassName to 23u, | ||
String::class.namedClassName to 25u, | ||
Double::class.namedClassName to 701u, | ||
LocalDate::class.namedClassName to 1082u, | ||
LocalTime::class.namedClassName to 1083u, | ||
LocalDateTime::class.namedClassName to 1114u, | ||
Instant::class.namedClassName to 1184u, | ||
// intervalOid = 1186u | ||
// uuidOid = 2950u | ||
) | ||
|
||
private val sqlTypeNames: Map<UInt, String> = oidMap.entries.associateBy({ it.value }) { it.key } | ||
|
||
private val KClass<*>.namedClassName get() = this.simpleName!! |
Oops, something went wrong.