ysys143 opened a new issue, #2500:
URL: https://github.com/apache/age/issues/2500
### Summary
On `master` (`8014174`) and on the `PG18` branch (= `release/PG18/1.8.0`,
both at
`e43dc1a`), a Cypher query that
compares `id()` against a list **held in a variable** dereferences a
`graphid` as if it
were a varlena pointer and terminates the backend with `SIGSEGV`. PostgreSQL
then
recycles every other backend and enters crash recovery, so one ordinary
query takes
down the instance.
`release/PG18/1.7.0` built from source on the identical base image is
unaffected.
### Minimal reproduction
```sql
CREATE EXTENSION IF NOT EXISTS age CASCADE;
LOAD 'age';
SET search_path = ag_catalog, "$user", public;
SELECT create_graph('gtest');
SELECT * FROM cypher('gtest', $$ CREATE (:P {name: 'a'}) $$) AS (v agtype);
-- crashes on 1.8.0; returns 0 on 1.7.0
SELECT count(*) FROM cypher('gtest',
$$ WITH [1] AS ids MATCH (n) WHERE id(n) IN ids RETURN n $$) AS (n agtype);
```
The `CREATE` is required: the crash only happens once `MATCH` actually
produces a row
(see the scope table). A single vertex is enough, and the list element does
not have to
be a real id — `[1]` matches nothing and still crashes.
Server log from running exactly the snippet above against a freshly created
graph:
```
LOG: client backend (PID 141) was terminated by signal 11: Segmentation
fault
DETAIL: Failed process was running: SELECT count(*) FROM cypher('gtest',
$$ WITH [1] AS ids MATCH (n) WHERE id(n) IN ids RETURN n $$)
AS (n agtype);
LOG: terminating any other active server processes
LOG: all server processes terminated; reinitializing
```
### Backtrace
Core dump from a `-O0 -g3` build:
```
Program terminated with signal SIGSEGV, Segmentation fault.
#0 pg_detoast_datum ()
#1 agtype_in_operator (fcinfo=0x...) at src/backend/utils/adt/agtype.c:5052
#2-#6 [ExecQual / ExecScan]
#7 standard_ExecutorRun ()
#9 PortalRun ()
#11 PostgresMain ()
```
`agtype.c:5052` is the fetch of the **left-hand** operand of `IN`:
```c
5051 /* get the item to search for */
5052 agt_item = AG_GET_ARG_AGTYPE_P(1);
```
### Root cause
`agtype_in_operator` is **byte-for-byte identical** between 1.7.0 and 1.8.0
(I diffed the
function body). What changed is the type of what reaches it.
On 1.8.0 `id()` produces `graphid`, not `agtype`:
```
-- 1.8.0
SELECT i FROM cypher('gtest', $$ MATCH (n:P) RETURN id(n) $$) AS (i bigint);
ERROR: cannot cast type graphid to bigint for column "i"
-- 1.7.0: returns 844424930131969
```
(`agtype` → `bigint` still works on 1.8.0 — e.g. `count()` declared `bigint`
— so this is
specific to `id()`'s new output type. This is consistent with 1.8.0
introducing native
`vertex` / `edge` types: `pg_cast` gains `vertex→agtype` and `edge→agtype`,
and `pg_proc`
gains `vertex_to_agtype` / `edge_to_agtype`, none of which exist on 1.7.0.)
`graphid` is a **pass-by-value int64**. When the planner selects
`agtype_in_operator`
without inserting the `graphid→agtype` cast, line 5052 hands that raw
integer to
`pg_detoast_datum`, which dereferences it as a pointer.
The variable-bound list is what lets this through. With a *multi-element
literal* list,
type resolution instead fails cleanly before execution:
```
-- 1.8.0
MATCH (n) WHERE id(n) IN [1,2] RETURN n
ERROR: not a common type: 16461
```
(The OID is assigned at `CREATE EXTENSION` time, so it differs per install.
Here
`SELECT 16461::regtype` → `ag_catalog.agtype`; a second install of the same
version
reported `16462` for the same error, which likewise resolves to
`ag_catalog.agtype`.)
So the same underlying type change produces a clean error on one path and a
segfault on
the other.
### Scope
Each probe run in its own session against the same seeded graph, on both
versions built
identically. `v` denotes a variable bound by `WITH`.
| probe | query | 1.7.0 | 1.8.0 |
|---|---|---|---|
| id + IN + var-list | `WITH [1] AS v MATCH (n) WHERE id(n) IN v` | ok (0) |
**SEGV** |
| id + IN + var-list, real ids | `MATCH (x:P) WITH collect(id(x)) AS v MATCH
(n) WHERE id(n) IN v` | ok (2) | **SEGV** |
| id + IN + 1-elem literal | `MATCH (n) WHERE id(n) IN [1]` | ok (0) | ok
(0) |
| id + IN + n-elem literal | `MATCH (n) WHERE id(n) IN [1,2]` | ok (0) |
`ERROR: not a common type: 16461` |
| **prop** + IN + var-list | `WITH ['a'] AS v MATCH (n) WHERE n.name IN v` |
ok (1) | ok (1) |
| id + **=** + var index | `WITH [1] AS v MATCH (n) WHERE id(n) = v[0]` | ok
(0) | ok (0) |
| id + = + literal | `MATCH (n) WHERE id(n) = 1` | ok (0) | ok (0) |
| var-list alone | `WITH [1] AS v MATCH (n) RETURN n` | ok (2) | ok (2) |
| id + IN + var, **0 rows matched** | same, on a graph with no vertices | ok
(0) | ok (0) |
| id + IN + var, **0 rows matched** | same, `MATCH (n:Absent)` | ok (0) | ok
(0) |
Dropping any one ingredient avoids the crash: replacing `id()` with a
property, replacing
`IN` with `=`, or replacing the variable with a literal list all run clean.
The last two
rows show the operator is only reached once a row survives `MATCH`, which is
why the
repro needs at least one vertex.
### Environment
Both legs built from the same `Dockerfile` — same base image, same
toolchain, same flags
— so AGE version is the only variable:
- base: `postgres:18` (PostgreSQL 18.4, Debian), `aarch64`
- crashing: `release/PG18/1.8.0` @ `e43dc1a12b78fba4acef9835b2b10379b8d243b4`
- working: `release/PG18/1.7.0` @ `806fa2ebdb300b3e76ef30cdba61803babbf2683`
- built with `make COPT="-O0 -g3 -fno-omit-frame-pointer"` for the backtrace
`release/PG18/1.8.0` and the `PG18` branch head are currently the **same
commit**, so this
is the state of the PG18 development tip, not just a tagged RC.
**`master` is affected too.** Built from the same Dockerfile (`--build-arg
AGE_BRANCH=master`,
commit `801417404978823bd8732452c3f7959017584785`) on the same `postgres:18`
base, the
snippet above crashes identically:
```
LOG: client backend (PID 96) was terminated by signal 11: Segmentation fault
DETAIL: Failed process was running: SELECT count(*) FROM cypher('gtest', ...
LOG: all server processes terminated; reinitializing
```
So this is not confined to the PG18 release lineage — a fix needs to land on
`master` as well.
### Impact
`WITH [<ids>] AS node_ids MATCH (a) WHERE id(a) IN node_ids` is the natural
way to fetch a
subgraph for a known vertex set, and it is what LightRAG's PostgreSQL graph
backend
issues, so a single ordinary API call crashes the server. Docker Hub
currently has no
`release_PG18_1.8.0` image, so this is not yet in users' hands.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]