apache / apache/age

toInteger() silently clamps out-of-range values to INT64_MAX/MIN instead of raising an error

Open
#2,564 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.