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
32 changes: 28 additions & 4 deletions async_postgres/pg_types/accessors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ proc getInt*(row: Row, col: int): int32 =
var v: int
if parseInt(row.bufView(off, clen), v) == 0:
raise newException(PgTypeError, "Column " & $col & ": invalid integer value")
if v < int(int32.low) or v > int(int32.high):
raise newException(
PgTypeError, "Column " & $col & ": integer value out of int32 range: " & $v
)
result = int32(v)

proc getInt16*(row: Row, col: int): int16 =
Expand All @@ -242,6 +246,10 @@ proc getInt16*(row: Row, col: int): int16 =
var v: int
if parseInt(row.bufView(off, clen), v) == 0:
raise newException(PgTypeError, "Column " & $col & ": invalid int16 value")
if v < int(int16.low) or v > int(int16.high):
raise newException(
PgTypeError, "Column " & $col & ": integer value out of int16 range: " & $v
)
result = int16(v)

proc getInt64*(row: Row, col: int): int64 =
Expand Down Expand Up @@ -899,6 +907,22 @@ optAccessor(getPath, getPathOpt, PgPath)
optAccessor(getPolygon, getPolygonOpt, PgPolygon)
optAccessor(getCircle, getCircleOpt, PgCircle)

proc parseInt32Elem(s: string): int32 =
## Parse a text array element into int32, rejecting out-of-range values.
## Plain ``int32(parseInt)`` would silently truncate (wrap) in release builds.
let v = parseInt(s)
if v < int(int32.low) or v > int(int32.high):
raise newException(PgTypeError, "integer array element out of int32 range: " & $v)
int32(v)

proc parseInt16Elem(s: string): int16 =
## Parse a text array element into int16, rejecting out-of-range values.
## Plain ``int16(parseInt)`` would silently truncate (wrap) in release builds.
let v = parseInt(s)
if v < int(int16.low) or v > int(int16.high):
raise newException(PgTypeError, "integer array element out of int16 range: " & $v)
int16(v)

proc getIntArray*(row: Row, col: int): seq[int32] =
## Get a column value as a seq of int32. Handles binary array format.
if row.isBinaryCol(col):
Expand All @@ -923,7 +947,7 @@ proc getIntArray*(row: Row, col: int): seq[int32] =
for e in elems:
if e.isNone:
raise newException(PgTypeError, "NULL element in int array")
result.add(int32(parseInt(e.get)))
result.add(parseInt32Elem(e.get))

proc getInt16Array*(row: Row, col: int): seq[int16] =
## Get a column value as a seq of int16. Handles binary array format.
Expand All @@ -949,7 +973,7 @@ proc getInt16Array*(row: Row, col: int): seq[int16] =
for e in elems:
if e.isNone:
raise newException(PgTypeError, "NULL element in int16 array")
result.add(int16(parseInt(e.get)))
result.add(parseInt16Elem(e.get))

proc getInt64Array*(row: Row, col: int): seq[int64] =
## Get a column value as a seq of int64. Handles binary array format.
Expand Down Expand Up @@ -1631,7 +1655,7 @@ proc getIntArrayElemOpt*(row: Row, col: int): seq[Option[int32]] =
if e.isNone:
result.add(none(int32))
else:
result.add(some(int32(parseInt(e.get))))
result.add(some(parseInt32Elem(e.get)))

proc getInt16ArrayElemOpt*(row: Row, col: int): seq[Option[int16]] =
if row.isBinaryCol(col):
Expand All @@ -1653,7 +1677,7 @@ proc getInt16ArrayElemOpt*(row: Row, col: int): seq[Option[int16]] =
if e.isNone:
result.add(none(int16))
else:
result.add(some(int16(parseInt(e.get))))
result.add(some(parseInt16Elem(e.get)))

proc getInt64ArrayElemOpt*(row: Row, col: int): seq[Option[int64]] =
if row.isBinaryCol(col):
Expand Down
36 changes: 36 additions & 0 deletions tests/test_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@ suite "Row accessors":
expect PgTypeError:
discard row.getInt16(0)

test "getInt16 out of range raises (no silent truncation)":
let row = @[some(toBytes("32768"))]
expect PgTypeError:
discard row.getInt16(0)
let rowNeg = @[some(toBytes("-32769"))]
expect PgTypeError:
discard rowNeg.getInt16(0)

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

test "getInt out of range raises (no silent truncation)":
let row = @[some(toBytes("2147483648"))]
expect PgTypeError:
discard row.getInt(0)
let rowNeg = @[some(toBytes("-2147483649"))]
expect PgTypeError:
discard rowNeg.getInt(0)

test "getInt64":
let row = @[some(toBytes("9999999999"))]
check row.getInt64(0) == 9999999999'i64
Expand Down Expand Up @@ -1743,10 +1759,30 @@ suite "Array row accessors":
let row: Row = @[some(toBytes("{}"))]
check row.getIntArray(0).len == 0

test "getIntArray out of range raises (no silent truncation)":
let row: Row = @[some(toBytes("{1,2147483648}"))]
expect PgTypeError:
discard row.getIntArray(0)

test "getInt16Array":
let row: Row = @[some(toBytes("{10,-20}"))]
check row.getInt16Array(0) == @[10'i16, -20'i16]

test "getInt16Array out of range raises (no silent truncation)":
let row: Row = @[some(toBytes("{10,32768}"))]
expect PgTypeError:
discard row.getInt16Array(0)

test "getIntArrayElemOpt out of range raises (no silent truncation)":
let row: Row = @[some(toBytes("{1,NULL,-2147483649}"))]
expect PgTypeError:
discard row.getIntArrayElemOpt(0)

test "getInt16ArrayElemOpt out of range raises (no silent truncation)":
let row: Row = @[some(toBytes("{1,NULL,-32769}"))]
expect PgTypeError:
discard row.getInt16ArrayElemOpt(0)

test "getInt64Array":
let row: Row = @[some(toBytes("{9999999999,-1}"))]
check row.getInt64Array(0) == @[9999999999'i64, -1'i64]
Expand Down