Skip to content

Commit 49b186e

Browse files
jrgemignaniMuhammadTahaNaveed
authored andcommitted
Fix Issue 1884: Ambiguous column reference (#2306)
Fix Issue 1884: Ambiguous column reference and invalid AGT header errors. Note: This PR was created with AI tools and a human, or 2. This commit addresses two related bugs that occur when using SET to store graph elements (vertices, edges, paths) as property values: Issue 1884 - "column reference is ambiguous" error: When a Cypher query uses the same variable in both the SET expression RHS and the RETURN clause (e.g., SET n.prop = n RETURN n), PostgreSQL would report "column reference is ambiguous" because the variable appeared in multiple subqueries without proper qualification. Solution: The fix for this issue was already in place through the target entry naming scheme that qualifies column references. "Invalid AGT header value" offset error: When deserializing nested VERTEX, EDGE, or PATH values stored in properties, the system would fail with errors like "Invalid AGT header value: 0x00000041". This occurred because ag_serialize_extended_type() did not include alignment padding (padlen) in the agtentry length calculation for these types, while fill_agtype_value() uses INTALIGN() when reading, causing offset mismatch. Solution: Modified ag_serialize_extended_type() in agtype_ext.c to include padlen in the agtentry length for VERTEX, EDGE, and PATH cases, matching the existing pattern used for INTEGER, FLOAT, and NUMERIC types: *agtentry = AGTENTRY_IS_AGTYPE | (padlen + (AGTENTRY_OFFLENMASK & ...)); This ensures the serialized length accounts for alignment padding, allowing correct deserialization of nested graph elements. Appropriate regression tests were added to verify the fixes. Co-authored by: Zainab Saad <105385638+Zainab-Saad@users.noreply.github.com> modified: regress/expected/cypher_set.out modified: regress/sql/cypher_set.sql modified: src/backend/parser/cypher_clause.c modified: src/backend/utils/adt/agtype_ext.c
1 parent 7112a9a commit 49b186e

4 files changed

Lines changed: 451 additions & 6 deletions

File tree

regress/expected/cypher_set.out

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,245 @@ SELECT * FROM cypher('issue_1634', $$ MATCH (u) DELETE (u) $$) AS (u agtype);
988988
---
989989
(0 rows)
990990

991+
--
992+
-- Issue 1884: column reference is ambiguous when using same variable in
993+
-- SET expression and RETURN clause
994+
--
995+
-- These tests cover:
996+
-- 1. "column reference is ambiguous" error when variable is used in both
997+
-- SET expression RHS (e.g., SET n.prop = n) and RETURN clause
998+
-- 2. "Invalid AGT header value" error caused by incorrect offset calculation
999+
-- when nested VERTEX/EDGE/PATH values are serialized in properties
1000+
--
1001+
-- Tests use isolated data to keep output manageable and avoid cumulative nesting
1002+
--
1003+
SELECT * FROM create_graph('issue_1884');
1004+
NOTICE: graph "issue_1884" has been created
1005+
create_graph
1006+
--------------
1007+
1008+
(1 row)
1009+
1010+
-- ============================================================================
1011+
-- Test Group A: Basic "column reference is ambiguous" fix (Issue 1884)
1012+
-- ============================================================================
1013+
-- Test A1: Core issue - SET n.prop = n with RETURN n (the original bug)
1014+
SELECT * FROM cypher('issue_1884', $$
1015+
CREATE (n:TestA1 {name: 'A1'})
1016+
SET n.self = n
1017+
RETURN n
1018+
$$) AS (result agtype);
1019+
result
1020+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1021+
{"id": 844424930131969, "label": "TestA1", "properties": {"name": "A1", "self": {"id": 844424930131969, "label": "TestA1", "properties": {"name": "A1"}}::vertex}}::vertex
1022+
(1 row)
1023+
1024+
-- Test A2: Multiple variables in SET and RETURN
1025+
SELECT * FROM cypher('issue_1884', $$
1026+
CREATE (a:TestA2 {name: 'A'})-[e:LINK {w: 1}]->(b:TestA2 {name: 'B'})
1027+
SET a.edge = e, b.edge = e
1028+
RETURN a, e, b
1029+
$$) AS (a agtype, e agtype, b agtype);
1030+
a | e | b
1031+
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1032+
{"id": 1125899906842625, "label": "TestA2", "properties": {"edge": {"id": 1407374883553281, "label": "LINK", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {"w": 1}}::edge, "name": "A"}}::vertex | {"id": 1407374883553281, "label": "LINK", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {"w": 1}}::edge | {"id": 1125899906842626, "label": "TestA2", "properties": {"edge": {"id": 1407374883553281, "label": "LINK", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {"w": 1}}::edge, "name": "B"}}::vertex
1033+
(1 row)
1034+
1035+
-- Test A3: SET edge property to node reference
1036+
SELECT * FROM cypher('issue_1884', $$
1037+
CREATE (a:TestA3 {name: 'X'})-[e:REL]->(b:TestA3 {name: 'Y'})
1038+
SET e.src = a, e.dst = b
1039+
RETURN e
1040+
$$) AS (e agtype);
1041+
e
1042+
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1043+
{"id": 1970324836974593, "label": "REL", "end_id": 1688849860263938, "start_id": 1688849860263937, "properties": {"dst": {"id": 1688849860263938, "label": "TestA3", "properties": {"name": "Y"}}::vertex, "src": {"id": 1688849860263937, "label": "TestA3", "properties": {"name": "X"}}::vertex}}::edge
1044+
(1 row)
1045+
1046+
-- ============================================================================
1047+
-- Test Group B: Nested VERTEX/EDGE/PATH serialization (offset error fix)
1048+
-- ============================================================================
1049+
-- Test B1: Vertex nested in vertex property (tests VERTEX serialization)
1050+
SELECT * FROM cypher('issue_1884', $$
1051+
CREATE (n:TestB1 {val: 1})
1052+
SET n.copy = n
1053+
RETURN n
1054+
$$) AS (result agtype);
1055+
result
1056+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
1057+
{"id": 2251799813685249, "label": "TestB1", "properties": {"val": 1, "copy": {"id": 2251799813685249, "label": "TestB1", "properties": {"val": 1}}::vertex}}::vertex
1058+
(1 row)
1059+
1060+
-- Verify nested vertex can be read back
1061+
SELECT * FROM cypher('issue_1884', $$
1062+
MATCH (n:TestB1)
1063+
RETURN n.copy
1064+
$$) AS (copy agtype);
1065+
copy
1066+
-------------------------------------------------------------------------------
1067+
{"id": 2251799813685249, "label": "TestB1", "properties": {"val": 1}}::vertex
1068+
(1 row)
1069+
1070+
-- Test B2: Edge nested in node property (tests EDGE serialization)
1071+
SELECT * FROM cypher('issue_1884', $$
1072+
CREATE (a:TestB2 {name: 'start'})-[e:B2REL {x: 100}]->(b:TestB2 {name: 'end'})
1073+
SET a.myEdge = e
1074+
RETURN a
1075+
$$) AS (a agtype);
1076+
a
1077+
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1078+
{"id": 2533274790395905, "label": "TestB2", "properties": {"name": "start", "myEdge": {"id": 2814749767106561, "label": "B2REL", "end_id": 2533274790395906, "start_id": 2533274790395905, "properties": {"x": 100}}::edge}}::vertex
1079+
(1 row)
1080+
1081+
-- Verify nested edge can be read back
1082+
SELECT * FROM cypher('issue_1884', $$
1083+
MATCH (n:TestB2 {name: 'start'})
1084+
RETURN n.myEdge
1085+
$$) AS (edge agtype);
1086+
edge
1087+
--------------------------------------------------------------------------------------------------------------------------------------
1088+
{"id": 2814749767106561, "label": "B2REL", "end_id": 2533274790395906, "start_id": 2533274790395905, "properties": {"x": 100}}::edge
1089+
(1 row)
1090+
1091+
-- Test B3: Path nested in node property (tests PATH serialization)
1092+
-- First create the pattern
1093+
SELECT * FROM cypher('issue_1884', $$
1094+
CREATE (a:TestB3)-[e:B3REL]->(b:TestB3)
1095+
RETURN a
1096+
$$) AS (a agtype);
1097+
a
1098+
-----------------------------------------------------------------------
1099+
{"id": 3096224743817217, "label": "TestB3", "properties": {}}::vertex
1100+
(1 row)
1101+
1102+
-- Then match the path and set it (MATCH only sees committed data)
1103+
SELECT * FROM cypher('issue_1884', $$
1104+
MATCH p = (a:TestB3)-[e:B3REL]->(b:TestB3)
1105+
SET a.myPath = p
1106+
RETURN a
1107+
$$) AS (a agtype);
1108+
a
1109+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1110+
{"id": 3096224743817217, "label": "TestB3", "properties": {"myPath": [{"id": 3096224743817217, "label": "TestB3", "properties": {}}::vertex, {"id": 3377699720527873, "label": "B3REL", "end_id": 3096224743817218, "start_id": 3096224743817217, "properties": {}}::edge, {"id": 3096224743817218, "label": "TestB3", "properties": {}}::vertex]::path}}::vertex
1111+
(1 row)
1112+
1113+
-- Verify nested path can be read back
1114+
SELECT * FROM cypher('issue_1884', $$
1115+
MATCH (n:TestB3)
1116+
WHERE n.myPath IS NOT NULL
1117+
RETURN n.myPath
1118+
$$) AS (path agtype);
1119+
path
1120+
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1121+
[{"id": 3096224743817217, "label": "TestB3", "properties": {}}::vertex, {"id": 3377699720527873, "label": "B3REL", "end_id": 3096224743817218, "start_id": 3096224743817217, "properties": {}}::edge, {"id": 3096224743817218, "label": "TestB3", "properties": {}}::vertex]::path
1122+
(1 row)
1123+
1124+
-- ============================================================================
1125+
-- Test Group C: Nested structures in arrays and maps
1126+
-- ============================================================================
1127+
-- Test C1: Vertex in array
1128+
SELECT * FROM cypher('issue_1884', $$
1129+
CREATE (n:TestC1 {tag: 'arrtest'})
1130+
SET n.arr = [n]
1131+
RETURN n
1132+
$$) AS (result agtype);
1133+
result
1134+
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1135+
{"id": 3659174697238529, "label": "TestC1", "properties": {"arr": [{"id": 3659174697238529, "label": "TestC1", "properties": {"tag": "arrtest"}}::vertex], "tag": "arrtest"}}::vertex
1136+
(1 row)
1137+
1138+
-- Verify array with nested vertex
1139+
SELECT * FROM cypher('issue_1884', $$
1140+
MATCH (n:TestC1)
1141+
RETURN n.arr[0]
1142+
$$) AS (elem agtype);
1143+
elem
1144+
---------------------------------------------------------------------------------------
1145+
{"id": 3659174697238529, "label": "TestC1", "properties": {"tag": "arrtest"}}::vertex
1146+
(1 row)
1147+
1148+
-- Test C2: Vertex in map
1149+
SELECT * FROM cypher('issue_1884', $$
1150+
CREATE (n:TestC2 {tag: 'maptest'})
1151+
SET n.obj = {node: n}
1152+
RETURN n
1153+
$$) AS (result agtype);
1154+
result
1155+
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1156+
{"id": 3940649673949185, "label": "TestC2", "properties": {"obj": {"node": {"id": 3940649673949185, "label": "TestC2", "properties": {"tag": "maptest"}}::vertex}, "tag": "maptest"}}::vertex
1157+
(1 row)
1158+
1159+
-- Verify map with nested vertex
1160+
SELECT * FROM cypher('issue_1884', $$
1161+
MATCH (n:TestC2)
1162+
RETURN n.obj.node
1163+
$$) AS (node agtype);
1164+
node
1165+
---------------------------------------------------------------------------------------
1166+
{"id": 3940649673949185, "label": "TestC2", "properties": {"tag": "maptest"}}::vertex
1167+
(1 row)
1168+
1169+
-- ============================================================================
1170+
-- Test Group D: MERGE and CREATE with self-reference
1171+
-- ============================================================================
1172+
-- Test D1: MERGE with SET self-reference
1173+
SELECT * FROM cypher('issue_1884', $$
1174+
MERGE (n:TestD1 {name: 'merged'})
1175+
SET n.ref = n
1176+
RETURN n
1177+
$$) AS (result agtype);
1178+
result
1179+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1180+
{"id": 4222124650659841, "label": "TestD1", "properties": {"ref": {"id": 4222124650659841, "label": "TestD1", "properties": {"name": "merged"}}::vertex, "name": "merged"}}::vertex
1181+
(1 row)
1182+
1183+
-- Test D2: CREATE with SET self-reference
1184+
SELECT * FROM cypher('issue_1884', $$
1185+
CREATE (n:TestD2 {name: 'created'})
1186+
SET n.ref = n
1187+
RETURN n
1188+
$$) AS (result agtype);
1189+
result
1190+
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1191+
{"id": 4503599627370497, "label": "TestD2", "properties": {"ref": {"id": 4503599627370497, "label": "TestD2", "properties": {"name": "created"}}::vertex, "name": "created"}}::vertex
1192+
(1 row)
1193+
1194+
-- ============================================================================
1195+
-- Test Group E: Functions with variable references
1196+
-- ============================================================================
1197+
-- Test E1: id() and label() functions
1198+
SELECT * FROM cypher('issue_1884', $$
1199+
CREATE (n:TestE1 {name: 'functest'})
1200+
SET n.myId = id(n), n.myLabel = label(n)
1201+
RETURN n
1202+
$$) AS (result agtype);
1203+
result
1204+
----------------------------------------------------------------------------------------------------------------------------------------
1205+
{"id": 4785074604081153, "label": "TestE1", "properties": {"myId": 4785074604081153, "name": "functest", "myLabel": "TestE1"}}::vertex
1206+
(1 row)
1207+
1208+
-- Test E2: nodes() and relationships() with path
1209+
-- First create the pattern
1210+
SELECT * FROM cypher('issue_1884', $$
1211+
CREATE (a:TestE2)-[e:E2REL]->(b:TestE2)
1212+
RETURN a
1213+
$$) AS (a agtype);
1214+
a
1215+
-----------------------------------------------------------------------
1216+
{"id": 5066549580791809, "label": "TestE2", "properties": {}}::vertex
1217+
(1 row)
1218+
1219+
-- Then match the path and extract nodes/relationships (MATCH only sees committed data)
1220+
SELECT * FROM cypher('issue_1884', $$
1221+
MATCH p = (a:TestE2)-[e:E2REL]->(b:TestE2)
1222+
SET a.pathNodes = nodes(p), a.pathRels = relationships(p)
1223+
RETURN a
1224+
$$) AS (a agtype);
1225+
a
1226+
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1227+
{"id": 5066549580791809, "label": "TestE2", "properties": {"pathRels": [{"id": 5348024557502465, "label": "E2REL", "end_id": 5066549580791810, "start_id": 5066549580791809, "properties": {}}::edge], "pathNodes": [{"id": 5066549580791809, "label": "TestE2", "properties": {}}::vertex, {"id": 5066549580791810, "label": "TestE2", "properties": {}}::vertex]}}::vertex
1228+
(1 row)
1229+
9911230
--
9921231
-- Clean up
9931232
--
@@ -1038,6 +1277,33 @@ NOTICE: graph "issue_1634" has been dropped
10381277

10391278
(1 row)
10401279

1280+
SELECT drop_graph('issue_1884', true);
1281+
NOTICE: drop cascades to 19 other objects
1282+
DETAIL: drop cascades to table issue_1884._ag_label_vertex
1283+
drop cascades to table issue_1884._ag_label_edge
1284+
drop cascades to table issue_1884."TestA1"
1285+
drop cascades to table issue_1884."TestA2"
1286+
drop cascades to table issue_1884."LINK"
1287+
drop cascades to table issue_1884."TestA3"
1288+
drop cascades to table issue_1884."REL"
1289+
drop cascades to table issue_1884."TestB1"
1290+
drop cascades to table issue_1884."TestB2"
1291+
drop cascades to table issue_1884."B2REL"
1292+
drop cascades to table issue_1884."TestB3"
1293+
drop cascades to table issue_1884."B3REL"
1294+
drop cascades to table issue_1884."TestC1"
1295+
drop cascades to table issue_1884."TestC2"
1296+
drop cascades to table issue_1884."TestD1"
1297+
drop cascades to table issue_1884."TestD2"
1298+
drop cascades to table issue_1884."TestE1"
1299+
drop cascades to table issue_1884."TestE2"
1300+
drop cascades to table issue_1884."E2REL"
1301+
NOTICE: graph "issue_1884" has been dropped
1302+
drop_graph
1303+
------------
1304+
1305+
(1 row)
1306+
10411307
--
10421308
-- End
10431309
--

0 commit comments

Comments
 (0)