This is an automated email from the ASF dual-hosted git repository.
mtaha pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/master by this push:
new a29e2810 Fix nondeterministic age_global_graph regression test (#2365)
a29e2810 is described below
commit a29e2810b1ac5a9e4c3e553879b683a6a1902c8b
Author: John Gemignani <[email protected]>
AuthorDate: Mon Apr 6 03:28:46 2026 -0700
Fix nondeterministic age_global_graph regression test (#2365)
The age_global_graph test had two issues that could cause intermittent
failures:
1. Nondeterministic warning output: The graph_stats() call on a graph
with deliberately deleted vertices produces WARNING messages for
dangling edges. These warnings are emitted by iterating edge label
tables (knows, stalks), and the iteration order is not guaranteed.
Since PostgreSQL WARNING messages cannot be caught or counted from
SQL (only ERROR and above are catchable via PL/pgSQL exception
handling), we suppress them with SET client_min_messages = error.
The suppressed warnings are documented verbatim in comments. The
graph_stats() result row still validates correct dangling-edge
handling.
2. Nondeterministic row ordering: Multiple MATCH...RETURN queries
returned multi-row results without ORDER BY, relying on scan order.
Added ORDER BY id(u), id(v), id(e), id(n), or id(a) as appropriate
to all MATCH...RETURN queries for future-proofing, even those
currently returning a single row.
Files changed:
regress/sql/age_global_graph.sql
regress/expected/age_global_graph.out
Co-authored-by: GitHub Copilot <[email protected]>
---
regress/expected/age_global_graph.out | 59 ++++++++++++++++++++---------------
regress/sql/age_global_graph.sql | 43 +++++++++++++++++--------
2 files changed, 64 insertions(+), 38 deletions(-)
diff --git a/regress/expected/age_global_graph.out
b/regress/expected/age_global_graph.out
index f2d9b059..47863780 100644
--- a/regress/expected/age_global_graph.out
+++ b/regress/expected/age_global_graph.out
@@ -44,19 +44,19 @@ SELECT * FROM cypher('ag_graph_3', $$ CREATE (v:vertex3)
RETURN v $$) AS (v agt
(1 row)
-- load contexts using the vertex_stats command
-SELECT * FROM cypher('ag_graph_3', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_3', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
result
-----------------------------------------------------------------------------------------------
{"id": 844424930131969, "label": "vertex3", "in_degree": 0, "out_degree": 0,
"self_loops": 0}
(1 row)
-SELECT * FROM cypher('ag_graph_2', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_2', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
result
-----------------------------------------------------------------------------------------------
{"id": 844424930131969, "label": "vertex2", "in_degree": 0, "out_degree": 0,
"self_loops": 0}
(1 row)
-SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
result
-----------------------------------------------------------------------------------------------
{"id": 844424930131969, "label": "vertex1", "in_degree": 0, "out_degree": 0,
"self_loops": 0}
@@ -64,7 +64,7 @@ SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN
vertex_stats(u) $$) AS (r
--- loading undefined contexts
--- should throw exception - graph "ag_graph_4" does not exist
-SELECT * FROM cypher('ag_graph_4', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_4', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
ERROR: graph "ag_graph_4" does not exist
LINE 1: SELECT * FROM cypher('ag_graph_4', $$ MATCH (u) RETURN verte...
^
@@ -130,19 +130,19 @@ LINE 1: SELECT * FROM cypher('ag_graph_4', $$ RETURN
delete_global_g...
-- delete_GRAPH_global_contexts
--
-- load contexts again
-SELECT * FROM cypher('ag_graph_3', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_3', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
result
-----------------------------------------------------------------------------------------------
{"id": 844424930131969, "label": "vertex3", "in_degree": 0, "out_degree": 0,
"self_loops": 0}
(1 row)
-SELECT * FROM cypher('ag_graph_2', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_2', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
result
-----------------------------------------------------------------------------------------------
{"id": 844424930131969, "label": "vertex2", "in_degree": 0, "out_degree": 0,
"self_loops": 0}
(1 row)
-SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
result
-----------------------------------------------------------------------------------------------
{"id": 844424930131969, "label": "vertex1", "in_degree": 0, "out_degree": 0,
"self_loops": 0}
@@ -193,14 +193,14 @@ SELECT * FROM cypher('ag_graph_2', $$ CREATE (:Person)
$$) as (v agtype);
(0 rows)
---adding edges between nodes
-SELECT * FROM cypher('ag_graph_2', $$ MATCH (a:Person), (b:Person) WHERE
a.name = 'A' AND b.name = 'B' CREATE (a)-[e:RELTYPE]->(b) RETURN e $$) as (e
agtype);
+SELECT * FROM cypher('ag_graph_2', $$ MATCH (a:Person), (b:Person) WHERE
a.name = 'A' AND b.name = 'B' CREATE (a)-[e:RELTYPE]->(b) RETURN e ORDER BY
id(e) $$) as (e agtype);
e
---
(0 rows)
--checking if vertex stats have been updated along with the new label
--should return 3 vertices
-SELECT * FROM cypher('ag_graph_1', $$ MATCH (n) RETURN vertex_stats(n) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (n) RETURN vertex_stats(n) ORDER
BY id(n) $$) AS (result agtype);
result
-----------------------------------------------------------------------------------------------
{"id": 281474976710657, "label": "", "in_degree": 0, "out_degree": 0,
"self_loops": 0}
@@ -209,7 +209,7 @@ SELECT * FROM cypher('ag_graph_1', $$ MATCH (n) RETURN
vertex_stats(n) $$) AS (r
(3 rows)
--should return 1 vertice and 1 label
-SELECT * FROM cypher('ag_graph_2', $$ MATCH (a) RETURN vertex_stats(a) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_2', $$ MATCH (a) RETURN vertex_stats(a) ORDER
BY id(a) $$) AS (result agtype);
result
-----------------------------------------------------------------------------------------------
{"id": 844424930131969, "label": "vertex2", "in_degree": 0, "out_degree": 0,
"self_loops": 0}
@@ -271,7 +271,7 @@ SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[]->(v) SET
u.id = id(u)
SET v.id = id(v)
SET u.name = 'u'
SET v.name = 'v'
- RETURN u,v $$) AS (u agtype, v agtype);
+ RETURN u,v ORDER BY id(u), id(v) $$) AS
(u agtype, v agtype);
u
| v
--------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------
{"id": 281474976710659, "label": "", "properties": {"id": 281474976710659,
"name": "u"}}::vertex | {"id": 281474976710660, "label": "", "properties":
{"id": 281474976710660, "name": "v"}}::vertex
@@ -285,17 +285,17 @@ SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[]->(v)
MERGE (v)-[:stalks]->(u)
--------
(0 rows)
-SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[e]->(v) RETURN u, e, v $$) AS
(u agtype, e agtype, v agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[e]->(v) RETURN u, e, v ORDER
BY id(e) $$) AS (u agtype, e agtype, v agtype);
u
|
e |
v
--------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------
- {"id": 281474976710660, "label": "", "properties": {"id": 281474976710660,
"name": "v"}}::vertex | {"id": 1407374883553281, "label": "stalks", "end_id":
281474976710659, "start_id": 281474976710660, "properties": {}}::edge | {"id":
281474976710659, "label": "", "properties": {"id": 281474976710659, "name":
"u"}}::vertex
{"id": 281474976710659, "label": "", "properties": {"id": 281474976710659,
"name": "u"}}::vertex | {"id": 1125899906842625, "label": "knows", "end_id":
281474976710660, "start_id": 281474976710659, "properties": {}}::edge | {"id":
281474976710660, "label": "", "properties": {"id": 281474976710660, "name":
"v"}}::vertex
- {"id": 281474976710662, "label": "", "properties": {"id": 281474976710662,
"name": "v"}}::vertex | {"id": 1407374883553282, "label": "stalks", "end_id":
281474976710661, "start_id": 281474976710662, "properties": {}}::edge | {"id":
281474976710661, "label": "", "properties": {"id": 281474976710661, "name":
"u"}}::vertex
{"id": 281474976710661, "label": "", "properties": {"id": 281474976710661,
"name": "u"}}::vertex | {"id": 1125899906842626, "label": "knows", "end_id":
281474976710662, "start_id": 281474976710661, "properties": {}}::edge | {"id":
281474976710662, "label": "", "properties": {"id": 281474976710662, "name":
"v"}}::vertex
- {"id": 281474976710664, "label": "", "properties": {"id": 281474976710664,
"name": "v"}}::vertex | {"id": 1407374883553283, "label": "stalks", "end_id":
281474976710663, "start_id": 281474976710664, "properties": {}}::edge | {"id":
281474976710663, "label": "", "properties": {"id": 281474976710663, "name":
"u"}}::vertex
{"id": 281474976710663, "label": "", "properties": {"id": 281474976710663,
"name": "u"}}::vertex | {"id": 1125899906842627, "label": "knows", "end_id":
281474976710664, "start_id": 281474976710663, "properties": {}}::edge | {"id":
281474976710664, "label": "", "properties": {"id": 281474976710664, "name":
"v"}}::vertex
- {"id": 281474976710666, "label": "", "properties": {"id": 281474976710666,
"name": "v"}}::vertex | {"id": 1407374883553284, "label": "stalks", "end_id":
281474976710665, "start_id": 281474976710666, "properties": {}}::edge | {"id":
281474976710665, "label": "", "properties": {"id": 281474976710665, "name":
"u"}}::vertex
{"id": 281474976710665, "label": "", "properties": {"id": 281474976710665,
"name": "u"}}::vertex | {"id": 1125899906842628, "label": "knows", "end_id":
281474976710666, "start_id": 281474976710665, "properties": {}}::edge | {"id":
281474976710666, "label": "", "properties": {"id": 281474976710666, "name":
"v"}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"id": 281474976710660,
"name": "v"}}::vertex | {"id": 1407374883553281, "label": "stalks", "end_id":
281474976710659, "start_id": 281474976710660, "properties": {}}::edge | {"id":
281474976710659, "label": "", "properties": {"id": 281474976710659, "name":
"u"}}::vertex
+ {"id": 281474976710662, "label": "", "properties": {"id": 281474976710662,
"name": "v"}}::vertex | {"id": 1407374883553282, "label": "stalks", "end_id":
281474976710661, "start_id": 281474976710662, "properties": {}}::edge | {"id":
281474976710661, "label": "", "properties": {"id": 281474976710661, "name":
"u"}}::vertex
+ {"id": 281474976710664, "label": "", "properties": {"id": 281474976710664,
"name": "v"}}::vertex | {"id": 1407374883553283, "label": "stalks", "end_id":
281474976710663, "start_id": 281474976710664, "properties": {}}::edge | {"id":
281474976710663, "label": "", "properties": {"id": 281474976710663, "name":
"u"}}::vertex
+ {"id": 281474976710666, "label": "", "properties": {"id": 281474976710666,
"name": "v"}}::vertex | {"id": 1407374883553284, "label": "stalks", "end_id":
281474976710665, "start_id": 281474976710666, "properties": {}}::edge | {"id":
281474976710665, "label": "", "properties": {"id": 281474976710665, "name":
"u"}}::vertex
(8 rows)
-- what is there now?
@@ -351,21 +351,30 @@ SELECT * FROM ag_graph_1._ag_label_edge;
1407374883553284 | 281474976710666 | 281474976710665 | {}
(8 rows)
--- there should be warning messages
+-- The graph_stats query below will produce warnings for the dangling edges
+-- created by the DELETE commands above. The warnings appear in
nondeterministic
+-- order because they come from iterating edge label tables (knows, stalks),
+-- so we suppress them with client_min_messages. Without suppression, the
+-- output would include these warnings (in some order):
+--
+-- WARNING: edge: [id: 1125899906842626, start: 281474976710661, end:
281474976710662, label: knows] start and end vertices not found
+-- WARNING: ignored malformed or dangling edge
+-- WARNING: edge: [id: 1125899906842627, start: 281474976710663, end:
281474976710664, label: knows] end vertex not found
+-- WARNING: ignored malformed or dangling edge
+-- WARNING: edge: [id: 1407374883553282, start: 281474976710662, end:
281474976710661, label: stalks] start and end vertices not found
+-- WARNING: ignored malformed or dangling edge
+-- WARNING: edge: [id: 1407374883553283, start: 281474976710664, end:
281474976710663, label: stalks] start vertex not found
+-- WARNING: ignored malformed or dangling edge
+--
+-- The result row validates that graph_stats handled the dangling edges
correctly.
+SET client_min_messages = error;
SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS
(result agtype);
-WARNING: edge: [id: 1125899906842626, start: 281474976710661, end:
281474976710662, label: knows] start and end vertices not found
-WARNING: ignored malformed or dangling edge
-WARNING: edge: [id: 1125899906842627, start: 281474976710663, end:
281474976710664, label: knows] end vertex not found
-WARNING: ignored malformed or dangling edge
-WARNING: edge: [id: 1407374883553282, start: 281474976710662, end:
281474976710661, label: stalks] start and end vertices not found
-WARNING: ignored malformed or dangling edge
-WARNING: edge: [id: 1407374883553283, start: 281474976710664, end:
281474976710663, label: stalks] start vertex not found
-WARNING: ignored malformed or dangling edge
result
--------------------------------------------------------------------------
{"graph": "ag_graph_1", "num_loaded_edges": 8, "num_loaded_vertices": 8}
(1 row)
+RESET client_min_messages;
--drop graphs
SELECT * FROM drop_graph('ag_graph_1', true);
NOTICE: drop cascades to 5 other objects
diff --git a/regress/sql/age_global_graph.sql b/regress/sql/age_global_graph.sql
index acd95fbf..376681a8 100644
--- a/regress/sql/age_global_graph.sql
+++ b/regress/sql/age_global_graph.sql
@@ -16,13 +16,13 @@ SELECT * FROM create_graph('ag_graph_3');
SELECT * FROM cypher('ag_graph_3', $$ CREATE (v:vertex3) RETURN v $$) AS (v
agtype);
-- load contexts using the vertex_stats command
-SELECT * FROM cypher('ag_graph_3', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
-SELECT * FROM cypher('ag_graph_2', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
-SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_3', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
+SELECT * FROM cypher('ag_graph_2', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
--- loading undefined contexts
--- should throw exception - graph "ag_graph_4" does not exist
-SELECT * FROM cypher('ag_graph_4', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_4', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
--- delete with invalid parameter
---should return false
@@ -55,9 +55,9 @@ SELECT * FROM cypher('ag_graph_4', $$ RETURN
delete_global_graphs('ag_graph_4')
--
-- load contexts again
-SELECT * FROM cypher('ag_graph_3', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
-SELECT * FROM cypher('ag_graph_2', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
-SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN vertex_stats(u) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_3', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
+SELECT * FROM cypher('ag_graph_2', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u) RETURN vertex_stats(u) ORDER
BY id(u) $$) AS (result agtype);
-- delete all graph contexts
-- should return true
@@ -81,14 +81,14 @@ SELECT * FROM cypher('ag_graph_1', $$ CREATE (n), (m) $$)
as (v agtype);
SELECT * FROM cypher('ag_graph_2', $$ CREATE (:Person) $$) as (v agtype);
---adding edges between nodes
-SELECT * FROM cypher('ag_graph_2', $$ MATCH (a:Person), (b:Person) WHERE
a.name = 'A' AND b.name = 'B' CREATE (a)-[e:RELTYPE]->(b) RETURN e $$) as (e
agtype);
+SELECT * FROM cypher('ag_graph_2', $$ MATCH (a:Person), (b:Person) WHERE
a.name = 'A' AND b.name = 'B' CREATE (a)-[e:RELTYPE]->(b) RETURN e ORDER BY
id(e) $$) as (e agtype);
--checking if vertex stats have been updated along with the new label
--should return 3 vertices
-SELECT * FROM cypher('ag_graph_1', $$ MATCH (n) RETURN vertex_stats(n) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (n) RETURN vertex_stats(n) ORDER
BY id(n) $$) AS (result agtype);
--should return 1 vertice and 1 label
-SELECT * FROM cypher('ag_graph_2', $$ MATCH (a) RETURN vertex_stats(a) $$) AS
(result agtype);
+SELECT * FROM cypher('ag_graph_2', $$ MATCH (a) RETURN vertex_stats(a) ORDER
BY id(a) $$) AS (result agtype);
--
-- graph_stats command
@@ -109,9 +109,9 @@ SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[]->(v) SET
u.id = id(u)
SET v.id = id(v)
SET u.name = 'u'
SET v.name = 'v'
- RETURN u,v $$) AS (u agtype, v agtype);
+ RETURN u,v ORDER BY id(u), id(v) $$) AS
(u agtype, v agtype);
SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[]->(v) MERGE
(v)-[:stalks]->(u) $$) AS (result agtype);
-SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[e]->(v) RETURN u, e, v $$) AS
(u agtype, e agtype, v agtype);
+SELECT * FROM cypher('ag_graph_1', $$ MATCH (u)-[e]->(v) RETURN u, e, v ORDER
BY id(e) $$) AS (u agtype, e agtype, v agtype);
-- what is there now?
SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS
(result agtype);
-- remove some vertices
@@ -121,8 +121,25 @@ DELETE FROM ag_graph_1._ag_label_vertex WHERE id::text =
'281474976710662';
DELETE FROM ag_graph_1._ag_label_vertex WHERE id::text = '281474976710664';
SELECT * FROM ag_graph_1._ag_label_vertex;
SELECT * FROM ag_graph_1._ag_label_edge;
--- there should be warning messages
+-- The graph_stats query below will produce warnings for the dangling edges
+-- created by the DELETE commands above. The warnings appear in
nondeterministic
+-- order because they come from iterating edge label tables (knows, stalks),
+-- so we suppress them with client_min_messages. Without suppression, the
+-- output would include these warnings (in some order):
+--
+-- WARNING: edge: [id: 1125899906842626, start: 281474976710661, end:
281474976710662, label: knows] start and end vertices not found
+-- WARNING: ignored malformed or dangling edge
+-- WARNING: edge: [id: 1125899906842627, start: 281474976710663, end:
281474976710664, label: knows] end vertex not found
+-- WARNING: ignored malformed or dangling edge
+-- WARNING: edge: [id: 1407374883553282, start: 281474976710662, end:
281474976710661, label: stalks] start and end vertices not found
+-- WARNING: ignored malformed or dangling edge
+-- WARNING: edge: [id: 1407374883553283, start: 281474976710664, end:
281474976710663, label: stalks] start vertex not found
+-- WARNING: ignored malformed or dangling edge
+--
+-- The result row validates that graph_stats handled the dangling edges
correctly.
+SET client_min_messages = error;
SELECT * FROM cypher('ag_graph_1', $$ RETURN graph_stats('ag_graph_1') $$) AS
(result agtype);
+RESET client_min_messages;
--drop graphs