AGE Version: apache/age master @ cfd3b63 (2026-08-14), extension 1.8.0, on PostgreSQL 18.6
Installation Method: Docker
API: Cypher
Steps to reproduce
- On a fresh graph, run:
LOAD 'age';
SET search_path = ag_catalog, public;
SELECT * FROM create_graph('test_overflow');
SELECT * FROM cypher('test_overflow', $$ RETURN toInteger('9223372036854775808') $$) AS (r agtype);
r
---------------------
9223372036854775807 -- INT64_MAX, silently clamped
toInteger('-9223372036854775809') returns -9223372036854775808 (INT64_MIN), and arbitrarily large inputs ('99999999999999999999999999') clamp the same way.
Related form (also verified): a numeric literal that overflows int64 is silently converted to a float with loss of precision — RETURN 9223372036854775808 yields 9.223372036854776e+18 (and -9223372036854775809 yields the negative float) instead of erroring. No error or warning in either case.
Expected behavior
Out-of-range input should raise an error or return null. The host database's own integer cast rejects the same value:
SELECT '9223372036854775808'::int8;
-- ERROR: value "9223372036854775808" is out of range for type bigint
Actual behavior
The value is silently clamped to the nearest 64-bit bound and returned as a normal result — silent data corruption (e.g. sums, comparisons and property writes then operate on the wrong number).
Root cause: the string argument is re-parsed as an agtype scalar by agtype_in_scalar (src/backend/utils/adt/agtype.c:1076), which calls PostgreSQL's pg_strtoint64() without checking errno. pg_strtoint64() saturates out-of-range input to INT64_MAX/INT64_MIN; PostgreSQL's own int8in() makes the same call but checks errno and raises "out of range" (which is why ::int8 rejects the same string). agtype_to_int8 then returns the already-clamped value unchanged.
AGE Version: apache/age master @ cfd3b63 (2026-08-14), extension 1.8.0, on PostgreSQL 18.6
Installation Method: Docker
API: Cypher
Steps to reproduce
toInteger('-9223372036854775809')returns-9223372036854775808(INT64_MIN), and arbitrarily large inputs ('99999999999999999999999999') clamp the same way.Related form (also verified): a numeric literal that overflows int64 is silently converted to a float with loss of precision —
RETURN 9223372036854775808yields9.223372036854776e+18(and-9223372036854775809yields the negative float) instead of erroring. No error or warning in either case.Expected behavior
Out-of-range input should raise an error or return null. The host database's own integer cast rejects the same value:
Actual behavior
The value is silently clamped to the nearest 64-bit bound and returned as a normal result — silent data corruption (e.g. sums, comparisons and property writes then operate on the wrong number).
Root cause: the string argument is re-parsed as an agtype scalar by
agtype_in_scalar(src/backend/utils/adt/agtype.c:1076), which calls PostgreSQL'spg_strtoint64()without checkingerrno.pg_strtoint64()saturates out-of-range input toINT64_MAX/INT64_MIN; PostgreSQL's ownint8in()makes the same call but checkserrnoand raises "out of range" (which is why::int8rejects the same string).agtype_to_int8then returns the already-clamped value unchanged.