Skip to content

Commit

Permalink
Correct boundaries check for int; raise OverflowError for such cases
Browse files Browse the repository at this point in the history
Since there is no error in a value representation and PyLong_AsLong(-Long)
raises exactly that exception if the value exceed the destination range, it
should be OverflowError instead of ValueError. Also make error message be
similar to one from the original OverflowError raised by PyLong_AsLong.
  • Loading branch information
vitaly-burovoy authored and elprans committed May 24, 2017
1 parent e082910 commit dfcf135
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 9 deletions.
48 changes: 40 additions & 8 deletions asyncpg/protocol/codecs/int.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,17 @@ cdef bool_decode(ConnectionSettings settings, FastReadBuffer buf):


cdef int2_encode(ConnectionSettings settings, WriteBuffer buf, obj):
cdef long val = cpython.PyLong_AsLong(obj)
if val < -32768 or val > 32767:
raise ValueError(
'integer too large to be encoded as INT2: {!r}'.format(val))
cdef int overflow = 0
cdef long val

try:
val = cpython.PyLong_AsLong(obj)
except OverflowError:
overflow = 1

if overflow or val < -32768 or val > 32767:
raise OverflowError(
'int too big to be encoded as INT2: {!r}'.format(obj))

buf.write_int32(2)
buf.write_int16(<int16_t>val)
Expand All @@ -33,20 +40,45 @@ cdef int2_decode(ConnectionSettings settings, FastReadBuffer buf):


cdef int4_encode(ConnectionSettings settings, WriteBuffer buf, obj):
cdef int32_t val = <int32_t>cpython.PyLong_AsLong(obj)
cdef int overflow = 0
cdef long val

try:
val = cpython.PyLong_AsLong(obj)
except OverflowError:
overflow = 1

# "long" and "long long" have the same size for x86_64, need an extra check
if overflow or (sizeof(val) > 4 and (val < -2147483648 or
val > 2147483647)):
raise OverflowError(
'int too big to be encoded as INT4: {!r}'.format(obj))

buf.write_int32(4)
buf.write_int32(val)
buf.write_int32(<int32_t>val)


cdef int4_decode(ConnectionSettings settings, FastReadBuffer buf):
return cpython.PyLong_FromLong(hton.unpack_int32(buf.read(4)))


cdef int8_encode(ConnectionSettings settings, WriteBuffer buf, obj):
cdef int64_t val = cpython.PyLong_AsLongLong(obj)
cdef int overflow = 0
cdef long long val

try:
val = cpython.PyLong_AsLongLong(obj)
except OverflowError:
overflow = 1

# Just in case for systems with "long long" bigger than 8 bytes
if overflow or (sizeof(val) > 8 and (val < -9223372036854775808 or
val > 9223372036854775807)):
raise OverflowError(
'int too big to be encoded as INT8: {!r}'.format(obj))

buf.write_int32(8)
buf.write_int64(val)
buf.write_int64(<int64_t>val)


cdef int8_decode(ConnectionSettings settings, FastReadBuffer buf):
Expand Down
20 changes: 19 additions & 1 deletion tests/test_codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,11 @@ async def test_invalid_input(self):
'2',
'aa',
]),
('smallint', ValueError, 'integer too large', [
('smallint', OverflowError, 'int too big to be encoded as INT2', [
2**256, # check for the same exception for any big numbers
decimal.Decimal("2000000000000000000000000000000"),
0xffff,
0xffffffff,
32768,
-32769
]),
Expand All @@ -484,10 +488,24 @@ async def test_invalid_input(self):
'2',
'aa',
]),
('int', OverflowError, 'int too big to be encoded as INT4', [
2**256, # check for the same exception for any big numbers
decimal.Decimal("2000000000000000000000000000000"),
0xffffffff,
2**31,
-2**31 - 1,
]),
('int8', TypeError, 'an integer is required', [
'2',
'aa',
]),
('bigint', OverflowError, 'int too big to be encoded as INT8', [
2**256, # check for the same exception for any big numbers
decimal.Decimal("2000000000000000000000000000000"),
0xffffffffffffffff,
2**63,
-2**63 - 1,
]),
('text', TypeError, 'expected str, got bytes', [
b'foo'
]),
Expand Down

0 comments on commit dfcf135

Please sign in to comment.