diff --git a/async_postgres/pg_client.nim b/async_postgres/pg_client.nim index cabb0d7..3faa704 100644 --- a/async_postgres/pg_client.nim +++ b/async_postgres/pg_client.nim @@ -1313,6 +1313,25 @@ proc queryValueOrDefault*[T]( return default return row.get(0, T) +proc queryValueOrDefault*[T]( + conn: PgConnection, + sql: string, + params: seq[PgParam] = @[], + default: T, + timeout: Duration = ZeroDuration, +): Future[T] {.async.} = + ## Execute a query and return the first column of the first row as `T`, + ## inferring `T` from `default`. + ## Returns `default` if no rows or the value is NULL. + ## Supported types: int32, int64, float64, bool, string. + let qr = await conn.query(sql, params, timeout = timeout) + if qr.rowCount == 0: + return default + let row = initRow(qr.data, 0) + if row.isNull(0): + return default + return row.get(0, T) + proc queryExists*( conn: PgConnection, sql: string, diff --git a/async_postgres/pg_pool.nim b/async_postgres/pg_pool.nim index 512585b..bbd6a7d 100644 --- a/async_postgres/pg_pool.nim +++ b/async_postgres/pg_pool.nim @@ -866,6 +866,24 @@ proc queryValueOrDefault*[T]( return default return row.get(0, T) +proc queryValueOrDefault*[T]( + pool: PgPool, + sql: string, + params: seq[PgParam] = @[], + default: T, + timeout: Duration = ZeroDuration, +): Future[T] {.async.} = + ## Execute a query and return the first column of the first row as `T`, + ## inferring `T` from `default`. + ## Returns `default` if no rows or the value is NULL. + let qr = await pool.query(sql, params, timeout = timeout) + if qr.rowCount == 0: + return default + let row = initRow(qr.data, 0) + if row.isNull(0): + return default + return row.get(0, T) + proc queryExists*( pool: PgPool, sql: string, diff --git a/async_postgres/pg_pool_cluster.nim b/async_postgres/pg_pool_cluster.nim index c8a5555..ad7c071 100644 --- a/async_postgres/pg_pool_cluster.nim +++ b/async_postgres/pg_pool_cluster.nim @@ -292,6 +292,14 @@ clusterForwards("read"): timeout: Duration = ZeroDuration, ): Future[T] + proc readQueryValueOrDefault*[T]( + cluster: PgPoolCluster, + sql: string, + params: seq[PgParam] = @[], + default: T, + timeout: Duration = ZeroDuration, + ): Future[T] + proc readQueryExists*( cluster: PgPoolCluster, sql: string, @@ -402,6 +410,14 @@ clusterForwards("write"): timeout: Duration = ZeroDuration, ): Future[T] + proc writeQueryValueOrDefault*[T]( + cluster: PgPoolCluster, + sql: string, + params: seq[PgParam] = @[], + default: T, + timeout: Duration = ZeroDuration, + ): Future[T] + proc writeQueryExists*( cluster: PgPoolCluster, sql: string, diff --git a/async_postgres/pg_sql.nim b/async_postgres/pg_sql.nim index 6438fec..69d84d5 100644 --- a/async_postgres/pg_sql.nim +++ b/async_postgres/pg_sql.nim @@ -353,6 +353,10 @@ sqlQueryForwards: timeout: Duration = ZeroDuration, ): untyped + proc queryValueOrDefault*[T]( + conn: PgConnection, sq: SqlQuery, default: T, timeout: Duration = ZeroDuration + ): untyped + proc queryExists*( conn: PgConnection, sq: SqlQuery, timeout: Duration = ZeroDuration ): untyped @@ -453,6 +457,10 @@ sqlQueryForwards: timeout: Duration = ZeroDuration, ): untyped + proc queryValueOrDefault*[T]( + pool: PgPool, sq: SqlQuery, default: T, timeout: Duration = ZeroDuration + ): untyped + proc queryExists*( pool: PgPool, sq: SqlQuery, timeout: Duration = ZeroDuration ): untyped @@ -533,6 +541,10 @@ sqlQueryForwards: timeout: Duration = ZeroDuration, ): untyped + proc readQueryValueOrDefault*[T]( + cluster: PgPoolCluster, sq: SqlQuery, default: T, timeout: Duration = ZeroDuration + ): untyped + proc readQueryExists*( cluster: PgPoolCluster, sq: SqlQuery, timeout: Duration = ZeroDuration ): untyped @@ -614,6 +626,10 @@ sqlQueryForwards: timeout: Duration = ZeroDuration, ): untyped + proc writeQueryValueOrDefault*[T]( + cluster: PgPoolCluster, sq: SqlQuery, default: T, timeout: Duration = ZeroDuration + ): untyped + proc writeQueryExists*( cluster: PgPoolCluster, sq: SqlQuery, timeout: Duration = ZeroDuration ): untyped diff --git a/tests/test_e2e.nim b/tests/test_e2e.nim index afb8855..13a1c65 100644 --- a/tests/test_e2e.nim +++ b/tests/test_e2e.nim @@ -5075,6 +5075,17 @@ suite "E2E: Convenience Query Methods": waitFor t() + test "queryValueOrDefault infers type from default": + proc t() {.async.} = + let conn = await connect(plainConfig()) + let val = await conn.queryValueOrDefault("SELECT 1 WHERE false", default = -1'i64) + doAssert val == -1'i64 + let val2 = await conn.queryValueOrDefault("SELECT 42", default = 0'i32) + doAssert val2 == 42'i32 + await conn.close() + + waitFor t() + test "queryValueOpt returns some on value": proc t() {.async.} = let conn = await connect(plainConfig()) @@ -5337,6 +5348,17 @@ suite "E2E: Convenience Query Methods": waitFor t() + test "pool queryValueOrDefault infers type from default": + proc t() {.async.} = + let pool = await newPool(initPoolConfig(plainConfig(), minSize = 1, maxSize = 2)) + let val = await pool.queryValueOrDefault("SELECT 1 WHERE false", default = -1'i32) + doAssert val == -1'i32 + let val2 = await pool.queryValueOrDefault("SELECT 7", default = 0'i32) + doAssert val2 == 7'i32 + await pool.close() + + waitFor t() + test "pool queryValueOpt": proc t() {.async.} = let pool = await newPool(initPoolConfig(plainConfig(), minSize = 1, maxSize = 2))