Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ __pycache__
**/apache_age_python.egg-info

drivers/python/build
*.bc
249 changes: 249 additions & 0 deletions regress/expected/cypher_merge.out
Original file line number Diff line number Diff line change
Expand Up @@ -2001,9 +2001,258 @@ SELECT * FROM cypher('issue_1954', $$ MATCH (n) DETACH DELETE n $$) AS (a agtype
---
(0 rows)

--
-- ON CREATE SET / ON MATCH SET tests (issue #1619)
--
SELECT create_graph('merge_actions');
NOTICE: graph "merge_actions" has been created
create_graph
--------------

(1 row)

-- Basic ON CREATE SET: first run creates the node
SELECT * FROM cypher('merge_actions', $$
MERGE (n:Person {name: 'Alice'})
ON CREATE SET n.created = true
RETURN n.name, n.created
$$) AS (name agtype, created agtype);
name | created
---------+---------
"Alice" | true
(1 row)

-- ON MATCH SET: second run matches the existing node
SELECT * FROM cypher('merge_actions', $$
MERGE (n:Person {name: 'Alice'})
ON MATCH SET n.found = true
RETURN n.name, n.created, n.found
$$) AS (name agtype, created agtype, found agtype);
name | created | found
---------+---------+-------
"Alice" | true | true
(1 row)

-- Both ON CREATE SET and ON MATCH SET (first run = create)
SELECT * FROM cypher('merge_actions', $$
MERGE (n:Person {name: 'Bob'})
ON CREATE SET n.created = true
ON MATCH SET n.matched = true
RETURN n.name, n.created, n.matched
$$) AS (name agtype, created agtype, matched agtype);
name | created | matched
-------+---------+---------
"Bob" | true |
(1 row)

-- Both ON CREATE SET and ON MATCH SET (second run = match)
SELECT * FROM cypher('merge_actions', $$
MERGE (n:Person {name: 'Bob'})
ON CREATE SET n.created = true
ON MATCH SET n.matched = true
RETURN n.name, n.created, n.matched
$$) AS (name agtype, created agtype, matched agtype);
name | created | matched
-------+---------+---------
"Bob" | true | true
(1 row)

-- ON CREATE SET with MERGE after MATCH (Case 1: has predecessor, first run = create)
SELECT * FROM cypher('merge_actions', $$
MATCH (a:Person {name: 'Alice'})
MERGE (a)-[:KNOWS]->(b:Person {name: 'Charlie'})
ON CREATE SET b.source = 'merge_create'
RETURN a.name, b.name, b.source
$$) AS (a agtype, b agtype, source agtype);
a | b | source
---------+-----------+----------------
"Alice" | "Charlie" | "merge_create"
(1 row)

-- ON MATCH SET with MERGE after MATCH (Case 1: has predecessor, second run = match)
SELECT * FROM cypher('merge_actions', $$
MATCH (a:Person {name: 'Alice'})
MERGE (a)-[:KNOWS]->(b:Person {name: 'Charlie'})
ON MATCH SET b.visited = true
RETURN a.name, b.name, b.visited
$$) AS (a agtype, b agtype, visited agtype);
a | b | visited
---------+-----------+---------
"Alice" | "Charlie" | true
(1 row)

-- Multiple SET items in a single ON CREATE SET
SELECT * FROM cypher('merge_actions', $$
MERGE (n:Person {name: 'Dave'})
ON CREATE SET n.a = 1, n.b = 2
RETURN n.name, n.a, n.b
$$) AS (name agtype, a agtype, b agtype);
name | a | b
--------+---+---
"Dave" | 1 | 2
(1 row)

-- Reverse order: ON MATCH before ON CREATE should work
SELECT * FROM cypher('merge_actions', $$
MERGE (n:Person {name: 'Eve'})
ON MATCH SET n.seen = true
ON CREATE SET n.new = true
RETURN n.name, n.new
$$) AS (name agtype, new agtype);
name | new
-------+------
"Eve" | true
(1 row)

-- Error: ON CREATE SET specified more than once
SELECT * FROM cypher('merge_actions', $$
MERGE (n:Person {name: 'Bad'})
ON CREATE SET n.a = 1
ON CREATE SET n.b = 2
RETURN n
$$) AS (n agtype);
ERROR: ON CREATE SET specified more than once
LINE 1: SELECT * FROM cypher('merge_actions', $$
^
-- Error: ON MATCH SET specified more than once
SELECT * FROM cypher('merge_actions', $$
MERGE (n:Person {name: 'Bad'})
ON MATCH SET n.a = 1
ON MATCH SET n.b = 2
RETURN n
$$) AS (n agtype);
ERROR: ON MATCH SET specified more than once
LINE 1: SELECT * FROM cypher('merge_actions', $$
^
-- Chained (non-terminal) MERGE with ON CREATE SET (eager-buffering path)
SELECT * FROM cypher('merge_actions', $$
MERGE (a:Person {name: 'Frank'})
ON CREATE SET a.created = true
MERGE (a)-[:KNOWS]->(b:Person {name: 'Grace'})
ON CREATE SET b.created = true
RETURN a.name, a.created, b.name, b.created
$$) AS (a_name agtype, a_created agtype, b_name agtype, b_created agtype);
a_name | a_created | b_name | b_created
---------+-----------+---------+-----------
"Frank" | true | "Grace" | true
(1 row)

-- Chained (non-terminal) MERGE with ON MATCH SET (second run = match)
SELECT * FROM cypher('merge_actions', $$
MERGE (a:Person {name: 'Frank'})
ON MATCH SET a.matched = true
MERGE (a)-[:KNOWS]->(b:Person {name: 'Grace'})
ON MATCH SET b.matched = true
RETURN a.name, a.matched, b.name, b.matched
$$) AS (a_name agtype, a_matched agtype, b_name agtype, b_matched agtype);
a_name | a_matched | b_name | b_matched
---------+-----------+---------+-----------
"Frank" | true | "Grace" | true
(1 row)

-- ON keyword as label name (backward compat via safe_keywords)
SELECT * FROM cypher('merge_actions', $$
CREATE (n:on {name: 'test'})
RETURN n.name
$$) AS (name agtype);
name
--------
"test"
(1 row)

-- Issue #2347: RHS of ON CREATE / ON MATCH SET referencing a bound
-- variable crashed the backend when MERGE had a previous clause, because
-- the lateral-join's ParseNamespaceItem had p_nscolumns=NULL.
-- ON CREATE SET with RHS referencing the outer MATCH's variable
SELECT * FROM cypher('merge_actions', $$ CREATE (:Person {name:'Anchor'}) $$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('merge_actions', $$
MATCH (a:Person {name: 'Anchor'})
MERGE (b:Person {name: 'FromOuter'})
ON CREATE SET b.source_name = a.name
RETURN a.name, b.name, b.source_name
$$) AS (a_name agtype, b_name agtype, b_source agtype);
a_name | b_name | b_source
----------+-------------+----------
"Anchor" | "FromOuter" | "Anchor"
(1 row)

-- ON CREATE SET with RHS referencing the MERGE-bound variable itself
SELECT * FROM cypher('merge_actions', $$
MATCH (a:Person {name: 'Anchor'})
MERGE (b:Person {name: 'SelfRef'})
ON CREATE SET b.echo_name = b.name
RETURN b.name, b.echo_name
$$) AS (b_name agtype, b_echo agtype);
b_name | b_echo
-----------+-----------
"SelfRef" | "SelfRef"
(1 row)

-- ON CREATE SET driven by UNWIND with self-reference on the RHS
-- (Muhammad's second reproducer)
SELECT * FROM cypher('merge_actions', $$
UNWIND ['U1', 'U2'] AS nm
MERGE (n:Person {name: nm})
ON CREATE SET n.copy_name = n.name
RETURN n.name, n.copy_name
$$) AS (n_name agtype, n_copy agtype);
n_name | n_copy
--------+--------
"U1" | "U1"
"U2" | "U2"
(2 rows)

-- Multiple SET items mixing outer-ref, self-ref, and literal RHS
SELECT * FROM cypher('merge_actions', $$
MATCH (a:Person {name: 'Anchor'})
MERGE (b:Person {name: 'MultiItem'})
ON CREATE SET b.from_a = a.name, b.self = b.name, b.lit = 'literal'
RETURN b.from_a, b.self, b.lit
$$) AS (fa agtype, sf agtype, lit agtype);
fa | sf | lit
----------+-------------+-----------
"Anchor" | "MultiItem" | "literal"
(1 row)

-- ON MATCH SET with variable RHS (second run on existing node)
SELECT * FROM cypher('merge_actions', $$
MATCH (a:Person {name: 'Anchor'})
MERGE (b:Person {name: 'FromOuter'})
ON CREATE SET b.source_name = a.name
ON MATCH SET b.last_seen_by = a.name
RETURN b.source_name, b.last_seen_by
$$) AS (src agtype, last agtype);
src | last
----------+----------
"Anchor" | "Anchor"
(1 row)

-- cleanup
SELECT * FROM cypher('merge_actions', $$ MATCH (n) DETACH DELETE n $$) AS (a agtype);
a
---
(0 rows)

--
-- delete graphs
--
SELECT drop_graph('merge_actions', true);
NOTICE: drop cascades to 5 other objects
DETAIL: drop cascades to table merge_actions._ag_label_vertex
drop cascades to table merge_actions._ag_label_edge
drop cascades to table merge_actions."Person"
drop cascades to table merge_actions."KNOWS"
drop cascades to table merge_actions."on"
NOTICE: graph "merge_actions" has been dropped
drop_graph
------------

(1 row)

SELECT drop_graph('issue_1907', true);
NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table issue_1907._ag_label_vertex
Expand Down
Loading
Loading