toInteger() silently clamps out-of-range values to INT64_MAX/MIN instead of raising an error
- Dominant language
- C
- Stars
- 4.8k
- Forks
- 523
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 9
Description
**AGE Version:** apache/age master @ cfd3b634 (2026-08-14), extension 1.8.0, on PostgreSQL 18.6
**Installation Method:** Docker
**API:** Cypher
### Steps to reproduce
1. On a fresh graph, run:
```sql
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:
```sql
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.
Contributor guide
Research direction
Start at agtype_in_scalar in src/backend/utils/adt/agtype.c:1076 and follow how agtype_to_int8 handles the parsed value. Compare this path with PostgreSQL's int8in() behavior, then run the SQL reproduction for out-of-range strings and numeric literals. Done means overflow no longer silently clamps or loses precision, and the documented error or null behavior is observed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, postgresql
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100