13 Commits
2.1.2 ... 1.1.0

Author SHA1 Message Date
8a452a6897 Add ShadowJar compatibility and Query file can be define name with comment
Add Migration and Query definition class
Add Docker DB for tests
2020-05-10 21:45:50 +02:00
Fabrice Lecomte
4c73ef1e0f set JVM to 11 for jitpack 2020-03-25 16:35:18 +01:00
Fabrice Lecomte
4c03eb7c87 set JVM to 11 2020-03-25 16:25:55 +01:00
Fabrice Lecomte
07678afbe7 set JVM to 11 2020-03-25 16:18:17 +01:00
Fabrice Lecomte
d3bcfdd627 config package 2020-03-25 15:26:19 +01:00
Fabrice Lecomte
deea5153f2 add method function.perform 2020-03-25 00:30:54 +01:00
Fabrice Lecomte
05fb868574 Add serialise list 2020-03-15 20:18:55 +01:00
Fabrice Lecomte
bbeec7bb60 add methode$ "isLastPage" to pagination 2020-02-24 20:41:29 +01:00
Fabrice Lecomte
012beb6884 Improve SQL log message 2020-02-19 16:46:20 +01:00
Fabrice Lecomte
45707f9734 change Idea config 2020-02-19 12:46:04 +01:00
Fabrice Lecomte
4e89aa072d RepositoryI::requester is now readonly 2020-02-11 20:39:43 +01:00
Fabrice Lecomte
b65e82a52f Improve UuidEntityVersioning 2020-01-29 17:03:22 +01:00
Fabrice Lecomte
898951e91a Add Immutable Entities 2020-01-23 00:40:47 +01:00
42 changed files with 345 additions and 279 deletions

View File

@@ -2,30 +2,16 @@
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
<package name="java.util" withSubpackages="true" static="false" />
</value>
</option>
<option name="SPACE_BEFORE_EXTEND_COLON" value="false" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="IMPORT_NESTED_CLASSES" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CALL_PARAMETERS_WRAP" value="5" />
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_WRAP" value="5" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>

2
.idea/misc.xml generated
View File

@@ -3,7 +3,7 @@
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="corretto-11" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="adopt-openjdk-11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="TaskProjectConfiguration">

View File

@@ -1,6 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Check" type="CompoundRunConfigurationType">
<toRun name="run DB" type="docker-deploy" />
<toRun name="Lint" type="GradleRunConfiguration" />
<toRun name="tests" type="JUnit" />
<method v="2" />

View File

@@ -0,0 +1,34 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test and Publish To MavenLocal" type="GradleRunConfiguration" factoryName="Gradle" singleton="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="publishToMavenLocal" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<extension name="net.ashald.envfile">
<option name="IS_ENABLED" value="false" />
<option name="IS_SUBST" value="false" />
<option name="IS_PATH_MACRO_SUPPORTED" value="false" />
<option name="IS_IGNORE_MISSING_FILES" value="false" />
<option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
<ENTRIES>
<ENTRY IS_ENABLED="true" PARSER="runconfig" />
</ENTRIES>
</extension>
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Lint" run_configuration_type="GradleRunConfiguration" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="tests" run_configuration_type="JUnit" />
</method>
</configuration>
</component>

View File

@@ -4,15 +4,14 @@ plugins {
jacoco
id("maven-publish")
kotlin("jvm") version "1.4.30"
id("org.jetbrains.kotlin.jvm") version "1.3.50"
id("org.jlleitschuh.gradle.ktlint") version "10.0.0"
id("org.owasp.dependencycheck") version "6.1.1"
id("fr.coppernic.versioning") version "3.2.1"
id("com.avast.gradle.docker-compose") version "0.14.0"
id("org.jlleitschuh.gradle.ktlint") version "8.2.0"
id("org.owasp.dependencycheck") version "5.1.0"
id("fr.coppernic.versioning") version "3.1.2"
}
group = "com.github.flecomte"
group = "flecomte"
version = versioning.info.tag
repositories {
@@ -28,41 +27,19 @@ tasks.withType<KotlinCompile> {
}
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
}
tasks.test {
useJUnit()
useJUnitPlatform()
systemProperty("junit.jupiter.execution.parallel.enabled", true)
finalizedBy(tasks.ktlintCheck)
}
tasks.publishToMavenLocal {
dependsOn(tasks.test)
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.4.31")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.1")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.12.1")
implementation("com.github.jasync-sql:jasync-postgresql:1.1.7")
implementation("org.slf4j:slf4j-api:1.7.30")
implementation("com.avast.gradle:gradle-docker-compose-plugin:0.14.0")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.3.31")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.9.9")
implementation("com.github.jasync-sql:jasync-postgresql:1.0.7")
implementation("org.slf4j:slf4j-api:1.7.26")
testImplementation("ch.qos.logback:logback-classic:1.2.3")
testImplementation("ch.qos.logback:logback-core:1.2.3")
testImplementation("io.mockk:mockk:1.10.6")
testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.4.30")
testImplementation("org.amshove.kluent:kluent:1.65")
testImplementation("io.mockk:mockk:1.9")
testImplementation("org.junit.jupiter:junit-jupiter:5.4.2")
testImplementation("org.amshove.kluent:kluent:1.47")
}
val sourcesJar by tasks.creating(Jar::class) {
@@ -70,14 +47,6 @@ val sourcesJar by tasks.creating(Jar::class) {
from(sourceSets.getByName("main").allSource)
}
apply(plugin = "docker-compose")
dockerCompose {
projectName = "postgres-json"
useComposeFiles = listOf("docker-compose.yml")
stopContainers = true
isRequiredBy(project.tasks.test)
}
publishing {
repositories {
maven {
@@ -96,4 +65,4 @@ publishing {
artifact(sourcesJar)
}
}
}
}

View File

@@ -13,3 +13,13 @@ services:
POSTGRES_DB: json_test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
pgadmin:
container_name: pgadmin4_json
image: dpage/pgadmin4
restart: always
ports:
- 8585:80
environment:
PGADMIN_DEFAULT_EMAIL: rusk23@gmail.com
PGADMIN_DEFAULT_PASSWORD: azerty

View File

@@ -1,4 +1,4 @@
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists

View File

@@ -12,7 +12,7 @@ import fr.postgresjson.entity.Serializable
import fr.postgresjson.serializer.Serializer
import fr.postgresjson.utils.LoggerDelegate
import org.slf4j.Logger
import java.util.concurrent.CompletableFuture
import java.util.concurrent.*
typealias SelectOneCallback<T> = QueryResult.(T?) -> Unit
typealias SelectCallback<T> = QueryResult.(List<T>) -> Unit
@@ -25,26 +25,17 @@ class Connection(
private val host: String = "localhost",
private val port: Int = 5432
) : Executable {
private var connection: ConnectionPool<PostgreSQLConnection>? = null
private lateinit var connection: ConnectionPool<PostgreSQLConnection>
private val serializer = Serializer()
private val logger: Logger? by LoggerDelegate()
internal fun connect(): ConnectionPool<PostgreSQLConnection> {
return connection.let { connectionPool ->
if (connectionPool == null || !connectionPool.isConnected()) {
PostgreSQLConnectionBuilder.createConnectionPool(
"jdbc:postgresql://$host:$port/$database?user=$username&password=$password"
).also {
connection = it
}
} else {
connectionPool
}
if (!::connection.isInitialized || !connection.isConnected()) {
connection = PostgreSQLConnectionBuilder.createConnectionPool(
"jdbc:postgresql://$host:$port/$database?user=$username&password=$password"
)
}
}
fun disconnect() {
connection?.run { disconnect() }
return connection
}
fun <A> inTransaction(f: (Connection) -> CompletableFuture<A>) = connect().inTransaction(f)
@@ -59,7 +50,7 @@ class Connection(
it is EntityI && typeReference.type.typeName == it::class.java.name
} as R?
val result = exec(sql, compileArgs(values))
val json = result.rows.firstOrNull()?.getString(0)
val json = result.rows[0].getString(0)
return if (json === null) {
null
} else {
@@ -280,14 +271,11 @@ class Connection(
logger?.debug("Query executed in $duration ms \n{}", args)
return result
} catch (e: Throwable) {
logger?.info(
"""
logger?.info("""
Query Error:
${sql.prependIndent()},
${values.joinToString(", ").prependIndent()}
""".trimIndent(),
e
)
""".trimIndent(), e)
throw e
}
}

View File

@@ -59,4 +59,4 @@ interface EmbedExecutable {
fun sendQuery(values: Map<String, Any?>): Int
fun sendQuery(vararg values: Pair<String, Any?>): Int =
sendQuery(values.toMap())
}
}

View File

@@ -52,4 +52,4 @@ interface Executable {
fun exec(sql: String, values: Map<String, Any?>): QueryResult
fun sendQuery(sql: String, values: List<Any?> = emptyList()): Int
fun sendQuery(sql: String, values: Map<String, Any?>): Int
}
}

View File

@@ -205,4 +205,4 @@ class Function(val definition: Function, override val connection: Connection) :
return placeholders.joinToString(separator = ", ")
}
}
}

View File

@@ -22,4 +22,4 @@ data class Paginated<T : EntityI>(
fun isLastPage(): Boolean = currentPage >= totalPages
private fun Double.ceil(): Int = ceil(this).toInt()
}
}

View File

@@ -106,4 +106,4 @@ class Query(override val name: String, private val sql: String, override val con
override fun sendQuery(values: Map<String, Any?>): Int {
return connection.sendQuery(sql, values)
}
}
}

View File

@@ -3,6 +3,7 @@ package fr.postgresjson.connexion
import fr.postgresjson.utils.searchSqlFiles
import java.net.URI
import fr.postgresjson.definition.Function as DefinitionFunction
import fr.postgresjson.definition.Function as FunctionDefinition
import fr.postgresjson.definition.Query as QueryDefinition
class Requester(
@@ -51,7 +52,7 @@ class Requester(
fun addFunction(functionsDirectory: URI): Requester {
functionsDirectory.searchSqlFiles()
.forEach {
if (it is DefinitionFunction) {
if (it is FunctionDefinition) {
addFunction(it)
}
}

View File

@@ -87,4 +87,4 @@ class Function(
}
}
}
}
}

View File

@@ -32,4 +32,4 @@ class Migration(
class MigrationNotFound(cause: Throwable? = null) : Resource.ParseException("Migration not found in script", cause)
enum class Direction { UP, DOWN }
}
}

View File

@@ -30,4 +30,4 @@ class Parameter(val name: String, val type: String, direction: Direction? = Dire
interface ParametersInterface {
val parameters: List<Parameter>
}
}

View File

@@ -14,12 +14,12 @@ class Query(
"""-- *name ?: ?(?<name>[^ \n]+)"""
.toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
.find(script)?.let {
it.groups["name"]?.value?.trim()
}
it.groups["name"]?.value?.trim()
}
/** Try to get name from the filename */
private fun getNameFromFile(source: Path): String = source
.fileName.toString()
.substringAfterLast("/")
.substringBeforeLast(".sql")
}
}

View File

@@ -20,10 +20,10 @@ interface Resource {
fun build(resource: String, path: Path): Resource =
try {
Migration(resource, path)
Function(resource, path)
} catch (e: ParseException) {
try {
Function(resource, path)
Migration(resource, path)
} catch (e: ParseException) {
try {
Query(resource, path)
@@ -37,4 +37,4 @@ interface Resource {
interface ResourceCollection {
val parameters: List<Parameter>
}
}

View File

@@ -1,7 +1,12 @@
package fr.postgresjson.entity
package fr.postgresjson.entity.immutable
import fr.postgresjson.entity.EntityI
import fr.postgresjson.entity.mutable.EntityDeletedAt
import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import fr.postgresjson.entity.mutable.EntityDeletedBy
import fr.postgresjson.entity.mutable.EntityDeletedByImp
import org.joda.time.DateTime
import java.util.UUID
import java.util.*
interface EntityRefI<T> : EntityI {
val id: T
@@ -12,7 +17,7 @@ interface UuidEntityI : EntityRefI<UUID> {
}
abstract class Entity<T>(override val id: T) : EntityRefI<T>
open class UuidEntity(id: UUID? = null) : UuidEntityI, Entity<UUID>(id ?: UUID.randomUUID())
open class UuidEntity(override val id: UUID = UUID.randomUUID()) : UuidEntityI, Entity<UUID>(id)
/* Version */
interface EntityVersioning<ID, NUMBER> {
@@ -21,27 +26,16 @@ interface EntityVersioning<ID, NUMBER> {
}
class UuidEntityVersioning(
override val versionNumber: Int,
versionId: UUID? = null
) : EntityVersioning<UUID, Int> {
override val versionId: UUID = versionId ?: UUID.randomUUID()
}
override var versionNumber: Int? = null,
override val versionId: UUID = UUID.randomUUID()
) : EntityVersioning<UUID, Int?>
/* Dates */
interface EntityCreatedAt {
val createdAt: DateTime
}
interface EntityUpdatedAt {
val updatedAt: DateTime
}
interface EntityDeletedAt {
val deletedAt: DateTime?
fun isDeleted(): Boolean {
return deletedAt?.let {
it < DateTime.now()
} ?: false
}
var updatedAt: DateTime
}
class EntityCreatedAtImp(
@@ -49,23 +43,15 @@ class EntityCreatedAtImp(
) : EntityCreatedAt
class EntityUpdatedAtImp(
override val updatedAt: DateTime = DateTime.now()
override var updatedAt: DateTime = DateTime.now()
) : EntityUpdatedAt
class EntityDeletedAtImp(
override val deletedAt: DateTime? = null
) : EntityDeletedAt
/* Author */
interface EntityCreatedBy<T : EntityI> {
val createdBy: T
}
interface EntityUpdatedBy<T : EntityI> {
val updatedBy: T
}
interface EntityDeletedBy<T : EntityI> {
val deletedBy: T?
var updatedBy: T
}
class EntityCreatedByImp<UserT : EntityI>(
@@ -73,13 +59,9 @@ class EntityCreatedByImp<UserT : EntityI>(
) : EntityCreatedBy<UserT>
class EntityUpdatedByImp<UserT : EntityI>(
override val updatedBy: UserT
override var updatedBy: UserT
) : EntityUpdatedBy<UserT>
class EntityDeletedByImp<UserT : EntityI>(
override val deletedBy: UserT?
) : EntityDeletedBy<UserT>
/* Mixed */
class EntityCreatedImp<UserT : EntityI>(
override val createdAt: DateTime = DateTime.now(),
@@ -89,22 +71,10 @@ class EntityCreatedImp<UserT : EntityI>(
class EntityUpdatedImp<UserT : EntityI>(
updatedAt: DateTime = DateTime.now(),
override val updatedBy: UserT
override var updatedBy: UserT
) : EntityUpdatedBy<UserT>,
EntityUpdatedAt by EntityUpdatedAtImp(updatedAt)
/* Published */
interface Published<UserT : EntityI> {
val publishedAt: DateTime?
val publishedBy: UserT?
}
class EntityPublishedImp<UserT : EntityI>(
override val publishedBy: UserT?
) : Published<UserT> {
override val publishedAt: DateTime? = null
}
/* Implementation */
abstract class EntityImp<T, UserT : EntityI>(
updatedBy: UserT,
@@ -115,12 +85,4 @@ abstract class EntityImp<T, UserT : EntityI>(
EntityDeletedAt by EntityDeletedAtImp(),
EntityCreatedBy<UserT> by EntityCreatedByImp(updatedBy),
EntityUpdatedBy<UserT> by EntityUpdatedByImp(updatedBy),
EntityDeletedBy<UserT> by EntityDeletedByImp(updatedBy)
abstract class UuidEntityExtended<T, UserT : EntityI>(
updatedBy: UserT,
publishedBy: UserT?
) :
EntityImp<T, UserT>(updatedBy),
EntityVersioning<UUID, Int> by UuidEntityVersioning(0),
Published<UserT> by EntityPublishedImp(publishedBy)
EntityDeletedBy<UserT> by EntityDeletedByImp(updatedBy)

View File

@@ -0,0 +1,135 @@
package fr.postgresjson.entity.mutable
import fr.postgresjson.entity.EntityI
import org.joda.time.DateTime
import java.util.*
interface EntityRefI<T> : EntityI {
var id: T?
}
interface UuidEntityI : EntityRefI<UUID> {
override var id: UUID?
}
interface IdEntityI : EntityRefI<Int> {
override var id: Int?
}
abstract class Entity<T>(override var id: T? = null) : EntityRefI<T>
open class UuidEntity(override var id: UUID? = UUID.randomUUID()) : UuidEntityI, Entity<UUID>(id)
open class IdEntity(override var id: Int? = null) : IdEntityI, Entity<Int>(id)
/* Version */
interface EntityVersioning<ID, NUMBER> {
var versionId: ID
var versionNumber: NUMBER?
}
class UuidEntityVersioning(
override var versionNumber: Int? = null,
override var versionId: UUID = UUID.randomUUID()
) : EntityVersioning<UUID, Int>
/* Dates */
interface EntityCreatedAt {
var createdAt: DateTime?
}
interface EntityUpdatedAt {
var updatedAt: DateTime?
}
interface EntityDeletedAt {
var deletedAt: DateTime?
fun isDeleted(): Boolean {
val deletedAt = deletedAt
return deletedAt != null && deletedAt < DateTime.now()
}
}
class EntityCreatedAtImp : EntityCreatedAt {
override var createdAt: DateTime? = null
}
class EntityUpdatedAtImp : EntityUpdatedAt {
override var updatedAt: DateTime? = null
}
class EntityDeletedAtImp : EntityDeletedAt {
override var deletedAt: DateTime? = null
}
/* Author */
interface EntityCreatedBy<T : EntityI> {
var createdBy: T?
}
interface EntityUpdatedBy<T : EntityI> {
var updatedBy: T?
}
interface EntityDeletedBy<T : EntityI> {
var deletedBy: T?
}
class EntityCreatedByImp<UserT : EntityI>(
override var createdBy: UserT?
) : EntityCreatedBy<UserT>
class EntityUpdatedByImp<UserT : EntityI>(
override var updatedBy: UserT?
) : EntityUpdatedBy<UserT>
class EntityDeletedByImp<UserT : EntityI>(
override var deletedBy: UserT?
) : EntityDeletedBy<UserT>
/* Mixed */
class EntityDeletedImp<UserT : EntityI>(
override var deletedBy: UserT? = null
) : EntityDeletedBy<UserT>,
EntityDeletedAt by EntityDeletedAtImp()
class EntityUpdatedImp<UserT : EntityI>(
override var updatedAt: DateTime? = null,
override var updatedBy: UserT? = null
) : EntityUpdatedBy<UserT>,
EntityUpdatedAt by EntityUpdatedAtImp()
class EntityCreatedImp<UserT : EntityI>(
override var createdAt: DateTime? = null,
override var createdBy: UserT? = null
) : EntityCreatedBy<UserT>,
EntityCreatedAt by EntityCreatedAtImp()
/* Published */
interface Published<UserT : EntityI> {
var publishedAt: DateTime?
var publishedBy: UserT?
}
class EntityPublishedImp<UserT : EntityI>(
override var publishedBy: UserT?
) : Published<UserT> {
override var publishedAt: DateTime? = null
}
/* Implementation */
abstract class EntityImp<T, UserT : EntityI>(
updatedBy: UserT?
) : Entity<T>(),
EntityCreatedAt by EntityCreatedAtImp(),
EntityUpdatedAt by EntityUpdatedAtImp(),
EntityDeletedAt by EntityDeletedAtImp(),
EntityCreatedBy<UserT> by EntityCreatedByImp(updatedBy),
EntityUpdatedBy<UserT> by EntityUpdatedByImp(updatedBy),
EntityDeletedBy<UserT> by EntityDeletedByImp(updatedBy)
abstract class UuidEntityExtended<T, UserT : EntityI>(
updatedBy: UserT?,
publishedBy: UserT?
) :
EntityImp<T, UserT>(updatedBy),
EntityVersioning<UUID, Int> by UuidEntityVersioning(),
Published<UserT> by EntityPublishedImp(publishedBy)

View File

@@ -4,8 +4,8 @@ import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException
import fr.postgresjson.connexion.Connection
import fr.postgresjson.migration.Migration.Action
import fr.postgresjson.migration.Migration.Status
import java.util.Date
import java.util.concurrent.CompletionException
import java.util.*
import java.util.concurrent.*
import fr.postgresjson.definition.Function as DefinitionFunction
data class Function(
@@ -93,4 +93,4 @@ data class Function(
infix fun `is different from`(other: DefinitionFunction): Boolean {
return other.script != this.up.script
}
}
}

View File

@@ -2,7 +2,8 @@ package fr.postgresjson.migration
import com.fasterxml.jackson.core.type.TypeReference
import fr.postgresjson.connexion.Connection
import fr.postgresjson.entity.Entity
import fr.postgresjson.definition.Migration as DefinitionMigration
import fr.postgresjson.entity.mutable.Entity
import fr.postgresjson.migration.Migration.Action
import fr.postgresjson.migration.Migration.Status
import fr.postgresjson.utils.LoggerDelegate
@@ -10,9 +11,8 @@ import fr.postgresjson.utils.searchSqlFiles
import org.slf4j.Logger
import java.io.FileNotFoundException
import java.net.URI
import java.util.Date
import java.util.*
import fr.postgresjson.definition.Function as DefinitionFunction
import fr.postgresjson.definition.Migration as DefinitionMigration
class MigrationEntity(
val filename: String,
@@ -36,13 +36,12 @@ interface Migration {
data class Migrations private constructor(
private val connection: Connection,
private val migrationsScripts: MutableMap<String, MigrationScript> = mutableMapOf(),
private val migrationsScripts: MutableMap<String, Query> = mutableMapOf(),
private val functions: MutableMap<String, Function> = mutableMapOf()
) {
private var directories: List<URI> = emptyList()
private val logger: Logger? by LoggerDelegate()
constructor(directory: URI, connection: Connection) : this(listOf(directory), connection)
constructor(connection: Connection, vararg directory: URI) : this(directory.toList(), connection)
constructor(directories: List<URI>, connection: Connection) : this(connection) {
initDB()
@@ -86,7 +85,7 @@ data class Migrations private constructor(
this::class.java.classLoader.getResource("sql/migration/findAllHistory.sql")!!.readText().let {
connection.select<MigrationEntity>(it, object : TypeReference<List<MigrationEntity>>() {})
.map { query ->
migrationsScripts[query.filename] = MigrationScript(query.filename, query.up, query.down, connection, query.executedAt)
migrationsScripts[query.filename] = Query(query.filename, query.up, query.down, connection, query.executedAt)
}
}
}
@@ -106,8 +105,8 @@ data class Migrations private constructor(
private fun getMigrationFromDirectory(directory: URI) {
val downs: MutableMap<String, DefinitionMigration> = mutableMapOf()
/* Set Down Migration */
directory.searchSqlFiles().apply {
/* Set Down Migration */
forEach { migration ->
if (migration is DefinitionMigration && migration.direction == DefinitionMigration.Direction.DOWN) {
downs += migration.name to migration
@@ -120,7 +119,7 @@ data class Migrations private constructor(
val down = downs[migration.name] ?: throw DownMigrationNotDefined(migration.name + ".down.sql")
downs -= migration.name
addMigrationScript(migration, down)
addQuery(migration, down)
} else if (migration is DefinitionFunction) {
addFunction(migration)
}
@@ -154,12 +153,12 @@ data class Migrations private constructor(
return this
}
fun addMigrationScript(up: DefinitionMigration, down: DefinitionMigration, callback: (MigrationScript) -> Unit = {}): Migrations =
addMigrationScript(up.name, up.script, down.script, callback)
fun addQuery(up: DefinitionMigration, down: DefinitionMigration, callback: (Query) -> Unit = {}): Migrations =
addQuery(up.name, up.script, down.script, callback)
fun addMigrationScript(name: String, up: String, down: String, callback: (MigrationScript) -> Unit = {}): Migrations {
fun addQuery(name: String, up: String, down: String, callback: (Query) -> Unit = {}): Migrations {
if (migrationsScripts[name] === null) {
migrationsScripts[name] = MigrationScript(name, up, down, connection).apply {
migrationsScripts[name] = Query(name, up, down, connection).apply {
doExecute = Action.UP
}
} else {
@@ -313,4 +312,4 @@ data class Migrations private constructor(
fun status(): Map<String, Int> {
TODO("not implemented")
}
}
}

View File

@@ -1,11 +1,11 @@
package fr.postgresjson.migration
import fr.postgresjson.connexion.Connection
import fr.postgresjson.entity.Entity
import fr.postgresjson.entity.mutable.Entity
import fr.postgresjson.migration.Migration.Action
import java.util.Date
import java.util.*
data class MigrationScript(
data class Query(
val name: String,
val up: String,
val down: String,
@@ -57,9 +57,9 @@ data class MigrationScript(
return Migration.Status.OK // TODO
}
fun copy(): MigrationScript {
fun copy(): Query {
return this.copy(name = name, up = up, down = down, connection = connection, executedAt = executedAt).also {
it.doExecute = this.doExecute
}
}
}
}

View File

@@ -51,4 +51,4 @@ class Serializer(val mapper: ObjectMapper = jacksonObjectMapper()) {
fun Serializable.serialize(pretty: Boolean = false) = Serializer().serialize(this, pretty)
fun List<Serializable>.serialize(pretty: Boolean = false) = Serializer().serialize(this, pretty)
inline fun <reified E : Serializable> E.deserialize(json: String) = Serializer().deserialize(json, this)
inline fun <reified E : Serializable> String.deserialize() = Serializer().deserialize<E>(this)
inline fun <reified E : Serializable> String.deserialize() = Serializer().deserialize<E>(this)

View File

@@ -7,4 +7,4 @@ import kotlin.reflect.KProperty
internal class LoggerDelegate<in R : Any> : ReadOnlyProperty<R, Logger> {
override fun getValue(thisRef: R, property: KProperty<*>) = LoggerFactory.getLogger(thisRef.javaClass.packageName)
}
}

View File

@@ -5,7 +5,6 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.net.URI
import java.net.URL
import java.nio.file.FileSystemNotFoundException
import java.nio.file.FileSystems
import java.nio.file.FileVisitOption
import java.nio.file.Files
@@ -17,19 +16,15 @@ fun URL.searchSqlFiles() = this.toURI().searchSqlFiles()
fun URI.searchSqlFiles() = sequence<Resource> {
val logger: Logger = LoggerFactory.getLogger("sqlFilesSearch")
val uri: URI = this@searchSqlFiles
logger.debug("""SQL files found in "${uri.toString().substringAfter('!')}" :""")
if (uri.scheme == "jar") {
try {
FileSystems.getFileSystem(uri)
} catch (e: FileSystemNotFoundException) {
FileSystems.newFileSystem(uri, emptyMap<String, Any>())
}
uri
val relativePath = uri.toString().substringAfter('!')
FileSystems
.newFileSystem(uri, emptyMap<String, Any>())
.getPath(relativePath)
.walk(5)
.asSequence()
.filter { it.fileName.toString().endsWith(".sql") }
.map { it.toUri().toURL() }
.map { this::class.java.getResource(path.toString()) }
.forEach {
logger.debug(it.toString())
yield(Resource.build(it))
@@ -47,5 +42,5 @@ fun URI.searchSqlFiles() = sequence<Resource> {
}
}
private fun Path.walk(maxDepth: Int = 2147483647, vararg options: FileVisitOption) = Files.walk(this, maxDepth, *options).sorted()
private fun URI.walk(maxDepth: Int = 2147483647, vararg options: FileVisitOption) = Files.walk(Path.of(this), maxDepth, *options).sorted()
private fun Path.walk(maxDepth: Int = 2147483647, vararg options: FileVisitOption) = Files.walk(this, maxDepth, *options)
private fun URI.walk(maxDepth: Int = 2147483647, vararg options: FileVisitOption) = Files.walk(Path.of(this), maxDepth, *options)

View File

@@ -1,73 +1,70 @@
package fr.postgresjson
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.entity.mutable.IdEntity
import fr.postgresjson.entity.Parameter
import fr.postgresjson.entity.UuidEntity
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Assert.*
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import java.util.UUID
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ConnectionTest() : TestAbstract() {
private class ObjTest(val name: String, id: UUID = UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00")) : UuidEntity(id)
private class ObjTest2(val title: String, var test: ObjTest?) : UuidEntity()
private class ObjTest3(val first: String, var seconde: String, var third: Int) : UuidEntity()
private class ObjTestWithParameterObject(var first: ParameterObject, var seconde: ParameterObject) : UuidEntity()
private class ObjTest(var name: String) : IdEntity()
private class ObjTest2(var title: String, var test: ObjTest?) : IdEntity()
private class ObjTest3(var first: String, var seconde: String, var third: Int) : IdEntity()
private class ObjTestWithParameterObject(var first: ParameterObject, var seconde: ParameterObject) : IdEntity()
private class ParameterObject(var third: String) : Parameter
@Test
fun getObject() {
val obj: ObjTest? = connection.selectOne("select to_json(a) from test a limit 1")
assertTrue(obj is ObjTest)
assertTrue(obj!!.id == UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96"))
assertTrue(obj!!.id == 1)
}
@Test
fun getExistingObject() {
val objs: List<ObjTest2> = connection.select(
"""
select
json_agg(j)
FROM (
SELECT
t.id, t.title,
t2 as test
from test2 t
JOIN test t2 ON t.test_id = t2.id
) j;
""".trimIndent()
val objs: List<ObjTest2> = connection.select("""
select
json_agg(j)
FROM (
SELECT
t.id, t.title,
t2 as test
from test2 t
JOIN test t2 ON t.test_id = t2.id
) j;
""".trimIndent()
)
assertNotNull(objs)
assertEquals(objs.size, 2)
assertEquals(objs[0].id, UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96"))
assertEquals(objs[0].test!!.id, UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96"))
assertEquals(objs[0].id, 1)
assertEquals(objs[0].test!!.id, 1)
}
@Test
fun callRequestWithArgs() {
val result: ObjTest? = connection.selectOne("select json_build_object('id', '2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00', 'name', ?::text)", listOf("myName"))
val result: ObjTest? = connection.selectOne("select json_build_object('id', 1, 'name', ?::text)", listOf("myName"))
assertNotNull(result)
assertEquals("myName", result!!.name)
}
@Test
fun callRequestWithArgsEntity() {
val o = ObjTest("myName", id = UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00"))
val obj: ObjTest? = connection.selectOne("select json_build_object('id', id, 'name', name) FROM json_to_record(?::json) as o(id uuid, name text);", listOf(o))
val o = ObjTest("myName")
o.id = 88
val obj: ObjTest? = connection.selectOne("select json_build_object('id', id, 'name', name) FROM json_to_record(?::json) as o(id int, name text);", listOf(o))
assertNotNull(obj)
assertTrue(obj is ObjTest)
assertEquals(obj!!.id, UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00"))
assertEquals(obj!!.id, 88)
assertEquals(obj.name, "myName")
}
@Test
fun callExec() {
val o = ObjTest("myName")
val result = connection.exec("select json_build_object('id', '2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00', 'name', ?::json->>'name')", listOf(o))
val result = connection.exec("select json_build_object('id', 1, 'name', ?::json->>'name')", listOf(o))
Assertions.assertEquals(1, result.rowsAffected)
}
@@ -144,8 +141,8 @@ class ConnectionTest() : TestAbstract() {
val result: Paginated<ObjTest> = connection.select(
"""
SELECT json_build_array(
json_build_object('id', '417aaa7e-7bc6-49b7-9fe8-6c8433b3f430', 'name', :name::text),
json_build_object('id', 'abd46e7a-e749-4ce4-8361-e7b64da89da6', 'name', :name::text || '-2')
json_build_object('id', 3, 'name', :name::text),
json_build_object('id', 4, 'name', :name::text || '-2')
), 10 as total
LIMIT :limit OFFSET :offset
""".trimIndent(),
@@ -182,4 +179,4 @@ class ConnectionTest() : TestAbstract() {
assertEquals("sec", result.seconde)
assertEquals(123, result.third)
}
}
}

View File

@@ -1,22 +1,15 @@
package fr.postgresjson
import fr.postgresjson.entity.Entity
import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedBy
import fr.postgresjson.entity.EntityI
import fr.postgresjson.entity.EntityUpdatedAt
import fr.postgresjson.entity.EntityUpdatedBy
import fr.postgresjson.entity.Published
import fr.postgresjson.entity.UuidEntityExtended
import fr.postgresjson.entity.mutable.*
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import java.util.UUID
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class EntityTest() {
private class User(id: UUID = UUID.randomUUID()) : Entity<UUID>(id)
private class ObjTest(var name: String) : UuidEntityExtended<Int?, User>(User(), User())
private class User(id: Int?) : Entity<Int?>(id)
private class ObjTest(var name: String) : UuidEntityExtended<Int?, User>(User(1), User(2))
@Test
fun getObject() {
@@ -24,11 +17,11 @@ class EntityTest() {
assertTrue(obj is ObjTest)
assertTrue(obj is UuidEntityExtended<Int?, User>)
assertTrue(obj is EntityI)
assertTrue(obj is Entity<UUID>)
assertTrue(obj is Entity<Int?>)
assertTrue(obj is Published<User>)
assertTrue(obj is EntityCreatedBy<User>)
assertTrue(obj is EntityUpdatedBy<User>)
assertTrue(obj is EntityCreatedAt)
assertTrue(obj is EntityUpdatedAt)
}
}
}

View File

@@ -10,14 +10,13 @@ import org.amshove.kluent.shouldThrow
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import java.util.UUID
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MigrationTest() : TestAbstract() {
@Test
fun `run up query`() {
val resources = this::class.java.getResource("/sql/migrations").toURI()
val m = Migrations(connection, resources)
val m = Migrations(resources, connection)
m.up().apply {
this `should contain` Pair("1", Migration.Status.OK)
size `should be equal to` 1
@@ -104,7 +103,7 @@ class MigrationTest() : TestAbstract() {
.getFunction("test_function")
.selectOne(listOf("test", "plip"))
Assertions.assertEquals(objTest!!.id, UUID.fromString("457daad5-4f1b-4eb7-80ec-6882adb8cc7d"))
Assertions.assertEquals(objTest!!.id, 3)
Assertions.assertEquals(objTest.name, "test")
}
@@ -120,7 +119,7 @@ class MigrationTest() : TestAbstract() {
.getFunction("test_function_duplicate")
.selectOne(listOf("test"))
Assertions.assertEquals(objTest!!.id, UUID.fromString("457daad5-4f1b-4eb7-80ec-6882adb8cc7d"))
Assertions.assertEquals(objTest!!.id, 3)
Assertions.assertEquals(objTest.name, "test")
val resources2 = this::class.java.getResource("/sql/function/Test2").toURI()
@@ -128,4 +127,4 @@ class MigrationTest() : TestAbstract() {
run().size `should be equal to` 1
}
}
}
}

View File

@@ -2,14 +2,13 @@ package fr.postgresjson
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntity
import fr.postgresjson.entity.mutable.IdEntity
import org.junit.Assert
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.util.UUID
class RequesterTest : TestAbstract() {
class ObjTest(var name: String, id: UUID = UUID.fromString("5623d902-3067-42f3-bfd9-095dbb12c29f")) : UuidEntity(id)
class ObjTest(var name: String) : IdEntity(1)
@Test
fun `get query from file`() {
@@ -19,7 +18,7 @@ class RequesterTest : TestAbstract() {
.getQuery("selectOne")
.selectOne()
assertEquals(objTest!!.id, UUID.fromString("829b1a29-5db8-47f9-9562-961c561ac528"))
assertEquals(objTest!!.id, 2)
assertEquals(objTest.name, "test")
}
@@ -31,7 +30,7 @@ class RequesterTest : TestAbstract() {
.getFunction("test_function")
.selectOne(listOf("test", "plip"))
assertEquals(objTest!!.id, UUID.fromString("457daad5-4f1b-4eb7-80ec-6882adb8cc7d"))
assertEquals(objTest!!.id, 3)
assertEquals(objTest.name, "test")
}
@@ -166,4 +165,4 @@ class RequesterTest : TestAbstract() {
assertEquals("myName", obj.name)
}
}
}

View File

@@ -1,6 +1,6 @@
package fr.postgresjson
import fr.postgresjson.entity.UuidEntity
import fr.postgresjson.entity.mutable.IdEntity
import fr.postgresjson.serializer.Serializer
import fr.postgresjson.serializer.deserialize
import fr.postgresjson.serializer.serialize
@@ -10,23 +10,23 @@ import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import java.util.UUID
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class SerializerTest {
private class ObjTest(var val1: String, var val2: Int, id: UUID = UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96")) : UuidEntity(id)
private class ObjTestDate(var val1: DateTime, id: UUID = UUID.fromString("829b1a29-5db8-47f9-9562-961c561ac528")) : UuidEntity(id)
private class ObjTest(var val1: String, var val2: Int) : IdEntity(1)
private class ObjTestDate(var val1: DateTime) : IdEntity(2)
private val serializer = Serializer()
private val objSerialized: String = """{"val1":"plop","val2":123,"id":"829b1a29-5db8-47f9-9562-961c561ac528"}"""
private val objSerializedWithExtra: String = """{"val1":"plop","val2":123,"id":"829b1a29-5db8-47f9-9562-961c561ac528","toto":"tata"}"""
private val objSerialized: String = """{"val1":"plop","val2":123,"id":2}"""
private val objSerializedWithExtra: String = """{"val1":"plop","val2":123,"id":2,"toto":"tata"}"""
private val objSerializedUpdate = """{"val1":"update","val2":123}"""
private lateinit var obj: ObjTest
@BeforeEach
fun before() {
obj = ObjTest("plop", 123, UUID.fromString("829b1a29-5db8-47f9-9562-961c561ac528"))
obj = ObjTest("plop", 123)
obj.id = 2
}
@Test
@@ -85,4 +85,4 @@ internal class SerializerTest {
assertEquals("update", objDeserialized.val1)
assertEquals(123, objDeserialized.val2)
}
}
}

View File

@@ -27,4 +27,4 @@ abstract class TestAbstract {
sendQuery(downSQL.readText()).join()
}.disconnect()
}
}
}

View File

@@ -8,7 +8,7 @@ create schema if not exists public;
create table if not exists test
(
id uuid not null
id serial not null
constraint test_pk
primary key,
name text
@@ -16,24 +16,24 @@ create table if not exists test
create table if not exists test2
(
id uuid not null,
id serial not null,
title text,
test_id uuid
test_id integer
constraint test2_test_id_fk
references test
);
INSERT INTO test (id, name) VALUES ('1e5f5d41-6d14-4007-897b-0ed2616bec96', 'plop') ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES ('1e5f5d41-6d14-4007-897b-0ed2616bec96', 'plop', '1e5f5d41-6d14-4007-897b-0ed2616bec96') ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES ('829b1a29-5db8-47f9-9562-961c561ac528', 'plip', '1e5f5d41-6d14-4007-897b-0ed2616bec96') ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES ('457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'ttt', null) ON CONFLICT DO NOTHING;
INSERT INTO test (id, name) VALUES (1, 'plop') ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES (1, 'plop', 1) ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES (2, 'plip', 1) ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES (3, 'ttt', null) ON CONFLICT DO NOTHING;
CREATE OR REPLACE FUNCTION test_function (name text default 'plop', IN hi text default 'hello', out result json)
LANGUAGE plpgsql
AS
$$
BEGIN
result = json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name);
result = json_build_object('id', 3, 'name', name);
END;
$$;
@@ -44,8 +44,8 @@ AS
$$
BEGIN
result = json_build_array(
json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name),
json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', hi)
json_build_object('id', 3, 'name', name),
json_build_object('id', 4, 'name', hi)
);
END;
$$;
@@ -56,8 +56,8 @@ AS
$$
BEGIN
SELECT json_build_array(
json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name::text),
json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', name::text || '-2')
json_build_object('id', 3, 'name', name::text),
json_build_object('id', 4, 'name', name::text || '-2')
),
10
INTO result, total
@@ -70,7 +70,7 @@ CREATE OR REPLACE FUNCTION test_function_object (inout resource json)
AS
$$
BEGIN
resource = json_build_object('id', '1e5f5d41-6d14-4007-897b-0ed2616bec96', 'name', 'changedName');
resource = json_build_object('id', 1, 'name', 'changedName');
END;
$$;

View File

@@ -3,6 +3,6 @@ LANGUAGE plpgsql
AS
$$
BEGIN
result = json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name);
result = json_build_object('id', 3, 'name', name);
END;
$$

View File

@@ -3,6 +3,6 @@ LANGUAGE plpgsql
AS
$$
BEGIN
resource = json_build_object('id', '1e5f5d41-6d14-4007-897b-0ed2616bec96', 'name', 'changedName');
resource = json_build_object('id', 1, 'name', 'changedName');
END;
$$

View File

@@ -4,8 +4,8 @@ AS
$$
BEGIN
result = json_build_array(
json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name),
json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', hi)
json_build_object('id', 3, 'name', name),
json_build_object('id', 4, 'name', hi)
);
END;
$$

View File

@@ -4,8 +4,8 @@ AS
$$
BEGIN
SELECT json_build_array(
json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name::text),
json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', name::text || '-2')
json_build_object('id', 3, 'name', name::text),
json_build_object('id', 4, 'name', name::text || '-2')
),
10
INTO result, total

View File

@@ -3,6 +3,6 @@ CREATE OR REPLACE FUNCTION test_function_duplicate (name text default 'plop') re
AS
$$
BEGIN
return json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name);
return json_build_object('id', 3, 'name', name);
END;
$$

View File

@@ -1 +1 @@
select json_build_object('id', '829b1a29-5db8-47f9-9562-961c561ac528', 'name', 'test');
select json_build_object('id', 2, 'name', 'test');

View File

@@ -1 +1 @@
select json_build_object('id', '829b1a29-5db8-47f9-9562-961c561ac528', 'name', :name::text), 'plop'::text as other;
select json_build_object('id', 2, 'name', :name::text), 'plop'::text as other;

View File

@@ -1,5 +1,5 @@
SELECT json_build_array(
json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', :name::text),
json_build_object('id', '6085c12e-e94d-4ae1-b7ad-23acc7a82a98', 'name', :name::text || '-2')
json_build_object('id', 3, 'name', :name::text),
json_build_object('id', 4, 'name', :name::text || '-2')
), 10 as total
LIMIT :limit OFFSET :offset