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
111 changes: 110 additions & 1 deletion regress/expected/cypher_merge.out
Original file line number Diff line number Diff line change
Expand Up @@ -2231,6 +2231,113 @@ $$) AS (src agtype, last agtype);
"Anchor" | "Anchor"
(1 row)

-- Exercise eager MERGE ON CREATE and ON MATCH SET over consecutive rows.
SELECT * FROM cypher('merge_actions', $$
UNWIND [1, 2, 3] AS ident
MERGE (n:Batch {id: ident})
ON CREATE SET n.payload = {nested: [ident, {active: true}]}
RETURN n.id, n.payload
$$) AS (id agtype, payload agtype);
id | payload
----+-----------------------------------
1 | {"nested": [1, {"active": true}]}
2 | {"nested": [2, {"active": true}]}
3 | {"nested": [3, {"active": true}]}
(3 rows)

SELECT * FROM cypher('merge_actions', $$
UNWIND [1, 2, 3] AS ident
MERGE (n:Batch {id: ident})
ON MATCH SET n.payload = {updated: [ident, false]}
RETURN n.id, n.payload
$$) AS (id agtype, payload agtype);
id | payload
----+-------------------------
1 | {"updated": [1, false]}
2 | {"updated": [2, false]}
3 | {"updated": [3, false]}
(3 rows)

-- Reuse a path created by an earlier input row.
SELECT * FROM cypher('merge_actions', $$
UNWIND [1, 2, 1] AS ident
MERGE (n:DedupBatch {
id: ident,
payload: {nested: [ident]}
})
RETURN ident, n.id, n.payload
$$) AS (input agtype, id agtype, payload agtype);
input | id | payload
-------+----+-----------------
1 | 1 | {"nested": [1]}
2 | 2 | {"nested": [2]}
1 | 1 | {"nested": [1]}
(3 rows)

SELECT * FROM cypher('merge_actions', $$
MATCH (n:DedupBatch)
RETURN count(n)
$$) AS (count agtype);
count
-------
2
(1 row)

-- Reuse a path created by an earlier input row in terminal MERGE.
SELECT * FROM cypher('merge_actions', $$
UNWIND [3, 4, 3] AS ident
MERGE (:DedupBatch {
id: ident,
payload: {nested: [ident]}
})
$$) AS (result agtype);
result
--------
(0 rows)

SELECT * FROM cypher('merge_actions', $$
MATCH (n:DedupBatch)
RETURN count(n)
$$) AS (count agtype);
count
-------
4
(1 row)

-- Exercise repeated duplicate candidates in eager and terminal MERGE.
SELECT * FROM cypher('merge_actions', $$
UNWIND range(1, 1000) AS ident
MERGE (n:DedupBatch {
id: 20,
payload: {nested: [20]}
})
RETURN count(n)
$$) AS (count agtype);
count
-------
1000
(1 row)

SELECT * FROM cypher('merge_actions', $$
UNWIND range(1, 1000) AS ident
MERGE (:DedupBatch {
id: 21,
payload: {nested: [21]}
})
$$) AS (result agtype);
result
--------
(0 rows)

SELECT * FROM cypher('merge_actions', $$
MATCH (n:DedupBatch)
RETURN count(n)
$$) AS (count agtype);
count
-------
6
(1 row)

-- cleanup
SELECT * FROM cypher('merge_actions', $$ MATCH (n) DETACH DELETE n $$) AS (a agtype);
a
Expand All @@ -2241,12 +2348,14 @@ SELECT * FROM cypher('merge_actions', $$ MATCH (n) DETACH DELETE n $$) AS (a agt
-- delete graphs
--
SELECT drop_graph('merge_actions', true);
NOTICE: drop cascades to 5 other objects
NOTICE: drop cascades to 7 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"
drop cascades to table merge_actions."Batch"
drop cascades to table merge_actions."DedupBatch"
NOTICE: graph "merge_actions" has been dropped
drop_graph
------------
Expand Down
144 changes: 142 additions & 2 deletions regress/expected/generated_columns.out
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,58 @@ SELECT category, properties FROM generated_columns."Product" ORDER BY category N
| {"type": "no-cat"}
(3 rows)

-- Exercise generated columns through multi-row CREATE, SET, and MERGE.
SELECT * FROM cypher('generated_columns', $$
UNWIND ['batch-a', 'batch-b', 'batch-c'] AS category
CREATE (p:Product {category: category, batch: 'create'})
RETURN count(p)
$$) AS (count agtype);
count
-------
3
(1 row)

SELECT * FROM cypher('generated_columns', $$
MATCH (p:Product)
SET p.checked = true
RETURN count(p)
$$) AS (count agtype);
count
-------
6
(1 row)

SELECT * FROM cypher('generated_columns', $$
UNWIND ['merge-a', 'merge-b', 'merge-c'] AS category
MERGE (p:Product {category: category})
ON CREATE SET p.batch = 'merge-create'
RETURN count(p)
$$) AS (count agtype);
count
-------
3
(1 row)

SELECT * FROM cypher('generated_columns', $$
UNWIND ['merge-a', 'merge-b', 'merge-c'] AS category
MERGE (p:Product {category: category})
ON MATCH SET p.batch = 'merge-match'
RETURN count(p)
$$) AS (count agtype);
count
-------
3
(1 row)

SELECT count(*) AS rows,
bool_and(category IS NOT DISTINCT FROM
agtype_access_operator(properties, '"category"')::varchar(25)) AS synchronized
FROM generated_columns."Product";
rows | synchronized
------+--------------
12 | t
(1 row)

--
-- GENERATED ALWAYS ... STORED column on an edge label
--
Expand All @@ -123,7 +175,7 @@ SELECT * FROM cypher('generated_columns', $$
$$) AS (r agtype);
r
----------------------------------------------------------------------------------------------------------------------------------------
{"id": 1125899906842625, "label": "REL", "end_id": 844424930131973, "start_id": 844424930131972, "properties": {"kind": "link"}}::edge
{"id": 1125899906842625, "label": "REL", "end_id": 844424930131982, "start_id": 844424930131981, "properties": {"kind": "link"}}::edge
(1 row)

SELECT kind FROM generated_columns."REL";
Expand Down Expand Up @@ -203,17 +255,105 @@ FROM generated_columns."T";
t
(1 row)

-- Exercise SET and DELETE on stored and virtual generated columns.
SELECT create_vlabel('generated_columns', 'DeleteStored');
NOTICE: VLabel "DeleteStored" has been created
create_vlabel
---------------

(1 row)

ALTER TABLE generated_columns."DeleteStored"
ADD COLUMN marker integer GENERATED ALWAYS AS (1) STORED;
SELECT * FROM cypher('generated_columns', $$
UNWIND [1, 2, 3] AS ident
CREATE (n:DeleteStored {id: ident})
RETURN count(n)
$$) AS (count agtype);
count
-------
3
(1 row)

SELECT * FROM cypher('generated_columns', $$
MATCH (n:DeleteStored)
DELETE n
$$) AS (n agtype);
n
---
(0 rows)

SELECT count(*) FROM generated_columns."DeleteStored";
count
-------
0
(1 row)

SELECT create_vlabel('generated_columns', 'DeleteVirtual');
NOTICE: VLabel "DeleteVirtual" has been created
create_vlabel
---------------

(1 row)

ALTER TABLE generated_columns."DeleteVirtual"
ADD COLUMN marker integer GENERATED ALWAYS AS (1) VIRTUAL;
SELECT * FROM cypher('generated_columns', $$
UNWIND [1, 2, 3] AS ident
CREATE (n:DeleteVirtual {id: ident})
RETURN count(n)
$$) AS (count agtype);
count
-------
3
(1 row)

SELECT * FROM cypher('generated_columns', $$
MATCH (n:DeleteVirtual)
SET n.checked = true
RETURN count(n)
$$) AS (count agtype);
count
-------
3
(1 row)

SELECT bool_and(marker = 1) AS virtual_ok
FROM generated_columns."DeleteVirtual";
virtual_ok
------------
t
(1 row)

SELECT * FROM cypher('generated_columns', $$
MATCH (n:DeleteVirtual)
DELETE n
RETURN count(n)
$$) AS (count agtype);
count
-------
3
(1 row)

SELECT count(*) FROM generated_columns."DeleteVirtual";
count
-------
0
(1 row)

--
-- Cleanup
--
SELECT drop_graph('generated_columns', true);
NOTICE: drop cascades to 6 other objects
NOTICE: drop cascades to 8 other objects
DETAIL: drop cascades to table generated_columns._ag_label_vertex
drop cascades to table generated_columns._ag_label_edge
drop cascades to table generated_columns."Product"
drop cascades to table generated_columns."REL"
drop cascades to table generated_columns."Plain"
drop cascades to table generated_columns."T"
drop cascades to table generated_columns."DeleteStored"
drop cascades to table generated_columns."DeleteVirtual"
NOTICE: graph "generated_columns" has been dropped
drop_graph
------------
Expand Down
85 changes: 85 additions & 0 deletions regress/expected/security.out
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,91 @@ $$) AS (since agtype);
-------
(0 rows)

-- Exercise function-based edge RLS through the default endpoint indexes.
SELECT * FROM cypher('rls_graph', $$
CREATE (:Person {name: 'DetachFnIndexHub', owner: 'rls_user1', department: 'DetachFn'})
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('rls_graph', $$
MATCH (hub:Person {name: 'DetachFnIndexHub'})
UNWIND range(1, 16) AS ident
CREATE (hub)-[:KNOWS {owner: 'rls_user1'}]->
(:Person {owner: 'rls_user1', department: 'DetachFn', leaf: ident})
$$) AS (a agtype);
a
---
(0 rows)

SET ROLE rls_user1;
SELECT * FROM cypher('rls_graph', $$
MATCH (p:Person {name: 'DetachFnIndexHub'}) DETACH DELETE p
$$) AS (a agtype);
a
---
(0 rows)

RESET ROLE;
SELECT * FROM cypher('rls_graph', $$
MATCH ()-[k:KNOWS]->() WHERE k.owner = 'rls_user1' RETURN count(k)
$$) AS (count agtype);
count
-------
0
(1 row)

-- Drop the endpoint indexes to exercise the connected-edge sequential scan.
DO $$
DECLARE
index_to_drop regclass;
BEGIN
FOR index_to_drop IN
SELECT indexrelid
FROM pg_index
WHERE indrelid = 'rls_graph."KNOWS"'::regclass
AND indnatts = 1
AND indkey[0] IN (2, 3)
LOOP
EXECUTE format('DROP INDEX %s', index_to_drop);
END LOOP;
END
$$;
SELECT * FROM cypher('rls_graph', $$
CREATE (:Person {name: 'DetachFnSeqHub', owner: 'rls_user1', department: 'DetachFn'})
$$) AS (a agtype);
a
---
(0 rows)

SELECT * FROM cypher('rls_graph', $$
MATCH (hub:Person {name: 'DetachFnSeqHub'})
UNWIND range(1, 16) AS ident
CREATE (hub)-[:KNOWS {owner: 'rls_user1'}]->
(:Person {owner: 'rls_user1', department: 'DetachFn', seq_leaf: ident})
$$) AS (a agtype);
a
---
(0 rows)

SET ROLE rls_user1;
SELECT * FROM cypher('rls_graph', $$
MATCH (p:Person {name: 'DetachFnSeqHub'}) DETACH DELETE p
$$) AS (a agtype);
a
---
(0 rows)

RESET ROLE;
SELECT * FROM cypher('rls_graph', $$
MATCH ()-[k:KNOWS]->() WHERE k.owner = 'rls_user1' RETURN count(k)
$$) AS (count agtype);
count
-------
0
(1 row)

-- cleanup
DROP POLICY detach_fn_knows_owner ON rls_graph."KNOWS";
DROP POLICY detach_fn_person_all ON rls_graph."Person";
Expand Down
Loading