Skip to content
Open
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
63 changes: 62 additions & 1 deletion regress/expected/cypher_delete.out
Original file line number Diff line number Diff line change
Expand Up @@ -812,15 +812,76 @@ SELECT id as "expected: 0 rows" FROM setdelete._ag_label_edge;
------------------
(0 rows)

-- stale ctid fallback - SET one alias, then DELETE the same entity via another alias.
SELECT * FROM cypher('setdelete', $$ CREATE (:B) $$) as ("CREATE" agtype);
CREATE
--------
(0 rows)

SELECT * FROM cypher('setdelete', $$ MATCH (n:B), (m:B) WHERE id(n) = id(m) SET n.flag = true DELETE m $$) as ("SET + DELETE alias" agtype);
SET + DELETE alias
--------------------
(0 rows)

SELECT count(*) as "expected: 0 rows" FROM setdelete."B";
expected: 0 rows
------------------
0
(1 row)

-- hidden ctid propagation through WITH.
SELECT * FROM cypher('setdelete', $$ CREATE (:C) $$) as ("CREATE" agtype);
CREATE
--------
(0 rows)

SELECT * FROM cypher('setdelete', $$ MATCH (n:C) WITH n DELETE n $$) as ("WITH DELETE" agtype);
WITH DELETE
-------------
(0 rows)

SELECT count(*) as "expected: 0 rows" FROM setdelete."C";
expected: 0 rows
------------------
0
(1 row)

-- stale edge ctid fallback with DETACH DELETE through another alias.
SELECT * FROM cypher('setdelete', $$ CREATE (:D)-[:de]->(:D) $$) AS ("CREATE" agtype);
CREATE
--------
(0 rows)

SELECT * FROM cypher('setdelete', $$ MATCH (n)-[e:de]->(m), (x)-[f:de]->(y) WHERE id(e) = id(f) SET e.i = 1 DETACH DELETE x $$) AS ("SET + DETACH alias" agtype);
SET + DETACH alias
--------------------
(0 rows)

SELECT count(*) as "expected: 1 row" FROM setdelete."D";
expected: 1 row
-----------------
1
(1 row)

SELECT count(*) as "expected: 0 rows" FROM setdelete.de;
expected: 0 rows
------------------
0
(1 row)

-- clean up
SELECT drop_graph('setdelete', true);
NOTICE: drop cascades to 6 other objects
NOTICE: drop cascades to 10 other objects
DETAIL: drop cascades to table setdelete._ag_label_vertex
drop cascades to table setdelete._ag_label_edge
drop cascades to table setdelete."A"
drop cascades to table setdelete.n
drop cascades to table setdelete.e
drop cascades to table setdelete.m
drop cascades to table setdelete."B"
drop cascades to table setdelete."C"
drop cascades to table setdelete."D"
drop cascades to table setdelete.de
NOTICE: graph "setdelete" has been dropped
drop_graph
------------
Expand Down
43 changes: 42 additions & 1 deletion regress/expected/cypher_remove.out
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,45 @@ SELECT * FROM cypher('cypher_remove', $$MATCH ()-[e:edge_multi_property]-() REMO
{"id": 3659174697238529, "label": "edge_multi_property", "end_id": 281474976710664, "start_id": 281474976710663, "properties": {}}::edge
(2 rows)

-- Hidden ctid columns must propagate through WITH for REMOVE.
SELECT * FROM cypher('cypher_remove', $$
CREATE (:ctid_remove {drop_me: 1})
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('cypher_remove', $$
MATCH (n:ctid_remove)
WITH n
REMOVE n.drop_me
RETURN n
$$) AS (a agtype);
a
----------------------------------------------------------------------------
{"id": 3940649673949185, "label": "ctid_remove", "properties": {}}::vertex
(1 row)

-- REMOVE must fall back from a stale ctid after an earlier SET moves the row.
SELECT * FROM cypher('cypher_remove', $$
CREATE (:ctid_remove_stale {drop_me: 1})
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('cypher_remove', $$
MATCH (n:ctid_remove_stale), (m:ctid_remove_stale)
WHERE id(n) = id(m)
SET n.changed = true
REMOVE m.drop_me
RETURN m
$$) AS (a agtype);
a
-------------------------------------------------------------------------------------------------
{"id": 4222124650659841, "label": "ctid_remove_stale", "properties": {"changed": true}}::vertex
(1 row)

--Errors
SELECT * FROM cypher('cypher_remove', $$REMOVE n.i$$) AS (a agtype);
ERROR: REMOVE cannot be the first clause in a Cypher query
Expand All @@ -475,7 +514,7 @@ LINE 1: SELECT * FROM cypher('cypher_remove', $$MATCH (n) REMOVE wro...
--
DROP FUNCTION remove_test;
SELECT drop_graph('cypher_remove', true);
NOTICE: drop cascades to 13 other objects
NOTICE: drop cascades to 15 other objects
DETAIL: drop cascades to table cypher_remove._ag_label_vertex
drop cascades to table cypher_remove._ag_label_edge
drop cascades to table cypher_remove.test_1
Expand All @@ -489,6 +528,8 @@ drop cascades to table cypher_remove.test_6
drop cascades to table cypher_remove.e
drop cascades to table cypher_remove.test_7
drop cascades to table cypher_remove.edge_multi_property
drop cascades to table cypher_remove.ctid_remove
drop cascades to table cypher_remove.ctid_remove_stale
NOTICE: graph "cypher_remove" has been dropped
drop_graph
------------
Expand Down
239 changes: 239 additions & 0 deletions regress/expected/cypher_set.out
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,226 @@ $$) AS (a agtype);
{"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
(1 row)

-- ============================================================================
-- ctid lookup fallback tests
-- ============================================================================
SELECT create_graph('ctid_set');
NOTICE: graph "ctid_set" has been created
create_graph
--------------

(1 row)

-- Same entity referenced through two aliases: the second SET must fall back
-- from the stale ctid to graphid lookup after the first SET moves the tuple.
SELECT * FROM cypher('ctid_set', $$CREATE (:v {k: 1})$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('ctid_set', $$
MATCH (n:v), (m:v)
WHERE id(n) = id(m)
SET n.a = 1
SET m.a = 2
RETURN n.a, m.a
$$) AS (n agtype, m agtype);
n | m
---+---
2 | 2
(1 row)

SELECT * FROM cypher('ctid_set', $$MATCH (n:v) RETURN n.a$$) AS (a agtype);
a
---
2
(1 row)

-- Updating the same entity through different aliases must preserve earlier
-- changes, including when a later RHS depends on an earlier SET.
SELECT * FROM cypher('ctid_set', $$
CREATE (:alias_vertex {k: 1})
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('ctid_set', $$
MATCH (n:alias_vertex), (m:alias_vertex)
WHERE id(n) = id(m)
SET n.a = 1
SET m.b = n.a + 1
RETURN n.a, m.a, m.b
$$) AS (na agtype, ma agtype, mb agtype);
na | ma | mb
----+----+----
1 | 1 | 2
(1 row)

SELECT * FROM cypher('ctid_set', $$
MATCH (n:alias_vertex)
RETURN n.a, n.b
$$) AS (a agtype, b agtype);
a | b
---+---
1 | 2
(1 row)

-- The same alias synchronization is required for edges.
SELECT * FROM cypher('ctid_set', $$
CREATE ()-[:alias_edge {k: 1}]->()
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('ctid_set', $$
MATCH ()-[n:alias_edge]->(), ()-[m:alias_edge]->()
WHERE id(n) = id(m)
SET n.a = 1
SET m.b = n.a + 1
RETURN n.a, m.a, m.b
$$) AS (na agtype, ma agtype, mb agtype);
na | ma | mb
----+----+----
1 | 1 | 2
(1 row)

SELECT * FROM cypher('ctid_set', $$
MATCH ()-[n:alias_edge]->()
RETURN n.a, n.b
$$) AS (a agtype, b agtype);
a | b
---+---
1 | 2
(1 row)

-- Hidden ctid columns must propagate through WITH.
SELECT * FROM cypher('ctid_set', $$CREATE (:w {k: 1})$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('ctid_set', $$
MATCH (n:w)
WITH n
SET n.k = 2
RETURN n.k
$$) AS (a agtype);
a
---
2
(1 row)

SELECT * FROM cypher('ctid_set', $$MATCH (n:w) RETURN n.k$$) AS (a agtype);
a
---
2
(1 row)

-- Hidden ctid columns must follow simple WITH aliases too.
SELECT * FROM cypher('ctid_set', $$
MATCH (n:w)
WITH n AS m
SET m.k = 3
RETURN m.k
$$) AS (a agtype);
a
---
3
(1 row)

SELECT * FROM cypher('ctid_set', $$MATCH (n:w) RETURN n.k$$) AS (a agtype);
a
---
3
(1 row)

-- Computed WITH expressions must not inherit a ctid from one of their inputs.
SELECT * FROM cypher('ctid_set', $$
CREATE (:expr {k: 1}), (:expr {k: 2})
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('ctid_set', $$
MATCH (a:expr {k: 1}), (b:expr {k: 2})
WITH CASE WHEN a.k = 1 THEN a ELSE b END AS n
SET n.mark = 1
RETURN n.k, n.mark
$$) AS (k agtype, mark agtype);
k | mark
---+------
1 | 1
(1 row)

-- Aggregating WITH clauses must not leak pre-aggregation ctid values.
SELECT * FROM cypher('ctid_set', $$
CREATE (:aggregate {k: 1}), (:aggregate {k: 2})
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('ctid_set', $$
MATCH (n:aggregate)
WITH collect(n) AS nodes, count(*) AS total
UNWIND nodes AS n
SET n.total = total
RETURN n.k, n.total
$$) AS (k agtype, total agtype);
k | total
---+-------
1 | 2
2 | 2
(2 rows)

-- DISTINCT is a row-shaping boundary and must preserve its semantics.
SELECT * FROM cypher('ctid_set', $$
CREATE (:distinct_source), (:distinct_source),
(:distinct_target {hits: 0})
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('ctid_set', $$
MATCH (:distinct_source), (n:distinct_target)
WITH DISTINCT n
SET n.hits = n.hits + 1
RETURN n.hits
$$) AS (hits agtype);
hits
------
1
(1 row)

-- Edge labels have no inherited primary key. A user-created B-tree index on
-- id must support fallback when DISTINCT intentionally suppresses the ctid.
SELECT * FROM cypher('ctid_set', $$
CREATE ()-[:indexed_edge {k: 1}]->(),
()-[:indexed_edge {k: 2}]->()
$$) AS (a agtype);
a
---
(0 rows)

CREATE INDEX ctid_set_indexed_edge_id_idx
ON ctid_set.indexed_edge (id);
SELECT * FROM cypher('ctid_set', $$
MATCH ()-[e:indexed_edge]->()
WITH DISTINCT e
SET e.indexed = true
RETURN e.k, e.indexed
ORDER BY e.k
$$) AS (k agtype, indexed agtype);
k | indexed
---+---------
1 | true
2 | true
(2 rows)

--
-- Clean up
--
Expand Down Expand Up @@ -1304,6 +1524,25 @@ NOTICE: graph "issue_1884" has been dropped

(1 row)

SELECT drop_graph('ctid_set', true);
NOTICE: drop cascades to 11 other objects
DETAIL: drop cascades to table ctid_set._ag_label_vertex
drop cascades to table ctid_set._ag_label_edge
drop cascades to table ctid_set.v
drop cascades to table ctid_set.alias_vertex
drop cascades to table ctid_set.alias_edge
drop cascades to table ctid_set.w
drop cascades to table ctid_set.expr
drop cascades to table ctid_set.aggregate
drop cascades to table ctid_set.distinct_source
drop cascades to table ctid_set.distinct_target
drop cascades to table ctid_set.indexed_edge
NOTICE: graph "ctid_set" has been dropped
drop_graph
------------

(1 row)

--
-- End
--
Loading