Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions async_postgres/pg_client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1188,8 +1188,6 @@ proc queryRowOpt*(
## Execute a query and return the first row, or `none` if no rows.
let qr = await conn.query(sql, params, resultFormat = resultFormat, timeout = timeout)
if qr.rowCount > 0:
if qr.fields.len > 0 and qr.data.fields.len == 0:
qr.data.fields = qr.fields
return some(initRow(qr.data, 0))
else:
return none(Row)
Expand All @@ -1202,11 +1200,11 @@ proc queryRow*(
timeout: Duration = ZeroDuration,
): Future[Row] {.async.} =
## Execute a query and return the first row.
## Raises `PgError` if no rows are returned.
## Raises `PgNoRowsError` if no rows are returned.
let row =
await conn.queryRowOpt(sql, params, resultFormat = resultFormat, timeout = timeout)
if row.isNone:
raise newException(PgError, "Query returned no rows")
raise newException(PgNoRowsError, "Query returned no rows")
return row.get

proc queryValue*(
Expand All @@ -1216,13 +1214,13 @@ proc queryValue*(
timeout: Duration = ZeroDuration,
): Future[string] {.async.} =
## Execute a query and return the first column of the first row as a string.
## Raises PgError if no rows are returned or the value is NULL.
## Raises `PgNoRowsError` if no rows are returned, or `PgNullError` if the value is NULL.
let qr = await conn.query(sql, params, timeout = timeout)
if qr.rowCount == 0:
raise newException(PgError, "Query returned no rows")
raise newException(PgNoRowsError, "Query returned no rows")
let row = initRow(qr.data, 0)
if row.isNull(0):
raise newException(PgError, "Query returned NULL")
raise newException(PgNullError, "Query returned NULL")
return row.getStr(0)

proc queryValue*[T](
Expand All @@ -1233,14 +1231,14 @@ proc queryValue*[T](
timeout: Duration = ZeroDuration,
): Future[T] {.async.} =
## Execute a query and return the first column of the first row as `T`.
## Raises PgError if no rows are returned or the value is NULL.
## Raises `PgNoRowsError` if no rows are returned, or `PgNullError` if the value is NULL.
## Supported types: int32, int64, float64, bool, string.
let qr = await conn.query(sql, params, timeout = timeout)
if qr.rowCount == 0:
raise newException(PgError, "Query returned no rows")
raise newException(PgNoRowsError, "Query returned no rows")
let row = initRow(qr.data, 0)
if row.isNull(0):
raise newException(PgError, "Query returned NULL")
raise newException(PgNullError, "Query returned NULL")
return row.get(0, T)

proc queryValueOpt*(
Expand Down Expand Up @@ -1349,12 +1347,12 @@ proc queryColumn*(
timeout: Duration = ZeroDuration,
): Future[seq[string]] {.async.} =
## Execute a query and return the first column of all rows as strings.
## Raises PgTypeError if any value is NULL.
## Raises `PgNullError` if any value is NULL.
let qr = await conn.query(sql, params, timeout = timeout)
for i in 0 ..< qr.rowCount:
let row = initRow(qr.data, i)
if row.isNull(0):
raise newException(PgTypeError, "NULL value in column")
raise newException(PgNullError, "NULL value in column")
result.add(row.getStr(0))

proc prepareImpl(
Expand Down
7 changes: 7 additions & 0 deletions async_postgres/pg_errors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ type
PgTypeError* = object of PgError
## Raised when a PostgreSQL value cannot be converted to the requested Nim type.

PgNoRowsError* = object of PgError
## Raised by single-row/single-value queries when the result set is empty.

PgNullError* = object of PgError
## Raised by single-value queries when the value is SQL NULL and the
## caller requested a non-nullable result.

PgConnectionError* = object of PgError
## Connection failures, disconnections, SSL/auth errors.

Expand Down
22 changes: 10 additions & 12 deletions async_postgres/pg_pool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -745,8 +745,6 @@ proc queryRowOpt*(
## Execute a query and return the first row, or `none` if no rows.
let qr = await pool.query(sql, params, resultFormat, timeout)
if qr.rowCount > 0:
if qr.fields.len > 0 and qr.data.fields.len == 0:
qr.data.fields = qr.fields
return some(initRow(qr.data, 0))
return none(Row)

Expand All @@ -758,11 +756,11 @@ proc queryRow*(
timeout: Duration = ZeroDuration,
): Future[Row] {.async.} =
## Execute a query and return the first row.
## Raises `PgError` if no rows are returned.
## Raises `PgNoRowsError` if no rows are returned.
let row =
await pool.queryRowOpt(sql, params, resultFormat = resultFormat, timeout = timeout)
if row.isNone:
raise newException(PgError, "Query returned no rows")
raise newException(PgNoRowsError, "Query returned no rows")
return row.get

proc queryValue*(
Expand All @@ -772,13 +770,13 @@ proc queryValue*(
timeout: Duration = ZeroDuration,
): Future[string] {.async.} =
## Execute a query and return the first column of the first row as a string.
## Raises `PgError` if no rows or the value is NULL.
## Raises `PgNoRowsError` if no rows are returned, or `PgNullError` if the value is NULL.
let qr = await pool.query(sql, params, timeout = timeout)
if qr.rowCount == 0:
raise newException(PgError, "Query returned no rows")
raise newException(PgNoRowsError, "Query returned no rows")
let row = initRow(qr.data, 0)
if row.isNull(0):
raise newException(PgError, "Query returned NULL")
raise newException(PgNullError, "Query returned NULL")
return row.getStr(0)

proc queryValue*[T](
Expand All @@ -789,13 +787,13 @@ proc queryValue*[T](
timeout: Duration = ZeroDuration,
): Future[T] {.async.} =
## Execute a query and return the first column of the first row as `T`.
## Raises `PgError` if no rows or the value is NULL.
## Raises `PgNoRowsError` if no rows are returned, or `PgNullError` if the value is NULL.
let qr = await pool.query(sql, params, timeout = timeout)
if qr.rowCount == 0:
raise newException(PgError, "Query returned no rows")
raise newException(PgNoRowsError, "Query returned no rows")
let row = initRow(qr.data, 0)
if row.isNull(0):
raise newException(PgError, "Query returned NULL")
raise newException(PgNullError, "Query returned NULL")
return row.get(0, T)

proc queryValueOpt*(
Expand Down Expand Up @@ -901,12 +899,12 @@ proc queryColumn*(
timeout: Duration = ZeroDuration,
): Future[seq[string]] {.async.} =
## Execute a query and return the first column of all rows as strings.
## Raises PgTypeError if any value is NULL.
## Raises `PgNullError` if any value is NULL.
let qr = await pool.query(sql, params, timeout = timeout)
for i in 0 ..< qr.rowCount:
let row = initRow(qr.data, i)
if row.isNull(0):
raise newException(PgTypeError, "NULL value in column")
raise newException(PgNullError, "NULL value in column")
result.add(row.getStr(0))

proc simpleQuery*(pool: PgPool, sql: string): Future[seq[QueryResult]] {.async.} =
Expand Down
66 changes: 59 additions & 7 deletions tests/test_e2e.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4952,7 +4952,7 @@ suite "E2E: Convenience Query Methods":
var raised = false
try:
discard await conn.queryRow("SELECT 1 WHERE false")
except PgError:
except PgNoRowsError:
raised = true
doAssert raised
await conn.close()
Expand All @@ -4974,7 +4974,7 @@ suite "E2E: Convenience Query Methods":
var raised = false
try:
discard await conn.queryValue("SELECT 1 WHERE false")
except PgError:
except PgNoRowsError:
raised = true
doAssert raised
await conn.close()
Expand All @@ -4987,7 +4987,7 @@ suite "E2E: Convenience Query Methods":
var raised = false
try:
discard await conn.queryValue("SELECT NULL::text")
except PgError:
except PgNullError:
raised = true
doAssert raised
await conn.close()
Expand Down Expand Up @@ -5043,7 +5043,7 @@ suite "E2E: Convenience Query Methods":
var raised = false
try:
discard await conn.queryValue(int32, "SELECT 1 WHERE false")
except PgError:
except PgNoRowsError:
raised = true
doAssert raised
await conn.close()
Expand All @@ -5056,7 +5056,7 @@ suite "E2E: Convenience Query Methods":
var raised = false
try:
discard await conn.queryValue(int64, "SELECT NULL::int8")
except PgError:
except PgNullError:
raised = true
doAssert raised
await conn.close()
Expand Down Expand Up @@ -5184,7 +5184,7 @@ suite "E2E: Convenience Query Methods":
var raised = false
try:
discard await conn.queryColumn("SELECT NULL::text")
except PgTypeError:
except PgNullError:
raised = true
doAssert raised
await conn.close()
Expand Down Expand Up @@ -5271,7 +5271,7 @@ suite "E2E: Convenience Query Methods":
var raised = false
try:
discard await pool.queryRow("SELECT 1 WHERE false")
except PgError:
except PgNoRowsError:
raised = true
doAssert raised
await pool.close()
Expand All @@ -5287,6 +5287,32 @@ suite "E2E: Convenience Query Methods":

waitFor t()

test "pool queryValue raises on no rows":
proc t() {.async.} =
let pool = await newPool(initPoolConfig(plainConfig(), minSize = 1, maxSize = 2))
var raised = false
try:
discard await pool.queryValue("SELECT 1 WHERE false")
except PgNoRowsError:
raised = true
doAssert raised
await pool.close()

waitFor t()

test "pool queryValue raises on NULL":
proc t() {.async.} =
let pool = await newPool(initPoolConfig(plainConfig(), minSize = 1, maxSize = 2))
var raised = false
try:
discard await pool.queryValue("SELECT NULL::text")
except PgNullError:
raised = true
doAssert raised
await pool.close()

waitFor t()

test "pool queryExists":
proc t() {.async.} =
let pool = await newPool(initPoolConfig(plainConfig(), minSize = 1, maxSize = 2))
Expand Down Expand Up @@ -5336,6 +5362,32 @@ suite "E2E: Convenience Query Methods":

waitFor t()

test "pool queryValue with typedesc raises on no rows":
proc t() {.async.} =
let pool = await newPool(initPoolConfig(plainConfig(), minSize = 1, maxSize = 2))
var raised = false
try:
discard await pool.queryValue(int32, "SELECT 1 WHERE false")
except PgNoRowsError:
raised = true
doAssert raised
await pool.close()

waitFor t()

test "pool queryValue with typedesc raises on NULL":
proc t() {.async.} =
let pool = await newPool(initPoolConfig(plainConfig(), minSize = 1, maxSize = 2))
var raised = false
try:
discard await pool.queryValue(int64, "SELECT NULL::int8")
except PgNullError:
raised = true
doAssert raised
await pool.close()

waitFor t()

test "pool queryValueOrDefault with typedesc":
proc t() {.async.} =
let pool = await newPool(initPoolConfig(plainConfig(), minSize = 1, maxSize = 2))
Expand Down