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
61 changes: 61 additions & 0 deletions async_postgres/pg_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1746,6 +1746,24 @@ proc getInt*(row: Row, col: int): int32 =
raise newException(PgTypeError, "Column " & $col & ": invalid integer value")
result = int32(v)

proc getInt16*(row: Row, col: int): int16 =
## Get a column value as int16. Handles binary int2 directly. Raises `PgTypeError` on NULL.
let (off, clen) = cellInfo(row, col)
if clen == -1:
raise newException(PgTypeError, "Column " & $col & " is NULL")
if row.isBinaryCol(col):
if clen == 2:
let b = row.data.buf
return int16((uint16(b[off]) shl 8) or uint16(b[off + 1]))
raise newException(
PgTypeError,
"Column " & $col & ": unexpected binary length " & $clen & " for int16",
)
var v: int
if parseInt(row.bufView(off, clen), v) == 0:
raise newException(PgTypeError, "Column " & $col & ": invalid int16 value")
result = int16(v)

proc getInt64*(row: Row, col: int): int64 =
## Get a column value as int64. Handles binary int2/4/8 directly. Raises `PgTypeError` on NULL.
let (off, clen) = cellInfo(row, col)
Expand Down Expand Up @@ -1803,6 +1821,25 @@ proc getFloat*(row: Row, col: int): float64 =
return float64(f32)
discard parseFloat(row.bufView(off, clen), result)

proc getFloat32*(row: Row, col: int): float32 =
## Get a column value as float32. Handles binary float4 directly. Raises `PgTypeError` on NULL.
let (off, clen) = cellInfo(row, col)
if clen == -1:
raise newException(PgTypeError, "Column " & $col & " is NULL")
if row.isBinaryCol(col):
if clen == 4:
var bits: uint32
let b = row.data.buf
bits =
(uint32(b[off]) shl 24) or (uint32(b[off + 1]) shl 16) or
(uint32(b[off + 2]) shl 8) or uint32(b[off + 3])
copyMem(addr result, addr bits, 4)
return
var f: float64
if parseFloat(row.bufView(off, clen), f) == 0:
raise newException(PgTypeError, "Column " & $col & ": invalid float32 value")
result = float32(f)

proc getNumeric*(row: Row, col: int): PgNumeric =
## Get a column value as PgNumeric. Handles binary numeric format.
if row.isBinaryCol(col):
Expand Down Expand Up @@ -2582,12 +2619,17 @@ template nameAccessor(getProc: untyped, T: typedesc) =

optAccessor(getStr, getStrOpt, string)
optAccessor(getInt, getIntOpt, int32)
optAccessor(getInt16, getInt16Opt, int16)
optAccessor(getInt64, getInt64Opt, int64)
optAccessor(getFloat, getFloatOpt, float64)
optAccessor(getFloat32, getFloat32Opt, float32)
optAccessor(getNumeric, getNumericOpt, PgNumeric)
optAccessor(getUuid, getUuidOpt, PgUuid)
optAccessor(getBool, getBoolOpt, bool)
optAccessor(getBytes, getBytesOpt, seq[byte])
optAccessor(getJson, getJsonOpt, JsonNode)
optAccessor(getTimestamp, getTimestampOpt, DateTime)
optAccessor(getDate, getDateOpt, DateTime)
optAccessor(getInterval, getIntervalOpt, PgInterval)
optAccessor(getInet, getInetOpt, PgInet)
optAccessor(getCidr, getCidrOpt, PgCidr)
Expand Down Expand Up @@ -4521,13 +4563,20 @@ optAccessor(getDateRangeArray, getDateRangeArrayOpt, seq[PgRange[DateTime]])
# timestamptz, date) making a single `get` overload ambiguous. Use the
# explicit getters (getTimestamp, getTimestampTz, getDate, etc.) instead.

proc get*(row: Row, col: int, T: typedesc[int16]): int16 =
## Generic typed accessor. Usage: ``row.get(0, int16)``
row.getInt16(col)

proc get*(row: Row, col: int, T: typedesc[int32]): int32 =
## Generic typed accessor. Usage: ``row.get(0, int32)``
row.getInt(col)

proc get*(row: Row, col: int, T: typedesc[int64]): int64 =
row.getInt64(col)

proc get*(row: Row, col: int, T: typedesc[float32]): float32 =
row.getFloat32(col)

proc get*(row: Row, col: int, T: typedesc[float64]): float64 =
row.getFloat(col)

Expand Down Expand Up @@ -4570,6 +4619,15 @@ proc get*(row: Row, col: int, T: typedesc[PgTsQuery]): PgTsQuery =
proc get*(row: Row, col: int, T: typedesc[PgXml]): PgXml =
row.getXml(col)

proc get*(row: Row, col: int, T: typedesc[PgBit]): PgBit =
row.getBit(col)

proc get*(row: Row, col: int, T: typedesc[PgHstore]): PgHstore =
row.getHstore(col)

proc get*(row: Row, col: int, T: typedesc[PgUuid]): PgUuid =
row.getUuid(col)

proc get*(row: Row, col: int, T: typedesc[PgPoint]): PgPoint =
row.getPoint(col)

Expand Down Expand Up @@ -4614,6 +4672,9 @@ proc get*(row: Row, col: int, T: typedesc[seq[bool]]): seq[bool] =
proc get*(row: Row, col: int, T: typedesc[seq[string]]): seq[string] =
row.getStrArray(col)

proc get*(row: Row, col: int, T: typedesc[seq[PgBit]]): seq[PgBit] =
row.getBitArray(col)

# Range types (DateTime-based ranges excluded — see note above)

proc get*(row: Row, col: int, T: typedesc[PgRange[int32]]): PgRange[int32] =
Expand Down
40 changes: 40 additions & 0 deletions tests/test_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,19 @@ suite "Row accessors":
expect IndexDefect:
discard row.getStr(-1)

test "getInt16":
let row = @[some(toBytes("123"))]
check row.getInt16(0) == 123'i16

test "getInt16 negative":
let row = @[some(toBytes("-456"))]
check row.getInt16(0) == -456'i16

test "getInt16 invalid value raises":
let row = @[some(toBytes("abc"))]
expect PgTypeError:
discard row.getInt16(0)

test "getInt":
let row = @[some(toBytes("42"))]
check row.getInt(0) == 42'i32
Expand All @@ -313,6 +326,15 @@ suite "Row accessors":
expect PgTypeError:
discard row.getInt64(0)

test "getFloat32":
let row = @[some(toBytes("2.5"))]
check abs(row.getFloat32(0) - 2.5'f32) < 1e-6

test "getFloat32 invalid value raises":
let row = @[some(toBytes("notanumber"))]
expect PgTypeError:
discard row.getFloat32(0)

test "getFloat":
let row = @[some(toBytes("3.14"))]
check abs(row.getFloat(0) - 3.14) < 1e-10
Expand Down Expand Up @@ -709,6 +731,14 @@ suite "Option accessors":
let row: Row = @[none(seq[byte])]
check row.getStrOpt(0) == none(string)

test "getInt16Opt some":
let row: Row = @[some(toBytes("123"))]
check row.getInt16Opt(0) == some(123'i16)

test "getInt16Opt none":
let row: Row = @[none(seq[byte])]
check row.getInt16Opt(0) == none(int16)

test "getIntOpt some":
let row: Row = @[some(toBytes("42"))]
check row.getIntOpt(0) == some(42'i32)
Expand All @@ -725,6 +755,16 @@ suite "Option accessors":
let row: Row = @[none(seq[byte])]
check row.getInt64Opt(0) == none(int64)

test "getFloat32Opt some":
let row: Row = @[some(toBytes("2.5"))]
let v = row.getFloat32Opt(0)
check v.isSome
check abs(v.get - 2.5'f32) < 1e-6

test "getFloat32Opt none":
let row: Row = @[none(seq[byte])]
check row.getFloat32Opt(0) == none(float32)

test "getFloatOpt some":
let row: Row = @[some(toBytes("3.14"))]
let v = row.getFloatOpt(0)
Expand Down
Loading